Compare commits

...

358 Commits

Author SHA1 Message Date
Joachim Breitner
a4428b621a Another test 2025-11-20 15:08:32 +01:00
Joachim Breitner
c42c1c3299 Final minimization 2025-11-20 15:08:13 +01:00
Joachim Breitner
b0cceddcc4 More manual 2025-11-20 14:02:01 +01:00
Joachim Breitner
0cb364d5e8 More manual 2025-11-20 13:52:21 +01:00
Joachim Breitner
c9d16a7cff More from copilot 2025-11-20 13:51:44 +01:00
Joachim Breitner
0dcb99d6be More from copilot 2025-11-20 13:45:30 +01:00
Joachim Breitner
1c3a8b6ac9 Replace all theorem bodies before final mutual block with sorry - reduced from 575 to 486 lines 2025-11-20 12:47:19 +01:00
Joachim Breitner
07c7dab663 More manual 2025-11-20 12:44:06 +01:00
Joachim Breitner
66a69b028c More manual 2025-11-20 12:40:39 +01:00
Joachim Breitner
3987c64f64 Aggressively simplify denote theorems - reduced from 913 to 729 lines 2025-11-20 12:36:53 +01:00
Joachim Breitner
68902add3f Further minimize proofs with sorry - all isPrefix and most hinv replaced 2025-11-20 12:32:13 +01:00
Joachim Breitner
2798df51c9 Minimize issue11277b.lean with sorry in proof bullets 2025-11-20 12:29:31 +01:00
Joachim Breitner
96dc2e5e02 After minimization 2 2025-11-20 12:08:56 +01:00
Joachim Breitner
37da550e9e Before minimization 2025-11-20 11:46:22 +01:00
Joachim Breitner
d737bf483c Update tests 2025-11-20 10:22:33 +01:00
Joachim Breitner
874a2d5214 Merge branch 'master' of https://github.com/leanprover/lean4 into joachim/avoid-splitters 2025-11-20 10:09:17 +01:00
Kim Morrison
a106ea053f test: split grind_lint.lean into 7 smaller files for faster CI (#11271)
This PR splits the single grind_lint.lean test (50+ seconds) into 7
separate files that each run in under 7 seconds:

- grind_lint_list.lean (5.7s): List namespace with exceptions
- grind_lint_array.lean (4.6s): Array namespace
- grind_lint_bitvec.lean (3.9s): BitVec namespace with exceptions
- grind_lint_std_hashmap.lean (6.8s): Std hash map/set namespaces
- grind_lint_std_treemap.lean (~6s): Std tree map/set namespaces
- grind_lint_std_misc.lean (~5s): Std.Do, Std.Range, Std.Tactic
- grind_lint_misc.lean (5.5s): All other non-Lean namespaces

Each file maintains complete namespace coverage and preserves all
existing exceptions. The split enables better CI parallelization and
faster feedback.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-20 05:19:02 +00:00
Leonardo de Moura
00600806ad fix: proof construction in grind ring (#11273)
This PR fixes a bug during proof construction in `grind`.
2025-11-20 04:52:18 +00:00
Aaron Liu
5c8ebd8868 feat: make Option.decidableEqNone coherent with Option.instDecidableEq (#9302)
This PR modifies `Option.instDecidableEq` and `Option.decidableEqNone`
so that the latter can be made into a global instance without causing
diamonds. It also adds `Option.decidabeNoneEq`.

See
[Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Option.2EdecidableEqNone/near/527226250).

---------

Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
Co-authored-by: Rob Simmons <rob@lean-fro.org>
2025-11-20 01:48:42 +00:00
Leonardo de Moura
47228b94fd feat: arbitrary grind parameters (#11268)
This PR implements support for arbitrary `grind` parameters. The feature
is similar to the one available in `simp`, where a proof term is treated
as a local universe-polymorphic lemma. This feature relies on `grind
-revert` (see #11248). For example, users can now write:

```lean
def snd (p : α × β) : β := p.2
theorem snd_eq (a : α) (b : β) : snd (a, b) = b := rfl

/--
trace: [grind.ematch.instance] snd_eq (a + 1): snd (a + 1, Type) = Type
[grind.ematch.instance] snd_eq (a + 1): snd (a + 1, true) = true
-/
#guard_msgs (trace) in
set_option trace.grind.ematch.instance true in
example (a : Nat) : (snd (a + 1, true), snd (a + 1, Type), snd (2, 2)) = (true, Type, snd (2, 2)) := by
  grind [snd_eq (a + 1)]
```

Note that in the example above, `snd_eq` is instantiated only twice, but
with different universe parameters.
As described in #11248, the new feature cannot be used with `grind
+revert`.
2025-11-19 21:01:01 +00:00
Lean stage0 autoupdater
126fca1ec8 chore: update stage0 2025-11-19 19:40:23 +00:00
Leonardo de Moura
2ed025ade8 feat: mark sizeOf theorems as grind theorems (#11265)
This PR marks the automatically generated `sizeOf` theorems as `grind`
theorems.

closes #11259

Note: Requested update stage0, we need it to be able to solve example in
the issue above.
```lean
example (a: Nat) (b: Nat): sizeOf a < sizeOf (a, b) := by
  grind
```
2025-11-19 18:38:35 +00:00
Henrik Böving
827a96ade3 fix: several memory leaks in the new String API (#11263)
This PR fixes several memory leaks in the new `String` API.

These leaks are mostly situations where we forgot to put borrowing
annotations. The single
exception is the new `String` constructor `ofByteArray`. It cannot take
the `ByteArray` as
a borrowed argument anymore and must thus free it on its own.
2025-11-19 18:23:35 +00:00
Sebastian Ullrich
e0f96208e4 chore: typo in error message (#11262) 2025-11-19 17:15:11 +00:00
Joachim Breitner
5cc0a10346 refactor: use Match.AltParamInfo also for splitters (#11261)
This PR continues the homogenization between matchers and splitters,
following up on #11256. In particular it removes the ambiguity whether
`numParams` includes the `discrEqns` or not.
2025-11-19 16:13:53 +00:00
Lean stage0 autoupdater
1b6fba49c2 chore: update stage0 2025-11-19 15:57:48 +00:00
Joachim Breitner
63bd0b5e77 refactor: introduce Match.altInfos (#11256)
This PR replaces `MatcherInfo.numAltParams` with a more detailed data
structure that allows us, in particular, to distinguish between an
alternative for a constructor with a `Unit` field and the alternative
for a nullary constructor, where an artificial `Unit` argument is
introduced.
2025-11-19 15:09:17 +00:00
Lean stage0 autoupdater
75342961fc chore: update stage0 2025-11-19 13:58:11 +00:00
Henrik Böving
52b687cab4 perf: less allocations when using string patterns (#11255)
This PR reduces the allocations when using string patterns. In
particular
`startsWith`, `dropPrefix?`, `endsWith`, `dropSuffix?` are optimized.
2025-11-19 13:06:27 +00:00
Joachim Breitner
63272c6c6f Stash (need to unif altNumParams and splitterAltNumParams 2025-11-19 11:21:22 +01:00
Joachim Breitner
75570f327f refactor: thunk field-less alternatives of casesOnSameCtor (#11254)
This RP adds a `Unit` argument to `casesOnSameCtor` to make it behave
moere similar to a matcher. Follow up in spirit to #11239.
2025-11-19 09:53:09 +00:00
Markus Himmel
52d05b6972 refactor: use String.split instead of String.splitOn or String.splitToList (#11250)
This PR introduces a function `String.split` which is based on
`String.Slice.split` and therefore supports all pattern types and
returns a `Std.Iter String.Slice`.

This supersedes the functions `String.splitOn` and `String.splitToList`,
and we remove all all uses of these functions from core. They will be
deprecated in a future PR.

Migrating from `String.splitOn` and `String.splitToList` is easy: we
introduce functions `Iter.toStringList` and `Iter.toStringArray` that
can be used to conveniently go from `Std.Iter String.Slice` to `List
String` and `Array String`, so for example `s.splitOn "foo"` can be
replaced by `s.split "foo" |>.toStringList`.
2025-11-19 09:35:19 +00:00
Joachim Breitner
f7031c7aa9 perf: in match splitters, thunk alts if needed (#11239)
This PR adds a `Unit` assumption to alternatives of the splitter that
would otherwise not have arguments. This fixes #11211.

In practice these argument-less alternatives did not cause wrong
behavior, as the motive when used with `split` is always a function
type. But it is better to be safe here (maybe someone uses splitters in
other ways), it may increase the effectiveness of #10184 and simplifies
#11220.

The perf impact is insignificant in the grand scheme of things on
stdlib, but the change is effective:
```
~/lean4 $ build/release/stage1/bin/lean tests/lean/run/matchSplitStats.lean 
969 splitters found
455 splitters are const defs
~/lean4 $ build/release/stage2/bin/lean tests/lean/run/matchSplitStats.lean 
969 splitters found
829 splitters are const defs
```
2025-11-19 09:08:34 +00:00
Lean stage0 autoupdater
9fc90488ce chore: update stage0 2025-11-19 08:40:32 +00:00
Markus Himmel
59949f89ee chore: add function String.Pos.extract (#11251)
This PR is a preparatory bootstrapping PR for #11240.
2025-11-19 08:05:28 +00:00
Leonardo de Moura
61186629d6 feat: grind -revert (#11248)
This PR implements the option `revert`, which is set to `false` by
default. To recover the old `grind` behavior, you should use `grind
+revert`. Previously, `grind` used the `RevSimpIntro` idiom, i.e., it
would revert all hypotheses and then re-introduce them while simplifying
and applying eager `cases`. This idiom created several problems:

* Users reported that `grind` would include unnecessary parameters. See
[here](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Grind.20aggressively.20includes.20local.20hypotheses.2E/near/554887715).
* Unnecessary section variables were also being introduced. See the new
test contributed by Sebastian Graf.
* Finally, it prevented us from supporting arbitrary parameters as we do
in `simp`. In `simp`, I implemented a mechanism that simulates local
universe-polymorphic theorems, but this approach could not be used in
`grind` because there is no mechanism for reverting (and re-introducing)
local universe-polymorphic theorems. Adding such a mechanism would
require substantial work: I would need to modify the local context
object. I considered maintaining a substitution from the original
variables to the new ones, but this is also tricky, because the mapping
would have to be stored in the `grind` goal objects, and it is not just
a simple mapping. After reverting everything, I would need to keep a
sequence of original variables that must be added to the mapping as we
re-introduce them, but eager case splits complicate this quite a bit.
The whole approach felt overly messy.

The new behavior `grind -revert` addresses all these issues. None of the
`grind` proofs in our test suite broke after we fixed the bugs exposed
by the new feature. That said, the traces and counterexamples produced
by `grind` are different. The new proof terms are also different.
2025-11-19 05:28:31 +00:00
Robert J. Simmons
d5ecca995f chore: update some error explanations (#11225)
This PR updates some of the Error Explanations that had gotten out of
sync with actual error messages
2025-11-19 03:16:40 +00:00
Robert J. Simmons
f81e64936a feat: improve error when an identifier is unbound because autoImplicit is off (#11119)
This PR introduces a clarifying note to "undefined identifier" error
messages when the undefined identifier is in a syntactic position where
autobinding might generally apply, but where and autobinding is
disabled. A corresponding note is made in the `lean.unknownIdentifier`
error explanation.

The core intended audience for this error message change is "newcomer
who would otherwise be baffled why the thing that works in this Mathlib
project gets 'unknown identifier' errors in this non-Mathlib project."

## Modified behavior

### Example 1
```lean4
set_option autoImplicit true in
set_option relaxedAutoImplicit false in
def thisBreaks (x : α₂) (y : size₂) := ()
```

Before:
```
Unknown identifier `size₂`
```

After:
```
Unknown identifier `size₂`

Note: It is not possible to treat `size₂` as an implicitly bound variable here because it has multiple characters while the `relaxedAutoImplicit` option is set to `false`.
```

### Example 2
```lean4
set_option autoImplicit false in
def thisAlsoBreaks (x : α₃) (y : size₃) := ()
```

Before:
```
Unknown identifier `α₃`
Unknown identifier `size₃`
```

After:
```
Unknown identifier `α₃`

Note: It is not possible to treat `α₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
Unknown identifier `size₃`

Note: It is not possible to treat `size₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
```

## How this works

The elaboration process knows whether it is considering syntax where we
be able to auto-bind implicits thanks to information in the
`Lean.Elab.Term.Context`.

Before this PR, this contains:
* `autoBoundImplicit`, a boolean that is true when we are considering
syntax that might be able to auto-bind implicit AND when the
`autoImplicit` flag is set to true
* `autoBoundImplicits`, an array of `Expr` variables that we've
autobound

After this PR, this contains:
* `autoBoundImplicitCtx`, an option which is `some` **whenever** we are
considering syntax that might be able to auto-bind implicit, and carries
the array of exprs as well as a copy of the `autoImplicit` flag's value.
(The latter lets us re-implement the `autoBoundImplicit` flag for
backward compatibility.)

Therefore, rather than having access to "elaboration is in an
autobinding context && flag is enabled", it's possible to recover both
of those individual values, and give different information to the user
in cases where we didn't attempt autobinding but would have if different
options had been set.

## Rationale

The revised error message avoids offering much guidance — it doesn't
actively suggest setting the option to a different value or suggest
adding an implicit binding. Care needs to be taken here to make sure
advice is not misleading; as the accepted RFC in #6462 points out, a
substantial portion of autobinding failures are just going to be
misspellings.

I considered and then rejected a code action here to that would add a
local `set_option autoImplicit true`. This seems undesirable or
counterproductive — if a project like Mathlib has proactively disabled
`autoImplicit`, its odd to be pushing local exceptions.

A hint prompting the user to add an implicit binding would be more
proper, but only in certain circumstances — we want to be conservative
in suggesting specific code actions! In a situation like this one, we'd
want to _avoid_ giving the suggestion of adding a `{HasArr}` binding,
which I think either requires tricky heuristics or means we'd want the
elaboration to play through the consequences of auto-binding and make
sure it doesn't cause any follow-on errors before suggesting adding an
implicit binding.

```
set_option autoImplicit true
set_option relaxedAutoImplicit false
instance has_arr : HasArr Preorder := { Arr := Function }
```

Additionally, it seems like it would make the most sense to offer to
auto-bind _all_ the relevant unknown identifiers at once. To avoid being
misleading, this too would seem to require playing through the
consequences of autobinding before being able to safely suggest the
change. This is enough additional complexity that I'm leaving it for
future work.

---------

Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
2025-11-19 03:11:34 +00:00
Mac Malone
5bb9839887 fix: symbol clashes between packages (#11082)
This PR prevents symbol clashes between (non-`@[export]`) definitions
from different Lean packages.

Previously, if two modules define a function with the same name and were
transitively imported (even privately) by some downstream module,
linking would fail due to a symbol clash. Similarly, if a user defined a
symbol with the same name as one in the `Lean` library, Lean would use
the core symbol even if one did not import `Lean`.

This is solved by changing Lean's name mangling algorithm to include an
optional package identifier. This identifier is provided by Lake via
`--setup` when building a module. This information is weaved through the
elaborator, interpreter, and compiler via a persistent environment
extension that associates modules with their package identifier.

With a package identifier, standard symbols have the form
`lp_<pkg-id>_<mangled-def>`. Without one, the old scheme is used (i.e.,
`l_<mangled-def>`). Module initializers are also prefixed with package
identifier (if any). For example, the initializer for a module `Foo` in
a package `test` is now `initialize_test_Foo` (instead of
`initialize_Foo`). Lake's default for native library names has also been
adjusted accordingly, so that libraries can still, by default, be used
as plugins. Thus, the default library name of the `lean_lib Foo` in
`package test` will now be `libtest_Foo`.

When using Lake to build the Lean core (i.e., `bootstrap = true`), no
package identifier will be used. Thus, definitions in user packages can
never have symbol clashes with core.

Closes #222.
2025-11-19 02:24:44 +00:00
Mac Malone
687698e79d test: module clash across packages (#11246)
This PR adds a test that covers importing modules defined in multiple
packages.

Currently, will resolve the module to its first occurrence in the its
search order. However, this will soon change, so this test is designed
to analyze that behavior.
2025-11-19 02:23:34 +00:00
Leonardo de Moura
8a0ee9aac7 fix: assigned universe metavars in grind (#11247)
This PR fixes an issue in the `grind` preprocessor. `simp` may introduce
assigned (universe) metavariables (e.g., when performing
zeta-reduction).
2025-11-19 00:19:17 +00:00
Leonardo de Moura
6dd8ad13e5 fix: grind minor issues (#11244)
This PR fixes minor issues in `grind`. In preparation for adding `grind
-revert`.
2025-11-18 22:11:20 +00:00
Markus Himmel
fa5d08b7de refactor: use String.Slice in String.take and variants (#11180)
This PR redefines `String.take` and variants to operate on
`String.Slice`. While previously functions returning a substring of the
input sometimes returned `String` and sometimes returned
`Substring.Raw`, they now uniformly return `String.Slice`.

This is a BREAKING change, because many functions now have a different
return type. So for example, if `s` is a string and `f` is a function
accepting a string, `f (s.drop 1)` will no longer compile because
`s.drop 1` is a `String.Slice`. To fix this, insert a call to `copy` to
restore the old behavior: `f (s.drop 1).copy`.

Of course, in many cases, there will be more efficient options. For
example, don't write `f <| s.drop 1 |>.copy |>.dropEnd 1 |>.copy`, write
`f <| s.drop 1 |>.dropEnd 1 |>.copy` instead. Also, instead of `(s.drop
1).copy = "Hello"`, write `s.drop 1 == "Hello".toSlice` instead.
2025-11-18 16:13:48 +00:00
Markus Himmel
03eb2f73ac chore: deprecate String.toSubstring (#11232)
This PR deprecates `String.toSubstring` in favor of
`String.toRawSubstring` (cf. #11154).
2025-11-18 13:50:50 +00:00
Wrenna Robson
36a6844625 feat: add Std.Trichotomous (#10945)
This PR adds `Std.Tricho r`, a typeclass for relations which identifies
them as trichotomous. This is preferred to `Std.Antisymm (¬ r · ·)` in
all cases (which it is equivalent to).
2025-11-18 13:20:53 +00:00
Lean stage0 autoupdater
4296f8deee chore: update stage0 2025-11-18 11:23:27 +00:00
Markus Himmel
e301f86c6c chore: add String.Pos.next (#11238)
This PR is split from a future PR and adds the function
`String.Pos.next`, an alias (and soon to be correct name) of
`String.ValidPos.next`.

This is for boring bootstrapping reasons.
2025-11-18 10:41:22 +00:00
Jovan Gerbscheid
4c972ba0d6 fix: add missing s! in UInt64.fromJson? (#11237)
This PR fixes the error thrown by `UInt64.fromJson?` and
`USize.fromJson?` to use the missing `s!`.
2025-11-18 10:31:31 +00:00
Joachim Breitner
f6e580ccf8 refactor: extract functionality from Match.MatchEqs (#11236)
This PR extracts two modules from `Match.MatchEqs`, in preparation of
#11220
and to use the module system to draw clear boundaries between concerns
here.
2025-11-18 10:02:10 +00:00
Sebastian Graf
51ed5f247c fix: register node kind for elabToSyntax functionality (#11235)
This PR registers a node kind for `Lean.Parser.Term.elabToSyntax` in
order to support the `Lean.Elab.Term.elabToSyntax` functionality without
registering a dedicated parser for user-accessible syntax.
2025-11-18 09:47:08 +00:00
Henrik Böving
1759b83929 test: regression test for #6332 (#11234)
Closes: #6332
2025-11-18 09:47:04 +00:00
Wojciech Różowski
e35d65174c feat: add intersection on DHashMap (#11112)
This PR adds intersection operation on `DHashMap`/`HashMap`/`HashSet`
and provides several lemmas about its behaviour.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-18 09:40:44 +00:00
Paul Reichert
1a4c3ca35d refactor: small iterator improvements (#11175)
This PR removes duplicated instance parameters in the standard library
and flips lemmas of the form `toList_eq_toListIter` into a form that is
suitable for `simp`.
2025-11-18 09:28:55 +00:00
Lean stage0 autoupdater
1f807969b7 chore: update stage0 2025-11-18 09:08:13 +00:00
Wojciech Różowski
f46c17fa1d feat: add lemmas for DHashMap/HashMap/HashSet about emptyWithCapacity/empty (#11223)
This PR adds missing lemmas relating `emptyWithCapacity`/`empty` and
`toList`/`keys`/`values` for `DHashMap`/`HashMap`/`HashSet`.
2025-11-18 08:17:16 +00:00
Kim Morrison
155db16572 chore: begin dev cycle for v4.27.0 (#11229)
Set LEAN_VERSION_MINOR to 27.
2025-11-18 08:12:49 +00:00
Markus Himmel
f6a9059709 chore: rename String.offsetOfPos to String.Pos.Raw.offsetOfPos (#11218)
This PR renames `String.offsetOfPos` to `String.Pos.Raw.offsetOfPos` to
align with the other `String.Pos.Raw` operations.
2025-11-18 07:24:06 +00:00
Sebastian Graf
59d2d00132 feat: turn a term elaborator into a syntax object with elabToSyntax (#11222)
This PR implements `elabToSyntax` for creating scoped syntax `s :
Syntax` for an arbitrary elaborator `el : Option Expr -> TermElabM Expr`
such that `elabTerm s = el`.

Roundtripping example implementing an elaborator imitating `let`:

```lean
elab "lett " decl:letDecl ";" e:term : term <= ty? => do
  let elabE (ty? : Option Expr) : TermElabM Expr := do elabTerm e ty?
  elabToSyntax elabE fun body => do
    elabTerm (← `(let $decl:letDecl; $body)) ty?

#guard lett x := 42; (x + 1) = 43
```
2025-11-18 07:10:31 +00:00
Leonardo de Moura
5a4226f2bd refactor: remove old grindSearchM framework (#11226)
This PR finally removes the old `grind` framework `SearchM`. It has been
replaced with the new `Action` framework.
2025-11-18 00:33:38 +00:00
Mac Malone
81d716069c fix: lake: improper uses of computeArtifact w/o text (#11216)
This PR ensures that the `text` argument of `computeArtifact` is always
provided in Lake code, fixing a hashing bug with
`buildArtifactUnlessUpToDate` in the process.

Closes #11209
2025-11-17 22:27:19 +00:00
Henrik Böving
033fa8c585 test: add additional regression test for #11131 from #10925 (#11224)
Closes #10925
2025-11-17 21:23:53 +00:00
Joachim Breitner
09001ecad6 fix: let realizeConst run withDeclNameForAuxNaming (#11221)
This PR lets `realizeConst` use `withDeclNameForAuxNaming` so that
auxilary definitions created there get non-clashing names.
2025-11-17 21:17:16 +00:00
Lean stage0 autoupdater
1c82929c34 chore: update stage0 2025-11-17 19:02:56 +00:00
Joachim Breitner
b67e8a15d0 perf: avoid quadratic calculation of notAlts in match splitter (#11196)
This PR avoids match splitter calculation from testing all quadratically
many pairs of alternatives for overlaps, by keeping track of possible
overlaps during matcher calculation, storing that information in the
`MatcherInfo`, and using that during matcher calculation.
2025-11-17 18:10:13 +00:00
Lean stage0 autoupdater
be6457284a chore: update stage0 2025-11-17 17:15:47 +00:00
Henrik Böving
07e6b99e2e fix: deallocation for closures in non default configurations (#11217)
This PR fixes fallout of the closure allocator changes in #10982. As far
as we know
this bug only meaningfully manifests in non default build configurations
without mimalloc such as:
`cmake --preset release -DUSE_MIMALLOC=OFF`

The issue is that I forgot to update the deallocation functions for
closures. However, this only
seems to matter if we disable mimalloc which is why this slipped through
testing.
2025-11-17 16:27:20 +00:00
Paul Reichert
8eb0293098 feat: add MPL specs for slice for ... in (#11141)
This PR provides a polymorphic `ForIn` instance for slices and an MPL
`spec` lemma for the iteration over slices using `for ... in`. It also
provides a version specialized to `Subarray`.
2025-11-17 15:58:29 +00:00
Markus Himmel
8671f81aa5 fix: lakefile require syntax in package not found on Reservoir error (#11198)
This PR fixes an error message in Lake which suggested incorrect
lakefile syntax.

The error message (which was very helpful by the way) looked like this:
```
error: TwoFX/batteries: package not found on Reservoir.

  If the package is on GitHub, you can add a Git source. For example:

    require ...
      from git "https://github.com/TwoFX/batteries" @ git "main"

  or, if using TOML:

    [[require]]
    git = "https://github.com/TwoFX/batteries"
    rev = "main"
    ...
```

The suggested Lakefile syntax does not work. The correct syntax,
according to the reference manual and according to my tests, is
```
    require ...
      from git "https://github.com/TwoFX/batteries" @ "main"
```
without the second `git`.
2025-11-17 15:12:23 +00:00
David Thrane Christiansen
5ce1f67261 fix: module docstring header nesting in Verso format (#11215)
This PR fixes an issue where header nesting levels were properly tracked
between, but not within, moduledocs.
2025-11-17 13:57:00 +00:00
Henrik Böving
bef8574b93 fix: be more careful when recording cases in the compiler (#11210)
This PR fixes a bug in the LCNF simplifier unearthed while working on
#11078. In some situations caused by `unsafeCast`, the simplifier would
record incorrect information about `cases`, leading to further bugs down
the line.

Suppose we have `v : NonScalar` due to an `unsafeCast` and we run
`cases` on it, expecting `Prod.mk fst snd`. The current code attempts to
record both the arguments from the constructor application in the case
arm `fst`, `snd` and the parameters for the type by inspecting the discr
`v`. However, `NonScalar` does of course not have any parameters,
causing the simplifier to record wrong information. This patch makes the
`cases` infrastructure more cautious when extracting information from
the type of `v`.
2025-11-17 11:34:16 +00:00
Joachim Breitner
27e5e21bfe perf: use Nat-based bitmask in sparse cases construction (#11200)
This PR changes how sparse case expressions represent the
none-of-the-above information. Instead of of many `x.ctorIdx ≠ i`
hypotheses, it introduces a single `Nat.hasNotBit mask x.ctorIdx`
hypothesis which compresses that information into a bitmask. This avoids
a quadratic overhead during splitter generation, where all n assumptions
would be refined through `.subst` and `.cases` constructions for all n
assumption of the splitter alternative.

The definition of `Nat.hasNotBit` uses `Nat.rightShift` which is fiddly
to get to reduce well, especially on open terms and with `Meta.whnf`.
Some experimentation was needed to find proof terms that work, these are
all put together in the `Lean.Meta.HasNotBit` module.

Fixes #11183

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-11-17 10:05:18 +00:00
Rob23oba
eba5a5a6ef fix: consider over-applications in reduceArity compiler pass (#11185)
This PR fixes the `reduceArity` compiler pass to consider
over-applications to functions that have their arity reduced.
Previously, this pass assumed that the amount of arguments to
applications was always the same as the number of parameters in the
signature. This is usually true, since the compiler eagerly introduces
parameters as long as the return type is a function type, resulting in a
function with a return type that isn't a function type. However, for
dependent types that sometimes are function types and sometimes not,
this assumption is broken, resulting in the additional parameters to be
dropped.

Closes #11131
2025-11-17 07:51:37 +00:00
Kim Morrison
bba399eefe chore: finish dealing with #grind_lint (#11207)
This ensures that no `grind` annotated theorem, simply by being
instantiated, causes a chain of >20 further instantiations, with a small
list of documented exceptions.
2025-11-17 06:58:28 +00:00
Kim Morrison
8b575dcbf2 chore: fixing grind annotations using #grind_lint (#11206)
Slightly more extensive version of #11205, for which I want separate CI.
2025-11-17 05:30:01 +00:00
Kim Morrison
d6f3ca24d3 chore: fixing grind annotations using #grind_lint (#11205) 2025-11-17 04:53:21 +00:00
Kim Morrison
8c7604f550 feat: try? runs tactics with separate heartbeats budgets (#11174)
This PR modifies the `try?` framework, so each subsidiary tactic runs
with a separate `maxHeartbeats` budget.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-11-17 01:30:43 +00:00
Kim Morrison
4b28713a44 feat: #grind_lint check produces a "Try this:" suggestion with #grind_list inspect commands (#11204)
This PR has `#grind_list check` produce a "Try this:" suggestion with
`#grind_list inspect` commands, as this is usually the next step in
dealing with problematic cases. We also fix the grind pattern for one
theorem, as part of testing the workflow. More to follow.
2025-11-17 00:52:57 +00:00
Leonardo de Moura
4c189bc8f2 fix: grind actions (#11203)
This PR fixes a few minor issues in the new `Action` framework used in
`grind`. The goal is to eventually delete the old `SearchM`
infrastructure. The main `solve` function used by `grind` is now based
on the `Action` framework. The PR also deletes dead code in `SearchM`.
2025-11-17 00:37:19 +00:00
Sebastian Ullrich
0b93b3f182 chore: record uses of user-defined attributes as shake dependencies (#11202) 2025-11-16 20:34:23 +00:00
Sebastian Ullrich
ed34ee0cd5 chore: make declMetaExt persistent for shake (#11201) 2025-11-16 20:11:56 +00:00
Joachim Breitner
8ef742647e test: benchmark for large partial match (#11199)
Creates an inductive data type with 100 constructors, and a function
that does
matches on half of its constructors, with a catch-all for the other
half, and generates the splitter.

Related to #11183.
2025-11-16 11:20:31 +00:00
Lean stage0 autoupdater
65a41c38a0 chore: update stage0 2025-11-16 10:13:26 +00:00
Markus Himmel
bf60550ce5 chore: rename Substring to Substring.Raw (#11154)
This PR renames `Substring`  to `Substring.Raw`.

This is to signify its status as a second-class citizen (not deprecated,
but no real plans for verification, like `String.Pos.Raw`) and to free
up the name `Substring` for a possible future type `String.Substring :
String -> Type` so that `s.Substring` is the type of substrings of `s`.

The functions `String.toSubstring` and `String.toSubstring'` will remain
for now for bootstrapping reasons.
2025-11-16 09:30:04 +00:00
Leonardo de Moura
ef1dc21f1c feat: use new grind? infrastructure to implement try? (#11197)
This PR implements `try?` using the new `finish?` infrastructure. It
also removes the old tracing infrastructure, which is now obsolete.
Example:

```lean
/--
info: Try these:
  [apply] grind
  [apply] grind only [findIdx, insert, = mem_indices_of_mem, = getElem?_neg, = getElem?_pos, = HashMap.mem_insert,
    = HashMap.getElem_insert, #1bba]
  [apply] grind only [findIdx, insert, = mem_indices_of_mem, = getElem?_neg, = getElem?_pos, = HashMap.mem_insert,
    = HashMap.getElem_insert]
  [apply] grind =>
    instantiate only [findIdx, insert, = mem_indices_of_mem]
    instantiate only [= getElem?_neg, = getElem?_pos]
    cases #1bba
    · instantiate only [findIdx]
    · instantiate only
      instantiate only [= HashMap.mem_insert, = HashMap.getElem_insert]
-/
#guard_msgs in
example (m : IndexMap α β) (a : α) (b : β) :
    (m.insert a b).findIdx a = if h : a ∈ m then m.findIdx a else m.size := by
  try?
```
2025-11-16 05:26:17 +00:00
Robert J. Simmons
31f09da88a feat: prioritize stuck synthetic MVar problems to improve error messages (#11184)
This PR modifies the error message that is returned when more than one
synthetic metavariable can't be resolved.

The two heuristics used for prioritization are:
- prefer typeclass problems associated with small ranges over typeclass
problems associated with large ranges (I'm pretty confident in this
heuristic)
- do not prefer typeclass problems over other kinds of errors (not as
confident in this heuristic)
2025-11-16 00:09:48 +00:00
Leonardo de Moura
2f3939f1ea fix: incorrect grind param warning (#11194)
This PR the redundant `grind` parameter warning message. It now checks
the `grind` theorem instantiation constraints too.
2025-11-15 20:17:55 +00:00
Leonardo de Moura
f4cd97ce04 feat: add grind_pattern constraint annotations (#11193)
This PR uses the new `grind_pattern` constraints to fix cases where an
unbounded number of theorem instantiations would be generated for
certain theorems in the standard library.
2025-11-15 19:08:03 +00:00
Joachim Breitner
e39894e62d feat: realizeConst to set CoreM's maxHeartbeat (#11191)
This PR makes sure that inside a `realizeConst` the `maxHeartbeat`
option is effective.
2025-11-15 17:36:09 +00:00
Johannes Tantow
100006fdd0 feat: verify all and any for hash maps (#10765)
This PR extends the `all`/`any` functions from hash sets to hash maps
and dependent hash maps and verifies them.
2025-11-15 16:59:37 +00:00
Joachim Breitner
a6f4e9156e fix: avoid unknown free variables in match error message (#11190)
This PR avoids running into an “unknown free variable” when printing the
“Failed to compile pattern matching” error. Fixes #11186.
2025-11-15 16:31:24 +00:00
Lean stage0 autoupdater
14625ec114 chore: update stage0 2025-11-15 05:46:38 +00:00
Leonardo de Moura
6f2c04b6a2 feat: grind_pattern constraints (#11189)
This PR implements `grind_pattern` constraints. They are useful for
controlling theorem instantiation in `grind`. As an example, consider
the following two theorems:
```lean
theorem extract_empty {start stop : Nat} :
    (#[] : Array α).extract start stop = #[] := …

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) := …
```

If both are used for theorem instantiation, an unbounded number of
instances is generated as soon as we add the term `#[].extract i j` to
the `grind` context.

We can now prevent this by adding a `grind_pattern` constraint to
`extract_extract`:

```lean
grind_pattern extract_extract => (as.extract i j).extract k l where
  as =/= #[]
```

With this constraint, only one instance is generated, as expected:

```lean
/-- trace: [grind.ematch.instance] extract_empty: #[].extract i j = #[] -/
#guard_msgs (drop error, trace) in
set_option trace.grind.ematch.instance true in
example (as : Array Nat) (h : #[].extract i j = as) : False := by
  grind only [= extract_empty, usr extract_extract]
```
2025-11-15 05:05:04 +00:00
Mac Malone
06f457b48a fix: lake: indeterminism in targets test (#11188)
This PR fixes a source of indeterminism in the `examples/targets` Lake
test (checking the job index).
2025-11-15 04:20:24 +00:00
Mac Malone
8ad0a61169 refactor: lake: scope all module build keys by package (#11169)
This PR changes all module build keys in Lake to be scoped by their
package. This enables building modules with the same name in different
packages (something previously only well-supported for executable
roots).

API-wise, the `BuildKey` definitions `module` and `moduleFacet` have
been deprecated and replaced with `packageModule` and
`packageModuleFacet`. The `moduleTargetIndicator` has also been removed
(with its purpose subsumed by `packageModule`).
2025-11-15 04:13:00 +00:00
Leonardo de Moura
d963d33985 feat: add grind_pattern constraints (#11187)
This PR adds syntax for specifying `grind_pattern` constraints and
extends the `EMatchTheorem` object.

--- 
Note: We need a manual stage0 update because it affects the .olean
files.
2025-11-14 18:27:17 -08:00
Robert J. Simmons
3f4e85413e doc: improved error messages when typeclass errors are stuck (#11179)
This PR removes most cases where an error message explained that it was
"probably due to metavariables," giving more explanation and a hint.

## Example

```
def square x := x * x
```

Before:

```lean4
typeclass instance problem is stuck, it is often due to metavariables
  HMul ?m.9 ?m.9 (?m.3 x)
```

After:
```
typeclass instance problem is stuck
  HMul ?m.9 ?m.9 (?m.3 x)

Note: Lean will not try to resolve this typeclass instance problem because the 
first and second type arguments to `HMul` are metavariables. These arguments 
must be fully determined before Lean will try to resolve the typeclass.

Hint: Adding type annotations and supplying implicit arguments to functions 
can give Lean more information for typeclass resolution. For example, if you 
have a variable `x` that you intend to be a `Nat`, but Lean reports it as 
having an unresolved type like `?m`, replacing `x` with `(x : Nat)` can get 
typeclass resolution un-stuck.
```

In addition to providing beginner-and-intermediate-friendly explanation
about **why** typeclass instance problems are treated as "stuck" when
metavariables appear in output positions, this PR provides
potentially-valuable improvement even to expert users: it explains
**which of the typeclass arguments are inputs** and therefore need to be
fully specified before typeclass resolution will be attempted. This
information can be tricky to find otherwise.

## Next steps, but probably after this PR

* error explanation
* detecting when the syntactic source is a binop and giving a
special-cased explanation on the binary operators and their associated
typeclasses
* detecting when the syntactic source is a function call, inspecting the
function call's type somewhat, and replacing the generic "replace `x`
with `(x : Nat)` hint with a specialized "replace `foo` with `foo (tyArg
:= Nat)`" hint
2025-11-14 21:25:46 +00:00
Alexander Bentkamp
bc2aae380c feat: add lemmas about Int range sizes (#11159)
This PR adds lemmas about the sizes of ranges of Ints, analogous to the
Nat lemmas in `Init.Data.Range.Polymorphic.NatLemmas`. See also
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Reasonning.20about.20PRange.20sizes.20.28with.20.60Int.60.29/with/546466339.

Closes #11158

---------

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-11-14 13:35:47 +00:00
Paul Reichert
b5b34ee054 feat: List slices (#11019)
This PR introduces slices of lists that are available via slice notation
(e.g., `xs[1...5]`).

* Moved the `take` combinator and the `List` iterator producer to
`Init`.
* Introduced a `toTake` combinator: `it.toTake` behaves like `it`, but
it has the same type as `it.take n`. There is a constant cost per
iteration compared to `it` itself.
* Introduced `List` slices. Their iterators are defined as
`suffixList.iter.take n` for upper-bounded slices and
`suffixList.iter.toTake` for unbounded ones.

Performance characteristics of using the slice `list[a...b]`:

* when creating it: `O(a)`
* every iterator step: `O(1)`
* `toList`: `O(b - a + 1)` (given that a <= b)

Because the slice only stores a suffix of `xs` internally, two slices
can be equal even though the underlying lists differ in an irrelevant
prefix. Because the `stop` field is allowed to be beyond the list's
upper bound, the slices `[1][0...1]` and `[1][0...2]` are not equal,
even though they effectively cover the same range of the same list.
Improving this would require us to call `List.length` when building the
slice, which would iterate through the whole list.
2025-11-14 11:33:25 +00:00
Sebastian Ullrich
5011b7bd89 chore: make compilation type mismatch error message from non-exposed defs a lot less mysterious (#11177) 2025-11-14 10:50:43 +00:00
Sebastian Ullrich
4602586b6a chore: suggest public meta import on phase check failure, which is more likely to be the correct variant (#11173) 2025-11-14 10:10:04 +00:00
Wojciech Różowski
36ee331ce2 feat: add minimal support for getEntry/getEntry?/getEntry!/getEntryD for DTreeMap (#11161)
This PR adds getEntry/getEntry?/getEntry!/getEntryD operation on
DTreeMap.
2025-11-14 09:09:53 +00:00
Markus Himmel
aca297d1c5 chore: some String API cleanup in Lake.Util.Version (#11160)
This PR performs some cleanup in `Lake.Util.Version`.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2025-11-14 08:56:56 +00:00
Kim Morrison
de073706c5 feat: redefine Int.pow, for faster kernel reduction (#11139)
This PR replaces #11138, which just added a `@[csimp]` lemma for
`Int.pow`, this time actually replacing the definition. This means we
not only get fast runtime behaviour, but take advantage of the special
kernel support for `Nat.pow`.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-11-14 05:45:19 +00:00
Kim Morrison
f7ead9667b feat: macro for try? (#11170)
This PR adds tactic and term mode macros for `∎` (typed `\qed`) which
expand to `try?`. The term mode version captures any produced
suggestions and prepends `by`.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 05:27:23 +00:00
Kim Morrison
ffbd744c85 chore: remove simp_all? +suggestions from try? for now (#11172)
This PR removes `simp_all? +suggestions` from `try?` for now. It's
really slow out in Mathlib; too often the suggestions cause `simp` to
loop. Until we have the ability for `try?` to move past a timeing-out
tactic (or maybe even until we have parallelism), it needs to be
removed.

Alternatively, we could try modifying `simp` so that e.g. it won't use a
premise more than once. This might help avoid loops, but it would
produce less-reproducible proofs.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 04:58:23 +00:00
Kim Morrison
833aaa823e chore: tactics using library suggestions set the caller field (#11171)
This PR ensures that tactics using library suggestions set the caller
field, so the premise selection engine has access to this. We'll later
use this to filter out some modules for grind, which we know have
already been fully annotated.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 04:50:55 +00:00
François G. Dorais
7b29d976ed feat: add instances NeZero(n^0) for n : Nat and n : Int (#10739)
This PR adds two missing `NeZero` instances for `n^0` where `n : Nat`
and `n : Int`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Add NeZero instances for n^0 when n : Nat and n : Int.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8305e65ba5. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-11-14 03:37:17 +00:00
Leonardo de Moura
1e84b6dff9 feat: add #grind_lint check in module <module> (#11167)
This PR implements support for `#grind_lint check in module <module>`.
Mathlib does not use namespaces, so we need to restrict the
`#grind_lint` search space using module (prefix) names. Example:

```lean
/--
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: Array.filterMap_some
[thm] instances
  [thm] Array.filterMap_filterMap ↦ 94
  [thm] Array.size_filterMap_le ↦ 5
  [thm] Array.filterMap_some ↦ 1
---
info: instantiating `Array.range_succ` triggers 22 additional `grind` theorem instantiations
-/
#guard_msgs in
#grind_lint check (min := 20) in module Init.Data.Array
```
2025-11-14 01:44:04 +00:00
Kim Morrison
bc9cc05082 feat: include current file in default premise selector (#11168)
This PR changes the default library suggestions (e.g. for `grind
+suggestions` or `simp_all? +suggestions) to include the theorems from
the current file in addition to the output of Sine Qua Non.
2025-11-14 01:31:30 +00:00
Leonardo de Moura
46ff76aabd feat: #grind_lint refinements (#11166)
This PR implements the following improvements to the `#grind_lint`
command:
1. More informative messages when the number of instances exceeds the
minimum threshold.
2. A code action for `#grind_lint inspect` that inserts
`set_option trace.grind.ematch.instance true` whenever the number of
instances exceeds
   the minimum threshold.
3. Displaying doc strings for `grind` configuration options in
`#grind_lint`.
4. Improve doc strings for `#grind_lint inspect` and `#grind_lint
check`.

Example:
```lean
/--
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: Array.filterMap_some
[thm] instances
  [thm] Array.filterMap_filterMap ↦ 94
  [thm] Array.size_filterMap_le ↦ 5
  [thm] Array.filterMap_some ↦ 1
---
info: Try this to display the actual theorem instances:
  [apply] set_option trace.grind.ematch.instance true in
  #grind_lint inspect Array.filterMap_some
-/
#guard_msgs in
#grind_lint inspect Array.filterMap_some
```
2025-11-13 20:36:01 +00:00
Markus Himmel
eb01aaeee4 chore: rename String.Iterator to String.Legacy.Iterator (#11152)
This PR renames `String.Iterator` to `String.Legacy.Iterator`.

From the docstring of `String.Legacy.Iterator`:

> This is a no-longer-supported legacy API that will be removed in a
future release. You should use
> `String.ValidPos` instead, which is similar, but safer. To iterate
over a string `s`, start with
> `p : s.startValidPos`, advance it using `p.next`, access the current
character using `p.get` and
> check if the position is at the end using `p = s.endValidPos` or
`p.IsAtEnd`.
2025-11-13 13:46:22 +00:00
Mac Malone
2b85e29cc9 test: version clash w/ diamond deps (#11155)
This PR adds a test replicating Kim's diamond dependency example.

The top-level package, `D`, depends on two intermediate packages, `B`
and `C`, which each require semantically different versions of another
package, `A`. The portion of `A` that `B` and `C` publicly use is
unchanged across the versions, but they both privately make use of
changed API. Currently, this causes a version clash. This will be made
to work without error later this quarter.
2025-11-13 05:40:56 +00:00
David Thrane Christiansen
ceb86b1293 fix: details in Markdown rendering of Verso docstrings (#11151)
This PR fixes some details in the Markdown renderings of Verso
docstrings, and adds tests to keep them correct. Also adds tests for
Verso docstring metadata.
2025-11-13 05:19:30 +00:00
Lean stage0 autoupdater
a00c78beea chore: update stage0 2025-11-13 02:05:09 +00:00
Leonardo de Moura
ff9c35d6ef feat: #grind_lint command (#11157)
This PR implements the `#grind_lint` command, a diagnostic tool for
analyzing the behavior of theorems annotated for theorem instantiation.
The command helps identify problematic theorems that produce excessive
or unbounded instance generation during E-matching, which can lead to
performance issues.
The main entry point is:
```
#grind_lint check
```
which analyzes all theorems marked with the `@[grind]` attribute.
For each theorem, it creates an artificial goal and runs `grind`,
collecting statistics about the number of instances produced.
Results are summarized using info messages, and detailed breakdowns are
shown for lemmas exceeding a configurable threshold.
Additional subcommands are provided for targeted inspection and control:

* `#grind_lint inspect thm`: analyzes one or more specific theorems in
detail
* `#grind_lint mute thm`: excludes a theorem from instantiation during
analysis
* `#grind_lint skip thm`: omits a theorem from being analyzed by
`#grind_lint check`
2025-11-13 00:42:18 +00:00
Kim Morrison
eb675f708b feat: user extensibility in try? (#11149)
This PR adds a user-extension mechanism for the `try?` tactic. You can
either use the `@[try_suggestion]` attribute on a declaration with
signature ``MVarId -> Try.Info -> MetaM (Array (TSyntax `tactic))`` to
produce suggestions, or the `register_try?_tactic <stx>` command with a
fixed piece of syntax. User-extensions are only tried *after* the
built-in try strategies have been tried and failed.

I wanted to ensure that if the user provides a tactic that produces a
"Try this:" suggestion, we both emit the original tactic and the
suggested replacement (this is what we already do with `grind` and
`simp`). I have this working, but it is quite hacky: we grab the message
log and parse it. I fear this will break when the "Try this:" format is
inevitably changed in the future.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds user-defined suggestion generators for `try?` via
`@[try_suggestion]` and `register_try?_tactic`, executed after built-ins
with priority and double-suggestion handling.
> 
> - **Parser/Command**:
> - Add command syntax `register_try?_tactic (priority := n)?
<tacticSeq>` in `Lean.Parser.Command`.
> - **Suggestion registry**:
> - Introduce `@[try_suggestion (prio)]` attribute with a scoped env
extension to register generators (`MVarId → Try.Info → MetaM (Array
(TSyntax `tactic))`).
>   - Priority ordering (higher first); supports local/global scope.
> - **Tactic engine (`try?`)**:
> - New unsafe pipeline to collect and run user generators after
built-in tactics; expands nested "Try this" outputs from user tactics.
> - `mkTryEvalSuggestStx` now takes `(goal, info)`; integrates user
tactics as fallback via `attempt_all`.
> - Suppress intermediate "Try this" messages during `evalAndSuggest` by
restoring the message log.
> - **Imports**:
>   - Add `meta import Lean.Elab.Command` for command elaboration.
> - **Tests**:
> - `try_register_builtin.lean`: command availability and warning
without import.
> - `try_user_suggestions.lean`: basic, priority, built-in fallback,
double-suggestion, and command registration cases.
> - Update `versoDocMissing.lean.expected.out` to include
`register_try?_tactic` in expected commands.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
302dc94544. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-12 23:49:54 +00:00
Wojciech Różowski
b39ee8a84b feat: add minimal support for getEntry/getEntry?/getEntry!/getEntryD for DHashMap (#11076)
This PR adds `getEntry`/`getEntry?`/`getEntry!`/`getEntryD` operation on
DHashMap.
2025-11-12 16:56:28 +00:00
Paul Reichert
9a3fb90e40 refactor: replace Iter(M).size with Iter(M).count (#10952)
This PR replaces `Iter(M).size` with the `Iter(M).count`. While the
former used a special `IteratorSize` type class, the latter relies on
`IteratorLoop`. The `IteratorSize` class is deprecated. The PR also
renames lemmas about ranges be replacing `_Rcc` with `_rcc`, `_Rco` with
`_roo` (and so on) in names, in order to be more consistent with the
naming convention.
2025-11-12 16:41:00 +00:00
Lean stage0 autoupdater
7f7a4d3eaf chore: update stage0 2025-11-12 15:54:53 +00:00
Sebastian Graf
09cf07b71c feat: new do elaborator, part 1: doElem_elab attribute (#11150)
This PR adds a new, inactive and unused `doElem_elab` attribute that
will allow users to register custom elaborators for `doElem`s in the
form of the new type `DoElab`. The old `do` elaborator is active by
default but can be switched off by disabling the new option
`backward.do.legacy`.
2025-11-12 14:25:28 +00:00
Leonardo de Moura
d464b13569 feat: add cases_next to grind tactic mode (#11148)
This PR addst the `cases_next` tactic to the `grind` interactive mode.
2025-11-12 03:26:18 +00:00
Leonardo de Moura
f2b3f90724 refactor: symmetric equality congruence in grind (#11147)
This PR refactors the implementation of the symmetric equality
congruence rule used in `grind`.
2025-11-12 01:10:37 +00:00
Kim Morrison
bc60b1c19d fix: don't suggest deprecated theorems (#11146)
This PR fixes a bug in #11125. Added a test this time ...

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Exclude deprecated declarations from library suggestions and add a
test verifying they are filtered out.
> 
> - **Library Suggestions**:
> - Update `isDeniedPremise` in `src/Lean/LibrarySuggestions/Basic.lean`
to treat `Lean.Linter.isDeprecated` as denied (`true`), filtering
deprecated constants from suggestions.
> - **Tests**:
> - Add `tests/lean/run/library_suggestions_deprecated.lean` to verify
deprecated theorems (e.g., `deprecatedTheorem`) are not suggested by
`currentFile`, while non-deprecated ones are.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ef7e546dbc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-12 00:58:47 +00:00
Leonardo de Moura
fa3c85ee84 fix: missing condition in isMatchCondCandidate (#11145)
This PR fixes a bug in `isMatchCondCandidate` used in `grind`. The
missing condition was causing a "not internalized term" `grind` internal
error.
2025-11-12 00:20:37 +00:00
Wojciech Różowski
34f9798b4b feat: add DTreeMap/TreeMap/TreeSet iterators and slices (#10776)
This PR adds iterators and slices for `DTreeMap`/`TreeMap`/`TreeSet`
based on zippers and provides basic lemmas about them.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-11 17:49:50 +00:00
Wojciech Różowski
e0af5122f7 feat: add union on ExtDTreeMap/ExtTreeMap/ExtTreeSet (#11070)
This PR adds union operation on ExtDHashMap/ExtHashMap/ExtHashSet nd
provides lemmas about union operations.

Stacked on top of #10946.
2025-11-11 16:52:07 +00:00
Markus Himmel
f1224277e2 perf: improve performance of String.ValidPos (#11142)
This PR aims to bring the performance of `String.ValidPos` closer to
that of `String.Pos.Raw` by adding/correcting `extern` annotations as
needed.

This is in response to a regression observed after #11127. The changes
to the `String` `Parsec` module lead to different compiler behavior for
functions like `strCore` and `natCore`. The new IR *looks* better than
the old IR, but the
[numbers](1e438647ba)
are a bit mixed.
2025-11-11 15:30:47 +00:00
Marc Huisinga
c2647cdbf5 fix: pre-filter completion items mod ascii casing (#11140)
This PR ensures that we pre-filter auto-completion items modulo ASCII
casing for consistency with the VS Code fuzzy matching.
2025-11-11 14:11:05 +00:00
dependabot[bot]
aaceb3dbf5 chore: CI: bump actions/upload-artifact from 4 to 5 (#11052)
Bumps
[actions/upload-artifact](https://github.com/actions/upload-artifact)
from 4 to 5.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:41:14 +00:00
dependabot[bot]
3ae409cf81 chore: CI: bump actions/download-artifact from 5 to 6 (#11053)
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 5 to 6.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:40:56 +00:00
dependabot[bot]
a7f47db134 chore: CI: bump softprops/action-gh-release from 2.3.3 to 2.4.1 (#11054)
Bumps
[softprops/action-gh-release](https://github.com/softprops/action-gh-release)
from 2.3.3 to 2.4.1.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:40:23 +00:00
Markus Himmel
2c2fcff4f8 refactor: do not use String.Iterator (#11127)
This PR removes all uses of `String.Iterator` from core, preferring
`String.ValidPos` instead.

In an upcoming PR, `String.Iterator` will be renamed to
`String.Legacy.Iterator`.
2025-11-11 11:46:58 +00:00
Kim Morrison
d1e19f2aa0 feat: support for induction in try? (#11136)
This PR adds support for `try?` to use induction; it will only perform
induction on inductive types defined in the current namespace and/or
module; so in particular for now it will not induct on built-in
inductives such as `Nat` or `List`.

This is stacked on top of #11132, and there are overlapping changes.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds vanilla induction suggestions to `try?`, updates collection of
inductive candidates, and tests the new behavior on custom inductive
types.
> 
> - **Try tactic pipeline**:
> - Add vanilla induction generators (`mkIndStx`, `mkAllIndStx`) that
try `induction <var> <;> …`, with fallback via `expose_names` when
needed.
> - Integrate induction into `mkTryEvalSuggestStx`, alongside existing
atomic, suggestions, and function-induction options.
> - **Collector updates (`Try/Collect.lean`)**:
> - Enhance `checkInductive` to `whnf` the type and use `getAppFn` to
detect inductive heads, populating `indCandidates`.
> - **Tests**:
> - New `tests/lean/run/try_induction.lean` covering suggestions for
`induction` on custom inductives, interaction with `grind`, and
coexistence with `fun_induction`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b357990c97. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-11 09:29:59 +00:00
Kim Morrison
838be605ac feat: replace Int.pow with a @[csimp] lemma (#11138)
This PR adds a `csimp` lemma for faster runtime evaluation of `Int.pow`
in terms of `Nat.pow`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Replaces `Int.pow` evaluation with a `@[csimp]` lemma using `Nat.pow`
and adds supporting lemmas (`pow_mul`, `neg_pow`, nonneg results).
> 
> - **Performance/runtime**:
> - Introduce `powImp` and `@[csimp]` theorem `pow_eq_powImp` to
evaluate `Int.pow` via `Nat.pow` with sign handling.
> - **Math lemmas (supporting)**:
>   - `Int.pow_mul`: `a ^ (n * m) = (a ^ n) ^ m`.
>   - `Int.sq_nonnneg`: nonnegativity of `m ^ 2`.
>   - `Int.pow_nonneg_of_even`: nonnegativity for even exponents.
>   - `Int.neg_pow`: `(-m)^n = (-1)^(n % 2) * m^n`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
66ac236db7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-11 06:39:10 +00:00
Kim Morrison
02b141ca15 feat: add library suggestions support to try? tactic (#11132)
This PR adds support for `grind +suggestions` and `simp_all?
+suggestions` in `try?`. It outputs `grind only [X, Y, Z]` or `simp_all
only [X, Y, Z]` suggestions (rather than just `+suggestions`).

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-11 06:38:28 +00:00
Kim Morrison
fe8238c76c feat: grind cases on Sum (#11087)
This PR enables `grind` to case bash on `Sum` and `PSum`.
2025-11-11 04:50:34 +00:00
François G. Dorais
7f77bfef4c feat: add List.mem_finRange (#9515)
This PR adds a missing lemma for the `List` API.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Add `[simp]` lemma `List.mem_finRange` proving any `x : Fin n` is in
`finRange n`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
631f7ca852. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-11-11 04:16:08 +00:00
Leonardo de Moura
e7e85e5e17 fix: stackoverflow during proof construction in grind (#11137)
This PR fixes a stackoverflow during proof construction in `grind`.

Closes #11134
2025-11-11 03:23:43 +00:00
Leonardo de Moura
1b5fb2fa50 fix: check exponent in grind lia and grind ring (#11135)
This PR ensures that `checkExp` is used in `grind lia` (formerly known
as `grind cutsat`) and `grind ring` to prevent stack overflows.

closes #11130
2025-11-11 02:28:55 +00:00
Leonardo de Moura
0e455f5347 fix: disequality ctor propagation in grind (#11133)
This PR fixes disequality propagation for constructor applications in
`grind`. The equivalence class representatives may be distinct
constructor applications, but we must ensure they have the same type.
Examples that were panic'ing before this PR:
```lean
example (a b : List Nat)
    : a ≍ ([] : List Int) → b ≍ ([1] : List Int) → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → a ≍ ([] : List Int) → b = [1] → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → a ≍ ([] : List Int) → b = [1] → b ≍ [(1 : Int)] → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → b = [1] → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → a ≍ ([] : List Int) → b = [1] → a = b ∨ p → p := by
  grind
```

Closes #11124
2025-11-11 01:28:54 +00:00
Leonardo de Moura
f74e21e302 fix: grind injection should not fail at clear (#11126)
This PR ensures `grind` does not fail when applying `injection` to a
hypothesis that cannot be cleared because of forward dependencies.
2025-11-10 14:50:18 +00:00
Wojciech Różowski
c08fcf6c28 feat: add union on ExtDHashMap/ExtHashMap/ExtHashSet (#10946)
This PR adds union operation on ExtDHashMap/ExtHashMap/ExtHashSet nd
provides lemmas about union operations.
2025-11-10 13:48:36 +00:00
Benjamin Shi
ecae85e77b doc: fix typo in List.finIdxOf? (#11111)
This PR fixes a typo in the doc string of `List.finIdxOf?`. The first
line of the doc string previously says the function returns the size of
the list if no element equal to `a`, but both the examples in the doc
string and real run-time behavior indicate it returns `none` in this
case.

Closes #11110
2025-11-10 10:04:07 +00:00
ecyrbe
6008c0d523 feat: add min and max list operations (#11060)
This PR add list `min` and `max` operations to complement `min?` and
`max?` ones in the same vain as `head?` and `head`.

It was dicussed here in
[zulip](https://leanprover.zulipchat.com/#narrow/channel/217875-Is-there-code-for-X.3F/topic/maximum.20of.20a.20list.20known.20to.20be.20nonempty/with/548296389)

it also add small unit tests for `min` , `max`, `min?` and `max?`
2025-11-10 09:56:59 +00:00
Kim Morrison
d47b474e41 feat: suggestions don't included deprecated theorems (#11125)
This PR adds a filter for premise selectors to ensure deprecated
theorems are not returned.
2025-11-10 04:24:06 +00:00
Kim Morrison
c7652413db feat: link docstrings for diamond inheritance (#11122)
This PR fixes a problem for structures with diamond inheritance: rather
than copying doc-strings (which are not available unless `.server.olean`
is loaded), we link to them. Adds tests.
2025-11-10 01:05:01 +00:00
Kim Morrison
08d0ae1e8a feat: add foldl_flatMap and foldr_flatMap theorems (#11123)
This PR adds theorems about folds over flatMaps, for
`List`/`Array`/`Vector`.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-09 23:00:29 +00:00
Kim Morrison
6fc48d14c0 feat: missing lemmas about List.findIdx (#11113)
This PR adds some small missing lemmas.
2025-11-09 21:16:11 +00:00
Mac Malone
80409a9ceb feat: lake: Job.sync & other touchups (#11118)
This PR adds `Job.sync` as a standard way of declaring a synchronous
job.

It makes some non-behavior changes to related Job APIs to improve
compilation.
2025-11-08 04:35:05 +00:00
Mac Malone
590ff23e71 fix: lake: moreLinkObjs|Libs on a lean_exe (#11117)
This PR fixes a bug where Lake ignored `moreLinkObjs` and `moreLinkLibs`
on a `lean_exe`.
2025-11-08 04:20:42 +00:00
Joachim Breitner
f843837bfa test: test missing cases error (#11107)
This PR tests the missing cases error.

I thought I broke this, but it seems I did not (or at least not this
way, maybe there is a way to trigger it).
2025-11-06 14:38:55 +00:00
Joachim Breitner
d41f39fb10 perf: sparse case splitting in match compilation (#10823)
This PR lets the match compilation procedure use sparse case analysis
when the patterns only match on some but not all constructors of an
inductive type. This way, less code is produce. Before, code handling
each of the other cases was then optimized and commoned-up by later
compilation pipeline, but that is wasteful to do.

In some cases this will prevent Lean from noticing that a match
statement is complete
because it performs less case-splitting for the unreachable case. In
this case, give explicit
patterns to perform the deeper split with `by contradiction` as the
right-hand side.

At least temporarily, there is also the option to disable this behaviour
with
```
set_option backwards.match.sparseCases false
```
2025-11-06 13:46:35 +00:00
Joachim Breitner
7459304e98 refactor: bv_decide: remove verifyEnum et. al (#11068)
This PR removes the `verifyEnum` functions from the bv_decide frontend.
These functions looked at the implementation of matchers to see if they
really do the matching that they claim to do. This breaks that
abstraction barrier, and should not be necessary, as only functions with
a `MatcherInfo` env entry are considered here, which should all play
nicely.
2025-11-06 09:22:36 +00:00
Kim Morrison
e6b1f1984c feat: suggestions tactic generates hovers (#11098)
This PR updates the `suggestions` tactic so the printed message includes
hoverable type information (and displays scores and flags when
relevant).
2025-11-06 06:31:04 +00:00
Kim Morrison
6d2af21aa0 feat: add Int.ediv_pow and related lemmas (#11100)
This PR adds `theorem Int.ediv_pow {a b : Int} {n : Nat} (hab : b ∣ a) :
(a / b) ^ n = a ^ n / b ^ n` and related lemmas.

---------

Co-authored-by: Bhavik Mehta <bhavikmehta8@gmail.com>
2025-11-06 06:16:18 +00:00
Kim Morrison
3a4e64fe94 feat: some missing Array grind annotations (#11102)
This PR adds some annotations missing in the Array bootstrapping files.
2025-11-06 05:22:40 +00:00
Leonardo de Moura
0d7ca700ad fix: Function.Injective initialization in grind (#11101)
This PR fixes an initialization issue for local `Function.Injective f`
hypotheses.

Closes #11088
2025-11-06 04:26:57 +00:00
Leonardo de Moura
f401f8b46e fix: universe meta-variable support in grind (#11099)
This PR improves the support for universe-metavariables in `grind`.

Closes #11086
2025-11-06 03:38:59 +00:00
Sebastian Ullrich
ae86c18ac1 chore: backward.privateInPublic should not break irrelevance of proofs for rebuilds (#11097) 2025-11-05 23:00:04 +00:00
Sebastian Ullrich
ea2b745e57 chore: new module system adjustments for the Mathlib port (#11093) 2025-11-05 22:17:53 +00:00
Kim Morrison
28a3cf9a6c chore: grind attributes for Prod (#11085) 2025-11-05 20:52:28 +00:00
Joachim Breitner
343887e480 perf: use hasIndepIndices (#11095)
This PR makes use of `hasIndepIndices`. That function was unused since
commit 54f6517ca3, but it seems it should
be used.
2025-11-05 18:41:23 +00:00
Joachim Breitner
d8a67095d6 chore: make workspaceSymbol benchmarks modules (#11094)
This PR makes workspaceSymbol benchmarks `module`s, so that they are
less sensitive to additions of private symbols in the standard library.
2025-11-05 18:40:39 +00:00
Joachim Breitner
0cb79868f4 feat: sparse casesOn constructions (#11072)
This PR adds “sparse casesOn” constructions. They are similar to
`.casesOn`, but have arms only for some constructors and a catch-all
(providing `t.ctorIdx ≠ 42` assumptions). The compiler has native
support for these constructors and now (because of the similarity) also
the per-constructor elimination principles.
2025-11-05 15:49:11 +00:00
Leonardo de Moura
ccecac5a56 chore: use abbrev in denote functions (#11092)
This PR ensures that `grind ac` denotation functions used in proof by
reflection are marked as `abbrev`.
2025-11-05 13:51:36 +00:00
Marc Huisinga
8b43fc54b2 doc: clarify server protocol violations around initialize (#11091) 2025-11-05 09:53:39 +00:00
Leonardo de Moura
e7f4f98071 fix: stackoverflow during proof construction in grind (#11084)
This PR fixes a stack overflow that occurs when constructing a proof
term in `grind`.

Closes #11081
2025-11-05 02:35:05 +00:00
Leonardo de Moura
52e37e0d55 refactor: denote functions in grind (#11071)
This PR ensures that the `denote` functions used to implement
proof-by-reflection terms in `grind` are abbreviations. This change
eliminates the need for the `withAbstractAtoms` gadget.
2025-11-04 23:34:17 +00:00
Leonardo de Moura
a4e073f565 fix: panic during equality propagation in grind ring (#11080)
This PR fixes a panic during equality propagation in the `grind ring`
module. If the maximum number of steps has been reached, the polynomials
may not be fully simplified.

Closes #11073
2025-11-04 23:20:38 +00:00
Sebastian Ullrich
18131de438 fix: evalConst meta check and auxiliary IR decls (#11079)
Uncovered in Mathlib through new boxed decls from `BaseIO` changes
2025-11-04 21:29:49 +00:00
Leonardo de Moura
e430626d8a fix: anchor values in grind? (#11077)
This PR fixes the anchor values produced by `grind?`
2025-11-04 13:03:18 +00:00
Kim Morrison
e76bbef79b feat: simp? +suggestions handles ambiguity (#11075)
This PR updates `simp? +suggestions` so that if a name is ambiguous
(because of namespaces) all alternatives are used, rather than erroring.
2025-11-04 05:26:51 +00:00
Kim Morrison
04d72fe346 chore: basic dev instructions for Claude (#11074)
This PR adds a `.claude/claude.md`, with basic development instructions
for Claude Code to operate in this repository.
2025-11-04 04:07:53 +00:00
Sebastian Ullrich
e4fb780f8a perf: remove unused argument to ExternEntry.opaque (#11066)
This used to create quite a few unique objects in public .olean
2025-11-03 17:26:32 +00:00
Wojciech Różowski
00e29075f3 feat: add union on DTreeMap/TreeMap/TreeSet (#10896)
This PR adds union operations on DTreeMap/TreeMap/TreeSet and their raw
variants and provides lemmas about union operations.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-11-03 13:47:44 +00:00
Kim Morrison
ec775907e4 chore: update stage0 2025-11-03 23:26:40 +11:00
Kim Morrison
8d603d34dc feat: make set_library_suggestions persistent 2025-11-03 23:26:40 +11:00
Mac Malone
528c0dd2e4 feat: lake: require dependencies by semver range (#10959)
This PR enables Lake users to require Reservoir dependencies by a
semantic version range. On a `lake update`, Lake will fetch the
package's version information from Reservoir and select the newest
version of the package that satisfies the range.

### Using Version Ranges

Version ranges can be specified through the `version` field of a TOML
`require` or the `@` clause of a Lean `require`. They are only
meaningful on Reservoir dependencies.

**lakefile.lean**
```lean-4
require "Seasawher" / "mdgen" @ "2.*"
```

**lakefile.toml**
```toml
[[require]]
name = "mdgen"
scope = "Seasawher"
version = "2.*"
```

The syntax for these versions ranges is a mix of
[Rust's](https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html?highlight=caret#version-requirement-syntax)
and
[Node's](https://github.com/npm/node-semver/tree/v7.7.3?tab=readme-ov-file#ranges)
with some Lean-friendly deviations.

### Comparators

The basic unit of semantic version ranges are version comparators. They
take a base version and a comparison operator and match versions which
compare positively with their base. Lake supports the following
comparison operators.

* `<`, `<=` / `≤`, `>`, `>=` / `≥`, `=`, `!=` / `≠`

Unlike Rust and Node, Lake supports Unicode alternatives for the
operators. It also adds the not-equal operator (`!=` / `≠`) to make
excluding broken versions easier.

Comparators can be combined into clauses via conjunction or disjunction:

* **AND clauses**: Rust-style `≥1.2.3, <1.8.0` or Node-style `1.2.3
<1.8.0`
* **OR clauses**: Node-style `1.2.7 || >=1.2.9, <2.0.0`

When the base version of a comparator has a `-` suffix (e.g.,
`>1.2.3-alpha.3`) it will match versions of the same core (`1.2.3`) with
suffixes that lexicographically compare (e.g., `1.2.3-alpha.7` or
`1.2.3-beta.2`) but will not match suffixed versions of different cores
(e.g., `3.4.5-rc5`). An empty `-` suffix can be used to disable this
behavior. For example, `<2.0.0-` will match `1.2.3-beta.2` and
`2.0.0-alpha.1`.

### Range Macros

In addition to the basic comparators, Lake also supports standard
shorthand for specifying more complex ranges. Namely, it supports the
caret (`^`) and tilde (`~`) operator along with wildcard ranges.

**Caret Ranges**
* `^1` => `≥1.0.0, <2.0.0-`
* `^1.2` => `≥1.2.0, <2.0.0-`
* `^1.2.3` => `≥1.2.3, <2.0.0-`
* `^1.2.3-beta.2` => `≥1.2.3-beta.2, <2.0.0-`
* `^0.2` => `≥0.0.0, <0.3.0-`
* `^0.2.3` => `≥0.2.3, <0.3.0-`
* `^0.0.3` => `≥0.0.3, <0.0.4-`
* `^0` => `≥0.0.0, <1.0.0-`
* `^0.0` => `≥0.0.0, <0.1.0-`

**Tilde Ranges**
* `~1` => `≥1.0.0, <2.0.0-`
* `~1.2` => `≥1.2.0, <1.3.0-`
* `~1.2.3` => `≥1.2.3, <1.3.0-`
* `~1.2.3-beta.2` => `≥1.2.3-beta.2, <1.3.0-`
* `^0` => `≥0.0.0, <1.0.0-`
* `^0.2.3` => `≥0.2.3, <0.3.0-`
* `^0.0.3` => `≥0.0.3, <0.0.4-`
* `~0` => `≥0.0.0, <1.0.0-`
* `~0.0` => `≥0.0.0, <0.1.0-`
* `~0.0.0` => `≥0.0.0, <0.1.0-`

**Wildcard Ranges**
* `*` => `≥0.0.0`
* `1.x` => `≥1.0.0, <2.0.0-`
* `1.*.x` => `≥1.0.0, <2.0.0-`
* `1.2.*` => `≥1.2.0, <1.3.0-`

These ranges closely follow Rust's and Node's syntax. Like Node but
unlike Rust, wildcard ranges support `x` and `X` as alternative syntax
for wildcards.
2025-11-03 04:18:24 +00:00
Kim Morrison
b8bd91d92b feat: simp? +suggestions (#11032)
This PR implements `simp? +suggestions`, which uses the configured
library suggestion engine to add relevant theorems to the `simp` call.
`simp +suggestions` without the `?` prints a message requiring adding
the `?`.
2025-11-03 03:26:16 +00:00
Lean stage0 autoupdater
e05b0e097c chore: update stage0 2025-11-03 02:54:49 +00:00
Mac Malone
d23dcc88ea fix: lake: pin mathlib in new/init to toolchain (#11063)
This PR changes the `math` and `math-lax` templates for `lake new` and
`lake init` to use the version of Mathlib corresponding to the current
Lean toolchain. Thus, `lake +x.y.z new <pkg> math` will use the Mathlib
for Lean `x.y.z`. On the other hand, `lake update` on such packages will
no longer update Mathlib automatically. Users will need to change the
Mathlib revision in the configuration file before updating.

This change was inspired by a
[discussion](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Build.20error.20on.20.60lake.20.2Bleanprover.2Flean4.3Av4.2E22.2E0.20new.20foo.20math.60/near/546236449)
on the community Zulip.
2025-11-03 02:16:29 +00:00
Mac Malone
1aca181fe2 fix: lake: use -O0 for debug builds (#11062)
This PR changes Lake's debug build type to use `-O0` instead of `-Og`
when compiling C code. `-Og` was found to be insufficient for debugging
compiled Lean code -- relevant code was stilled optimized out.
2025-11-03 02:16:26 +00:00
Leonardo de Moura
2c45d55683 fix: deep recursion type checking grind proof (#11061)
This PR fixes a deep recursion issue in the kernel when type-checking a
proof term produced by `grind`.

Closes #11059
2025-11-02 19:43:48 +00:00
Rob23oba
f4c71f6ec8 fix: simplify Nat.ble (#11058)
This PR changes `Nat.ble` by joining the two `Nat.ble Nat.zero _` cases
into one, allowing `decide (0 <= x) = true` and `decide (0 < succ x) =
true` to be solvable by `rfl`.
2025-11-02 12:39:49 +00:00
Henrik Böving
823173a761 fix: make ST.Ref.ptrEq behave as stated in the docs (#11056)
This PR fixes `ST.Ref.ptrEq` to act as described in the docs. This fixes
two bugs:
1. The recent `IO.RealWorld` elimination PR overlooked this function
(afaik this is the only one),
   causing its return value to be generally wrong.
2. The implementation of `ptrEq` would previously always consider two
different cells with pointer
equivalent value to be pointer equal. However, the function is supposed
to check whether two
   `Ref` are the same cell, not whether the contained elements are.
2025-11-02 10:42:33 +00:00
Leonardo de Moura
3e86729ee0 feat: grind? using finish? infrastructure (#11057)
This PR implements `grind?` using the new `grind => finish?`
infrastructure.
2025-11-02 05:00:50 +00:00
Leonardo de Moura
2da124d469 chore: remove grind offset (#11051)
This PR removes the `grind offset` module because it is (now) subsumed
by `grind order`.
2025-11-01 19:08:18 +00:00
Leonardo de Moura
1d00dee392 fix: grind order equality propagation for Nat (#11050)
This PR fixes equality propagation for `Nat` in `grind order`.
2025-11-01 18:35:34 +00:00
Leonardo de Moura
e5a6901161 feat: Nat equality propagation in grind order (#11049)
This PR implements equality propagation for `Nat` in `grind order`.
`grind order` supports offset equalities for rings, but it has an
adapter for `Nat`. Example:
```lean
example (a b : Nat) (f : Nat → Int) : a ≤ b + 1 → b + 1 ≤ a → f (1 + a) = f (1 + b + 1) := by
  grind -offset -mbtc -lia -linarith (splits := 0)
```
2025-11-01 15:37:17 +00:00
Markus Himmel
cf871a892c chore: use String.ofList instead of String.mk in elaborator+kernel (#11048)
This PR is a follow-up to #11017, preparing for the eventual removal of
the `String.mk` function.
2025-11-01 14:44:16 +00:00
Markus Himmel
d24ece1396 feat: String.toList_map (#11021)
This PR adds more theory about `Splits` for strings and deduces the
first user-facing `String` lemma, `String.toList_map`.
2025-11-01 13:54:39 +00:00
Leonardo de Moura
faed852c2f feat: equality propagation in grind order (#11047)
This PR implements (nested term) equality propagation in `grind order`.
That is, it propagates implied equalities from `grind order` back to the
`grind` core. Examples:
```lean
open Lean Grind Std

example [LE α] [IsPartialOrder α] (a b : α) (f : α → Nat) : a ≤ b → b ≤ c → c ≤ a → f a = f b := by
  grind (splits := 0)

example [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPartialOrder α] [OrderedRing α]
    (a b : α) (f : α → Int) : a ≤ b + 1 → b ≤ a - 1 → f a = f (2 + b - 1) := by
  grind -mbtc -lia -linarith (splits := 0)

example (a b : Int) (f : Int → Int) : a ≤ b + 1 → b ≤ a - 1 → f a = f (2 + b - 1) := by
  grind -mbtc -lia -linarith (splits := 0)
```
2025-11-01 13:45:54 +00:00
Sebastian Ullrich
4939f447a3 test: avoid testing colliding private inductives (#11041)
`prelude-injectivity.lean` was testing inj thm generation for all
inductives in core, including private ones, which could lead to name
clashes that should not be able to occur in actual files. Put it under
the module system to not load private decls in the first place.
2025-11-01 11:47:52 +00:00
Henrik Böving
3d307925b7 refactor: make constant folding more robust for future bugs (#11044)
This PR enforces users of the constant folder API to provide proofs of
their algebraic properties,
thus hopefully avoiding bugs such as #11042 and #11043 in the future.
2025-11-01 11:07:20 +00:00
Rob23oba
1fa67d0d47 fix: overeager Nat.sub constant folding (#11043)
This PR fixes a case of overeager constant folding on Nat where the
compiler would mistakenly assume `0 - x = x` (see also #11042 for the
same bug on UInts).
2025-11-01 10:14:20 +00:00
Henrik Böving
51ef1dcc5e fix: overeager uint constant folding (#11042)
This PR fixes a case of overeager constant folding on UInts where the
compiler would mistakenly
assume `0 - x = x`.
2025-11-01 02:42:43 +00:00
Leonardo de Moura
282b583f1d fix: panic during the processing of generalized patterns in grind (#11040)
This PR fixes a panic that occurred during the processing of generalized
E-matching patterns in `grind`.

Closes #10983
2025-10-31 16:58:11 +00:00
Leonardo de Moura
4a111501eb fix: grind invalid universe level regression (#11039)
This PR fixes the `grind` invalid universe level regression reported in
#11036

Closes #11036
2025-10-31 15:44:34 +00:00
Leonardo de Moura
16d0005991 feat: finish? produces finish only suggestion (#11034)
This PR adds a new suggestion to `finish?`. It now generates the `grind`
tactic script as before, and a `finish only` tactic. Example:
```lean
/--
info: Try these:
  [apply] ⏎
    instantiate only [findIdx, insert, = mem_indices_of_mem]
    instantiate only [= getElem?_neg, = getElem?_pos]
    cases #1bba
    · instantiate only [findIdx]
    · instantiate only
      instantiate only [= HashMap.mem_insert, = HashMap.getElem_insert]
  [apply] finish only [findIdx, insert, = mem_indices_of_mem, = getElem?_neg, = getElem?_pos, = HashMap.mem_insert,
    = HashMap.getElem_insert, #1bba]
-/
example (m : IndexMap α β) (a : α) (b : β) :
    (m.insert a b).findIdx a = if h : a ∈ m then m.findIdx a else m.size := by
  grind => finish?
```
2025-10-31 14:47:24 +00:00
Markus Himmel
377f149862 refactor: use String.ofList and String.toList for String <-> List Char conversion (#11017)
This PR establishes `String.ofList` and `String.toList` as the preferred
method for converting between strings and lists of characters and
deprecates the alternatives `String.mk`, `List.asString` and
`String.data`.
2025-10-31 14:41:23 +00:00
Markus Himmel
c41cb64ca7 chore: adjust pr-title check to enforce capitalization (#11033)
This PR updates the `pr-title` CI check to enforce that the commit
message does not start with a capital letter followed by a non-capital
letter.

This should ensure that messages do not start with a capitalized word,
but allow messages that start with an acronym.
2025-10-31 07:23:25 +00:00
Kim Morrison
1ce05b2a17 feat: library suggestion engine for local theorems (#11030)
This PR adds a library suggestion engine for local theorems. To be
useful, I still need to write more combinators to re-rank and combine
suggestions from multiple engines.

This is stacked on top of #11029.
2025-10-31 04:48:53 +00:00
Kim Morrison
0db89d65b2 chore: use 'library suggestions' rather than 'premise selection' (#11029)
This PR changes the terminology used from "premise selection" to
"library suggestions". This will be more understandable to users (we
don't assume anyone is familiar with the premise selection literature),
and avoids a conflict with the existing use of "premise" in Lean
terminology (e.g. "major premise" in induction, as well as generally the
synonym for "hypothesis"/"argument").
2025-10-31 04:07:49 +00:00
Kim Morrison
00d41d64e5 feat: remove +premises from grind? output (#11028)
This PR ensures that `grind? +premises` removes `+premises` from the
"Try this" suggestion.
2025-10-31 03:00:33 +00:00
Leonardo de Moura
40e1e097c1 fix: grind order nontermination and propagation issues (#11026)
This PR fixes a nontermination and missing propagation bug in `grind
order`. It also register relevant case-splits for arithmetic.

Closes #11001
2025-10-30 23:12:51 +00:00
Joachim Breitner
d7e4f32d75 feat: Bool.ctorIdx (#11024)
This PR lets `Bool` have `.ctorIdx` like any other inductive.
2025-10-30 21:17:43 +00:00
Joachim Breitner
c7f57d6a0b fix: avoid unnecessary branching in match compilation (#10763)
This PR improves match compilation: Branch on variables in the order
suggested by the first remaining alternative, and do not branch when the
first remaining alternative does not require it. This fixes
https://github.com/leanprover/lean4/issues/10749. With `set_option
backwards.match.rowMajor false` the old behavior can be turned on.

(For now this is an experiment to get familiar with the code and the
whole
problem domain. It is likely overly naive.)
2025-10-30 20:05:13 +00:00
Wrenna Robson
275f9077b6 fix: Rename theorems that use sorted instead of pairwise (#10743)
This PR renames theorems that use `sorted` in their name to instead use
`pairwise`.


Closes #10742.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-10-30 16:07:35 +00:00
Henrik Böving
8b28467655 perf: better detection of repeated branching on same value (#11020)
This PR improves the detection of situations where we branch multiple
times on the same value in the
code generator. Previously this would only consider repeated branching
on function arguments, now on
arbitrary values.


Closes: #11018
2025-10-30 16:02:45 +00:00
Markus Himmel
c0ad969b14 fix: docstring of ByteArray.IsValidUTF8.intro (#11022)
This PR fixes an incomplete docstring.
2025-10-30 15:57:01 +00:00
AxelBoldt
e73e0901f8 doc: improve variable names and specify types in SMul doc (#10989)
When documenting smul : M → α → α, it is unintuitive and confusing to
use variables a:M and b:α. It is better to use m:M and a:α. I also
specified the types of all involved terms in the documentation text.
2025-10-30 15:56:42 +00:00
Henrik Böving
cc046e0c18 perf: improve join point finding (#10999)
This PR improves join point finding in the compiler through two means:
1. We now handle situations where a function `f` can only become a join
point when a function `g`
   becomes a join point as well correctly.
2. We introduce a second join point finding pass after specialisation
and before the following
simplification pass, as the specialiser might have introduced new join
point opportunities for
   the simplifier to exploit.

Notably in the code from #10995 we now correctly detect the missing join
point which required both
of these changes to be made.

Closes: #10995
2025-10-30 15:05:11 +00:00
Wrenna Robson
e11ef3ee4e fix: Remove redundant instance requirement (#10941)
This PR removes a redundant instance requirement from
`Std.instIrreflLtOfIsPreorderOfLawfulOrderLT`.
2025-10-30 13:21:26 +00:00
Kim Morrison
b2b385b456 chore: update stage0 2025-10-30 18:43:59 +11:00
Kim Morrison
7a8c2daf96 feat: sine qua non premise selection 2025-10-30 18:43:59 +11:00
Lean stage0 autoupdater
33e92677ba chore: update stage0 2025-10-30 07:41:23 +00:00
Markus Himmel
5af12df54b chore: add String.ofList redefine String.toList (#11016)
This PR ensures that `String.toList` and `String.ofList` exist and have
the right `extern` annotations.
2025-10-30 07:07:12 +00:00
Kim Morrison
705084d9ba chore: deprecate more duplications (#11004)
This PR deprecates various duplicated definitions, detected in
[#mathlib4 > duplicate declarations @
💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/duplicate.20declarations/near/547434277)
2025-10-30 05:58:29 +00:00
Joachim Breitner
e2f5938e74 refactor: some Meta.Match.Match refactorings (#11011)
This PR extracts some refactorings from #10763, including dropping dead
code and not failing in `inaccessibleAsCtor`, which leadas to (slightly)
better error messages, and also on the grounds that the failing
alternative may actually be unreachable.
2025-10-29 23:24:57 +00:00
Leonardo de Moura
d3c9056d2b feat: support grind parameters in finish and finish? (#11012)
This PR ensures the `grind` tactics `finish` and `finish?` can take
parameters.
2025-10-29 20:51:48 +00:00
Joachim Breitner
2f0f0a3018 chore: pr release: git commit --allow-empty (#11013)
This PR uses `--allow-empty` so that the PR release script does not fail
when it's being retried after an intermittent failure.
2025-10-29 21:48:11 +01:00
Henrik Böving
1587d02dfb fix: more stable eager lambda lifting heuristic (#11010)
This PR makes the eager lambda lifting heuristic more predictable by
blocking it from lifting from
any kind of inlineable function, not just `@[inline]`. It also adapts
the doc-string to describe
what is actually going on.
2025-10-29 13:58:23 +00:00
Henrik Böving
a51822ead2 perf: inline Decidable instances (#11008)
This PR inlines several Decidable instances for performance reasons.

Unlike the previous #10934 it does not attempt to also simplify the
Decidable instance system as
that has proven to have non trivial performance impact.

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-10-29 10:59:54 +00:00
Henrik Böving
2cfd980528 fix: revert the waitAny refactoring (#11000)
This PR fixes a memleak caused by the Lean based `IO.waitAny`
implementation by reverting it.

This the faulty Lean implementation:
```lean
def IO.waitAny (tasks : @& List (Task α)) (h : tasks.length > 0 := by exact Nat.zero_lt_succ _) :
    BaseIO α := do
  have : Nonempty α := ⟨tasks[0].get⟩
  let promise : IO.Promise α ← IO.Promise.new
  tasks.forM <| fun t => BaseIO.chainTask (sync := true) t promise.resolve
  return promise.result!.get
```
In a situation where we call this function repeatedly in a loop with a
pair of tasks `[t1, t2]`
where `t2` is a long lived task that we pass every time and `t1` is
fresh a short lived task, `t2` will
accumlate more and more children from `BaseIO.chainTask` that fill
memory over time. The old C++
implementation did not have this issue so we are reverting.
2025-10-29 08:27:16 +00:00
Henrik Böving
2497cf0b40 fix: Revert "perf: inline decidable instances" (#11007)
This PR reverts leanprover/lean4#10934
2025-10-29 08:06:51 +00:00
Markus Himmel
167429501b refactor: redefine String.replace (#10986)
This PR defines `String.Slice.replace` and redefines `String.replace` to
use the `Slice` version.

The new implementation is generic in the pattern, so it supports things
like `"education".replace isVowel "☃!" = "☃!d☃!c☃!t☃!☃!n"`. Since it
uses the `ForwardSearcher` infrastructure, `String` patterns are
searched using KMP, unlike the previous implementation which had
quadratic runtime. As a side effect, the behavior when replacing an
empty string now matches that of most other programming languages,
namely `"abc".replace "" "k" = "kakbkck"`.
2025-10-29 07:48:33 +00:00
Sebastian Graf
d427423917 chore: remove duplicate lemmas in Std.Do.SPred (#11006)
This PR removes the duplicate lemmas
`Std.Do.SPred.{and_pure,or_pure,imp_pure,entails_pure_intro}`.
2025-10-29 07:26:24 +00:00
Markus Himmel
106b0fa661 fix: KMP implementation (#10998)
This PR fixes the KMP implementation, which did incorrect bookkeeping of
the backtracking process, leading to incorrect starting ranges of
matches.

The new implementation does not require `partial` anywhere.
2025-10-29 06:04:45 +00:00
Kim Morrison
335e34df19 chore: add deprecations for duplicated theorems (#10967) 2025-10-29 05:26:16 +00:00
Leonardo de Moura
d436619c6d feat: add anchor support for restricting search space in grind only (#11003)
This PR adds support for specifying anchors to restrict the search space
in `grind` when using `grind only`. Anchors can limit which case splits
are performed and which local lemmas are instantiated.
2025-10-29 01:16:10 +00:00
Marc Huisinga
19533ab1d4 feat: revamp server logging (#10787)
This PR revamps the server logging mechanism to allow filtering the log
output by LSP method.
2025-10-28 16:26:59 +00:00
Leonardo de Moura
28310a77ad feat: config options at finish (#10997)
This PR adds support for configuration options at `finish` and
`finish?`.
2025-10-28 15:41:26 +00:00
Lean stage0 autoupdater
8ff5b8ec2c chore: update stage0 2025-10-28 13:47:28 +00:00
Leonardo de Moura
99ff606d58 chore: rename cutsat => lia (#10991)
This PR renames `cutsat` in configuration options and trace messages to
`lia`.
2025-10-28 12:25:48 +00:00
Kim Morrison
8862d23453 chore: cleanup in Dyadic API (#10994) 2025-10-28 06:41:46 +00:00
Kim Morrison
d8335bd661 feat: add @[grind ext] attributes for extensional maps (#10993)
This PR allows `grind` to work extensionally on extensional maps/sets.
2025-10-28 05:20:45 +00:00
Kim Morrison
1981c62604 feat: make grind +premises more robust to bad suggestions (#10992)
This PR ensures that `grind +premises` silently drops warnings and
errors about bad suggestions.
2025-10-28 03:22:42 +00:00
Leonardo de Moura
bb3dd13f72 feat: set_config for setting grind configuration options (#10990)
This PR adds the `set_config` tactic for setting `grind` configuration
options. It uses the same syntax used for setting configuration options
in the `grind` main tactic.
2025-10-28 02:25:01 +00:00
Joachim Breitner
808d123e6f doc: typo in grindDef docstring (#10988)
This PR fixes a typo in the `grindDef` docstring.
2025-10-27 15:59:06 +00:00
Joachim Breitner
14d76cc062 fix: decreasing_by: preserve variable names of match alts (#10980)
This PR tries to preserve names of pattern variables in match
alternatives in `decreasing_by`, by telescoping into the concrete
alternative rather than the type of the matcher's alt. Fixes #10976.
2025-10-27 14:00:36 +00:00
Markus Himmel
d2f76ade61 fix: search for empty string (#10985)
This PR ensures that searching for an empty string returns the expected
pattern of alternating size-zero matches and size-one rejects.

In particular, splitting by an empty string returns an array formed of
the empty string, all of the string's characters as singleton strings,
followed by another empty string. This matches the [Rust
behavior](https://doc.rust-lang.org/std/primitive.str.html#method.split),
for example.
2025-10-27 13:05:33 +00:00
Rob23oba
12750d25b5 perf: inline decidable instances (#10934)
This PR adds inline annotations to several `Decidable` instances.
Additionally, it removes the `Decidable` instance for `p → q` which is
made redundant by `forall_prop_decidable`.
2025-10-27 12:05:01 +00:00
Henrik Böving
334fa475b4 fix: use general allocator for closures (#10982)
This PR changes the closure allocator to use the general allocator
instead of the small object one.
This is because users may create closures with a gigantic amount of
closed variables which in turn
boost the size of the closure beyond the small object threshold.

This issue was uncovered by #10979. Detecting that the small object
threshold is at fault requires
building mimalloc in debug mode at which point it yields:
```
mimalloc: assertion failed: at "/home/henrik/lean4/build/debug/mimalloc/src/mimalloc/src/alloc.c":132, mi_heap_malloc_small_zero
  assertion: "size <= MI_SMALL_SIZE_MAX"
```
The generated code at fault here looks as follows:
```c
LEAN_EXPORT lean_object* l_initExec___at___00res_spec__0(lean_object* x_1) {
_start:
{
lean_object* x_2; lean_object* x_3; lean_object* x_4; lean_object* x_5; lean_object* x_6; lean_object* x_7; lean_object* x_8; lean_object* x_9; lean_object* x_10; lean_object* x_11; lean_object* x_12; lean_object* x_13; lean_object* x_14;
x_2 = lean_alloc_closure((void*)(l_initializer_ext___at___00initExec___at___00res_spec__0_spec__0___lam__0___boxed), 3, 0);
x_3 = l_initExec___redArg___closed__0;
x_4 = l_initExec___redArg___closed__1;
x_5 = l_instMonadLiftNonDetT___closed__0;
x_6 = l_initExec___redArg___closed__2;
x_7 = l_initExec___at___00res_spec__0___closed__0;
lean_inc_ref(x_2);
x_8 = lean_alloc_closure((void*)(l_initExec___at___00res_spec__0___lam__29___boxed), 213, 212);
lean_closure_set(x_8, 0, x_3);
lean_closure_set(x_8, 1, x_2);
lean_closure_set(x_8, 2, x_4);
lean_closure_set(x_8, 3, x_3);
lean_closure_set(x_8, 4, x_4);
lean_closure_set(x_8, 5, x_3);
lean_closure_set(x_8, 6, x_4);
lean_closure_set(x_8, 7, x_3);
lean_closure_set(x_8, 8, x_4);
lean_closure_set(x_8, 9, x_3);
lean_closure_set(x_8, 10, x_4);
lean_closure_set(x_8, 11, x_3);
lean_closure_set(x_8, 12, x_4);
lean_closure_set(x_8, 13, x_3);
lean_closure_set(x_8, 14, x_4);
lean_closure_set(x_8, 15, x_5);
lean_closure_set(x_8, 16, x_6);
lean_closure_set(x_8, 17, x_5);
lean_closure_set(x_8, 18, x_5);
lean_closure_set(x_8, 19, x_5);
lean_closure_set(x_8, 20, x_5);
lean_closure_set(x_8, 21, x_5);
lean_closure_set(x_8, 22, x_5);
...
```
With the crash happening in `lean_alloc_closure` where we
unconditionally invoke the small allocator
which cannot cope with closures this large. Hopefully changing this to
the general purpose allocator
doesn't have too much of an impact on performance.

Closes: #10979
2025-10-27 10:16:59 +00:00
Markus Himmel
8fe260de55 feat: termination arguments for String.ValidPos and String.Slice.Pos (#10933)
This PR adds the basic infrastructure to perform termination proofs
about `String.ValidPos` and `String.Slice.Pos`.

We choose approach where the intended way to do termination arguments is
to argue about the position itself rather than some projection of it
like `remainingBytes`.

The types `String.ValidPos` and `String.Slice.Pos` are equipped with a
`WellFoundedRelation` instance given by the greater-than relation. This
means that if a function takes a position `p` and performs a recursive
call on `q`, then the decreasing obligation will be `p < q`. This works
well in the common case where `q` is `p.next h`, in which case the goal
`p < p.next h` is solved by the simplifier.

For stepping through a string backwards, we introduce a type synonym
with a `WellFoundedRelation` instance given by the less-than relation.
This means that if a function takes a position `p` and performs a
recursive call on `q` and specifies `termination_by p.down`, then the
decreasing obligation will be `q < p`. This works well in the case where
`q` is `p.prev h`, in which case the goal `p.prev h < p` is solved by
the simplifier.

For termination arguments invoving multiple strings, the lower-level
primitive `p.remainingBytes` (landing in `Nat`) is also available.

In a future PR, we will additionally provide the necessary typeclasses
instances to register `String.ValidPos` and `String.Slice.Pos` with
`grind` to make complex termination arguments more convenient in user
code.
2025-10-27 10:05:44 +00:00
Henrik Böving
7e1be20317 perf: widen more in ElimDeadBranches (#10856)
This PR performs more widening in ElimDeadBranches in an attempt to
improve performance in situations with a lot of local precision.

While this is not enough to make the compilation instant it pushes
compilation time from 12s to 3s for the example in #10857 and barely
introduces regressions so it seems like a good first step in this
direction.

Closes: #10857
2025-10-27 09:12:16 +00:00
Leonardo de Moura
3a42ee0c30 feat: grind tactic mode improvements (#10978)
This PR implements the following `grind` improvements:
1. `set_option` can now be used to set `grind` configuration options in
the interactive mode.
2. Fixes a bug in the repeated theorem instantiation detection.
3. Adds the macro `use [...]` as a shorthand for `instantiate only
[...]`.
2025-10-27 04:47:02 +00:00
Kim Morrison
30ecacd625 feat: add LawfulOfScientific class (#10971)
This PR adds a `LawfulOfScientific` class, providing compatibility with
a `Lean.Grind.Field` structure.
2025-10-27 04:18:55 +00:00
Kim Morrison
a0e742be5e chore: >6 month old deprecations (#10969) 2025-10-26 22:48:41 +00:00
Leonardo de Moura
50e2fdaa74 feat: add cdot combinator in grind tactic mode (#10975)
This PR adds the combinator ` · t_1 ... t_n` to the `grind` interactive
mode. The `finish?` tactic now generates scripts using this combinator
to conform to Mathlib coding standards. The new format is also more
compact. Example:
```lean
/--
info: Try this:
  [apply] ⏎
    instantiate only [= mem_indices_of_mem, insert, = getElem_def]
    instantiate only [= getElem?_neg, = getElem?_pos]
    cases #f590
    · cases #ffdf
      · instantiate only
        instantiate only [= Array.getElem_set]
      · instantiate only
        instantiate only [size, = HashMap.mem_insert, = HashMap.getElem_insert, = Array.getElem_push]
    · instantiate only [= mem_indices_of_mem, = getElem_def]
      instantiate only [usr getElem_indices_lt]
      instantiate only [size]
      cases #ffdf
      · instantiate only [=_ WF]
        instantiate only [= getElem?_neg, = getElem?_pos, = Array.getElem_set]
        instantiate only [WF']
      · instantiate only
        instantiate only [= HashMap.mem_insert, = HashMap.getElem_insert, = Array.getElem_push]
-/
#guard_msgs in
example (m : IndexMap α β) (a a' : α) (b : β) (h : a' ∈ m.insert a b) :
    (m.insert a b)[a'] = if h' : a' == a then b else m[a'] := by
  grind => finish?
```
2025-10-26 21:27:00 +00:00
Sebastian Ullrich
77ddfd49e6 chore: further shake improvements (#10947) 2025-10-26 11:27:19 +00:00
Kim Morrison
4887eeb77c chore: remove >6 month old deprecations (#10968) 2025-10-26 10:01:30 +00:00
Kim Morrison
9cf2877945 fix: mis-stated lemmas about empty raw maps (#10966)
This PR fixes some mis-stated lemmas which should have been about the
`.Raw` variants of maps.
2025-10-26 07:51:39 +00:00
Leonardo de Moura
97d63db52c fix: mbtc for nonlinear terms in grind cutsat (#10965)
This PR ensures that model-based theory combination in `grind cutsat`
considers nonlinear terms. Nonlinear multiplications such as `x * y` are
treated as uninterpreted symbols in `cutsat`.

Closes #10885
2025-10-26 04:35:34 +00:00
Leonardo de Moura
f8b0beeba9 fix: propagator for a^(n+m) in grind (#10964)
This PR adds a propagator for `a^(n+m)` and removes its normalizer. This
change was motivated by issue #10661

Closes #10661
2025-10-26 03:52:28 +00:00
Leonardo de Moura
93c5bd0fdd chore: use realizeGlobalConstNoOverloadWithInfo (#10963)
closes #10427
closes #10426
2025-10-26 02:45:46 +00:00
Leonardo de Moura
feb864712f fix: spurious warning message in grind (#10962)
This PR fixes a spurious warning message in `grind`.

Closes #10670
2025-10-26 02:40:12 +00:00
Leonardo de Moura
2f3211028b feat: support for Rat scientific literals (#10961)
This PR adds support for scientific literals for `Rat` in `grind`.
`grind` does not yet add support for this kind of literal in arbitrary
fields.

closes #10489
2025-10-26 02:05:26 +00:00
Leonardo de Moura
cdaa827b2a fix: grind linarith counterexample (#10960)
This PR fixes a bug in the `grind linarith` model/counterexample
construction.

Closes #10500
2025-10-26 00:27:47 +00:00
Kim Morrison
a9d6bc60d0 chore: update stage0 2025-10-26 10:29:47 +11:00
Kim Morrison
9166c71e08 feat: don't count symbols in instances and proofs 2025-10-26 10:29:47 +11:00
Kim Morrison
43b2d41e81 chore: add test file 2025-10-26 10:29:47 +11:00
Kim Morrison
e8620255a0 feat: symbol frequency environment extension 2025-10-26 10:29:47 +11:00
Leonardo de Moura
48f0eb206c test: rationals in grind linarith (#10958) 2025-10-25 21:48:43 +00:00
Leonardo de Moura
962e7d5a30 test: for #10317 (#10957)
This PR adds tests for issue that has been fixed by previous commits.

closes #10317
2025-10-25 21:42:56 +00:00
Leonardo de Moura
aa59c01742 fix: equality propagation in grind order (#10956)
This PR fixes a bug in the equality propagation procedure in
`grind.order`. Specifically, it affects the procedure that asserts
equalities in the `grind` core state that are implied by (ring)
inequalities in the `grind.order` module.

closes #10622
2025-10-25 20:12:04 +00:00
Leonardo de Moura
cd60d9c14a fix: grind order regression (#10955)
This PR fixes a regression in the `grind order` module introduced by

Closes #10953
2025-10-25 16:12:28 +00:00
Eric Wieser
b3ef7c9f25 chore: add an assertion about mkValueTypeClosure (#10954)
This is a guard against #10705; if a kernel error is raised when the
return value of this function is eventually checked, it is often
silenced downstream, making it hard to spot the failure.
If we panic here via `assert!`, then the diagnostic cannot be missed.
2025-10-25 12:59:17 +00:00
Lean stage0 autoupdater
be2c2bcf9b chore: update stage0 2025-10-25 11:32:01 +00:00
Leonardo de Moura
d5ca0c7032 fix: bug in cutsat model construction (#10951)
This PR fixes a bug in the `cutsat` incremental model construction. The
model was not being reset when new (unsatisfied) equalities were
asserted.
2025-10-25 04:16:32 +00:00
Leonardo de Moura
3c2ab0fefa feat: model-based theory combination tactic and action (#10950)
This PR adds the `mbtc` tactic to the `grind` interactive mode. It
implements model-based theory combination. It also ensures `finish?` is
capable of generating it.
2025-10-25 03:35:19 +00:00
Leonardo de Moura
1643fd7532 fix: finish? checks whether solver propagation steps are needed (#10949)
This PR ensures that solver propagation steps are necessary in the
generated tactic script to close the goal.

It produces more compact proof scripts, but this is not just an
optimization, if we include an unnecessary step, we may fail to replay
the generated script when `cases` steps are pruned using
non-chronological backtracking (NCB). For example, when executing
`finish?`, we may have performed a `cases #<anchor>` step that enabled
`ring` to propagate a new fact. If this fact is not used in the final
proof, and the corresponding `cases #<anchor>` step is pruned by NCB,
the `ring` step will fail during replay.
2025-10-25 02:27:44 +00:00
Leonardo de Moura
53442d48f5 feat: finish? produces partial tactic scripts with sorry (#10948)
This PR ensures that `finish?` produces partial tactic scripts
containing `sorry`s.
We may add an option to disable this feature in the future.
It is enabled by default because it provides a useful way to debug
`grind` failures.
2025-10-24 23:47:30 +00:00
Joachim Breitner
96bace56fa fix: run enableRealizationsForConst on sizeOf decls (#10944)
This PR runs enableRealizationsForConst on sizeOf declarations. Fixes
#10573.
2025-10-24 16:15:38 +00:00
Sebastian Ullrich
b0127e01e3 chore: final module system fixes and refinements for initial Mathlib porting (#10869) 2025-10-24 15:53:49 +00:00
Joachim Breitner
e71d0abc7d test: test case for #10775 (#10943) 2025-10-24 14:54:36 +00:00
Joachim Breitner
a6d50a61b3 fix: Meta.Closure: topologically sort abstracted vars (#10926)
This PR topologically sorts abstracted vars in
`Meta.Closure.mkValueTypeClosure` if MVars are being abstracted.
Fixes #10705

---------

Co-authored-by: Eric Wieser <efw@google.com>
2025-10-24 12:07:16 +00:00
Sebastian Ullrich
dec007693a chore: fix C++ warning (#10922) 2025-10-24 11:09:08 +00:00
Henrik Böving
1145498521 fix: regression from ST redefinition (#10940)
This PR fixes a regression introduced by the `ST` redefinition by making
the definition of `ST` as
reducible as previously. The key issue here is that in the previous
state it would reduce to a
function at which point the monad lifting mechanisms don't kick in in
the same fashion anymore.
2025-10-24 08:50:56 +00:00
Leonardo de Moura
2f07b70870 fix: default parameter value in constructor footgun at cases tactic (#10939)
This PR fixes another instance of the “default parameter value in
constructor” footgun, which was affecting the `cases` tactic in the
`grind` interactive mode.
2025-10-24 00:56:15 +00:00
Leonardo de Moura
09b36c332a fix: missing processNewFacts at solver tactics (#10938)
This PR ensures solver `grind` tactics (e.g., `ac`, `ring`, `lia`, etc)
process pending facts after making progress.
2025-10-24 00:08:06 +00:00
Leonardo de Moura
955fff52c5 fix: missing reset ematch.num at cases (#10937)
This PR fixes a missing counter reset at the `cases` tactic in `grind`
interactive mode.
2025-10-23 23:58:36 +00:00
Leonardo de Moura
6d665f3e91 fix: bugs at grind => finish? (#10936)
This PR fixes issues in `grind => finish?` that were preventing
generated `grind` tactic scripts from being successfully replayed.
2025-10-23 22:35:20 +00:00
Joachim Breitner
74fd46894f fix: deprecation warning location with field notation (#10826)
This PR fixes the location of the “deprecated constant” and similar
error messages on field notation (`e.f`, `(e).f`, `e |>. f`). Fixes
#10821.
2025-10-23 20:55:25 +00:00
Joachim Breitner
ffaadcc990 doc: warning for wf_preprocess (#10897)
This PR adds a warning to `wf_preproces` that these lemmas can be used
to introduce hidden partiality.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-10-23 20:54:40 +00:00
Joachim Breitner
54175f3b99 fix: decreasing_by: remove mdata (#10931)
This PR strips the `Expr.mdata` that `WF.Fix` uses to associate goal
with recursive calls from the goal presented to the tactics.
Fixes #10895.
2025-10-23 20:54:32 +00:00
Markus Himmel
59573646c2 chore: more minor String improvements (#10930)
This PR moves some more material out of `Init.Data.String.Basic` and
fixes the incorrect name
`String.Pos.Raw.IsValidForSlice.le_utf8ByteSize`.
2025-10-23 13:57:23 +00:00
Markus Himmel
ba7798b389 chore: more reorganization of strings (#10928)
This PR splits more material out of `Init.Data.String.Basic`.
2025-10-23 11:56:11 +00:00
Henrik Böving
f1203f3d0d perf: used hashmaps for symbol lookup in the interpreter (#10927)
This PR uses hashmaps for the symbol lookups in the IR interpreter
instead of the existing rbmaps.
Thus reducing the constant overhead per function call.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-10-23 11:45:20 +00:00
Henrik Böving
881a131ad3 chore: re-enable tests (#10923) 2025-10-23 08:38:57 +00:00
Lean stage0 autoupdater
fb3e2c15fd chore: update stage0 2025-10-23 08:03:18 +00:00
Rob23oba
fad0e69cc7 fix: make name mangling unambiguous (#10727)
This PR fixes name mangling to be unambiguous / injective by adding `00`
for disambiguation where necessary. Additionally, the inverse function,
`Lean.Name.unmangle` has been added which can be used to unmangle a
mangled identifier. This unmangler has been added to demonstrate the
injectivity but also to allow unmangling identifiers e.g. for debugging
purposes.

Closes #10724
2025-10-23 07:18:07 +00:00
Kim Morrison
cf22c367a1 feat: grind +premises (#10920)
This PR adds support for `grind +premises`, calling the currently
configured premise selection algorithm and including the results as
parameters to `grind`. (Recall that there is not currently a default
premise selector provided by Lean4: you need a downstream premise
selector to make use of this.)
2025-10-23 06:42:48 +00:00
Mac Malone
291d238ec4 test: symbol clash on private import of same def (#10915)
This PR adds a test for depending on two packages which privately import
modules that define the same Lean definition. It verifies the current
behavior of a symbol clash. This behavior will be fixed later this
quarter.
2025-10-23 03:42:52 +00:00
Kim Morrison
69e1eae480 feat: grind +lax ignores bad parameters (#10890)
This PR adds a `+lax` configuration option for `grind`, causing it to
ignore parameters referring to non-existent theorems, or to theorems for
which we can't generate a pattern. This allows throwing large sets of
theorems (e.g. from a premise selection enginre) into `grind` to see
what happens.
2025-10-23 03:19:31 +00:00
Leonardo de Moura
b1d4c9b9d5 feat: grind "silent" have (#10919)
This PR implements the `have <ident>? : <prop>` tactic for the `grind`
interactive mode. The proposition is proved using the default `grind`
search strategy. This tactic is also useful for inspecting or querying
the current `grind` state.
2025-10-23 02:36:49 +00:00
Leonardo de Moura
d748b0c8c9 feat: instantiate tactic parameter optimizer (#10916)
This PR implements parameter optimization for the generated
`instantiate` tactics produced by `finish?`.
We use a simple parameter optimizer that takes two sets as input: the
lower and upper bounds.
The lower bound consists of the theorems actually used in the proof
term, while the upper bound includes all the theorems instantiated in a
particular theorem instantiation step.
The lower bound is often sufficient to replay the proof, but in some
cases, additional theorems must be included because a theorem
instantiation may contribute to the proof by providing terms and many
not be present in the final proof term.
2025-10-23 01:22:33 +00:00
Kim Morrison
90af66d64b chore: minor changes to MePo (#10917)
This PR makes minor changes to the MePo premise selection algorithm.

I'm increasingly believing that MePo will not work well in Lean; I've
tried a few things without success. Alistair Geesing's thesis from 2023
had similar conclusions.

My intention is to reach the point we can properly benchmark premise
selection algorithms before doing any more work here.
2025-10-23 01:15:38 +00:00
Markus Himmel
3ce7d4ef5c chore: minor optimizations on the critical path (#10900)
This PR optimizes two `String` proofs and makes sure that
`MkIffOfInductiveProp` does not import `Lean.Elab.Tactic`, which
previously pushed it to the very end of the import graph.
2025-10-22 19:32:26 +00:00
Leonardo de Moura
63c06725bb feat: preserve instantiation order at finish? (#10899)
This PR ensures the generated `instantiate` tactic instantiates the
theorems using the same order used by `finish?`
2025-10-22 17:44:26 +00:00
Markus Himmel
b5dc11e8d3 chore: move some material out of Init.Data.String.Basic (#10893)
This PR splits some low-hanging fruit out of `Init.Data.String.Basic`:
basic material about `String.Pos.Raw`, `String.Substrig`, and
`String.Iterator`.

More splitting required and the remaining material is quite unorganized,
but it's a start.
2025-10-22 16:31:08 +00:00
Eric Wieser
08bc333705 perf: mark move constructors and assignment operators as noexcept (#10784)
Detected by
https://clang.llvm.org/extra/clang-tidy/checks/performance/noexcept-move-constructor.html.
This ensures constructions like `std::vector<object_ref>` call these
operators instead of the copy ones, and do not do extra refcounting.

Note that `optional` and `atomic` need something more complex using
`noexcept()`, as they are templated.
2025-10-22 14:21:51 +00:00
Henrik Böving
52b1b342ab feat: zero cost BaseIO (#10625)
This PR implements zero cost `BaseIO` by erasing the `IO.RealWorld`
parameter from argument lists and structures. This is a **major breaking
change for FFI**.

Concretely:
- `BaseIO` is defined in terms of `ST IO.RealWorld`
- `EIO` (and thus `IO`) is defined in terms of `EST IO.RealWorld`
- The opaque `Void` type is introduced and the trivial structure
optimization updated to account for it. Furthermore, arguments of type
`Void s` are removed from the argument lists of the C functions.
- `ST` is redefined as `Void s -> ST.Out s a` where `ST.Out` is a pair
of `Void s` and `a`

This together has the following major effects on our generated code:
- Functions that return `BaseIO`/`ST`/`EIO`/`IO`/`EST` now do not take
the dummy world parameter anymore. To account for this FFI code needs to
delete the dummy world parameter from the argument lists.
- Functions that return `BaseIO`/`ST` now return their wrapped value
directly. In particular `BaseIO UInt32` now returns a `uint32_t` instead
of a `lean_object*`. To account for this FFI code might have to change
the return type and does not need to call `lean_io_result_mk_ok` anymore
but can instead just `return` values right away (same with extracting
values from `BaseIO` computations.
- Functions that return `EIO`/`IO`/`EST` now only return the equivalent
of an `Except` node which reduces the allocation size. The
`lean_io_result_mk_ok`/`lean_io_result_mk_error` functions were updated
to account for this already so no change is required.

Besides improving performance by dropping allocation (sizes) we can now
also do fun new things such as:
```lean
@[extern "malloc"]
opaque malloc (size : USize) : BaseIO USize
```
2025-10-22 10:55:12 +02:00
Hikaru Hasegawa
6ee8511ae3 doc: fix example parenthesis in natAbs docstring (#10870)
This PR fixes an incorrect parenthesis in the `natAbs` docstring in
`src/Init/Data/Int/Basic.lean`.
2025-10-22 08:23:09 +00:00
Markus Himmel
6a1cc7d6b8 chore: minor String improvements (#10891)
This PR renames the cast functions on `String.ValidPos` for `set` and
`modify` to adhere to the established naming convention.

It also fixes two typos and very slighly tweaks the import graph,
shortening the critical path by a negligible amount.
2025-10-22 06:35:51 +00:00
Kim Morrison
2fb4d4bac2 chore: updates to release automation (#10888) 2025-10-22 03:17:28 +00:00
Leonardo de Moura
9b794befe0 feat: use new TermInfo.isDisplayableTerm when hovering grind anchors (#10887)
This PR uses the new `TermInfo.isDisplayableTerm` when hovering over
`cases` tactic anchors in the `grind` interactive mode.
2025-10-22 02:52:18 +00:00
Kim Morrison
38240612bc chore: add Rat.max_def lemma (#10886)
Closes #10842.
2025-10-22 00:38:52 +00:00
Mac Malone
3768c07654 fix: lake: cache revision path (#10883)
This PR fixes a bug with Lake's cache where revisions were stored at the
incorrect path. Revisions were stored at `<rev>/<pkg>.jsonl` rather than
the correct `<pkg>/<rev>.jsonl`.
2025-10-21 23:59:44 +00:00
Kim Morrison
466b0f824c chore: begin development cycle for v4.26 (#10884) 2025-10-21 23:22:58 +00:00
Kim Morrison
11eabdb000 chore: release_steps runs lake exe cache get when needed (#10882) 2025-10-21 22:49:38 +00:00
Leonardo de Moura
bd05f87d01 fix: grind proof instability (#10881)
This PR fixes a proof instability source in `grind`.

We say a proof is *unstable* if minor changes in the `.lean` file
containing the proof **affect** it.
2025-10-21 22:08:15 +00:00
Leonardo de Moura
56f3ca6fc7 fix: propagation in grind order (#10877)
This PR fixes theory propagation issue in `grind order`.
2025-10-21 16:38:39 +00:00
Sofia Rodrigues
94cb32bc46 fix: ipv4 address encoding from libuv to lean (#10854)
This PR fixes the IPv4 address encoding from libuv to lean
2025-10-21 14:17:22 +00:00
Leonardo de Moura
2f90b1dadd fix: elaborate grind state filter (#10874)
This PR uses the correct context for elaborating the `grind` state
filter.
2025-10-21 12:31:21 +00:00
Sebastian Graf
2991c66c81 chore: add missing spec lemmas for OptionT (#10867) 2025-10-21 12:01:35 +00:00
Eric Wieser
3f82b307aa fix: rule of 3 for xtimit (#10818)
This PR adds a missing move assignment operator, and deletes the copy
assignment operator.

C++ types should not implement move constructors without also
implementing move assignment. This also ensures that `m_fn` is correctly
emptied after a move, which is not guaranteed by the standard.

This change is also needed to allow `lean::optional` to be eventually
replaced by `std::optional`.
2025-10-21 12:01:23 +00:00
Sebastian Graf
bce47c6e0d fix: improve performance of mvcgen by optimizing try (mpure_intro; trivial) (#10872)
This PR improves the performance of `mvcgen` by an optimized
implementation for `try (mpure_intro; trivial)`. This tactic sequence is
used to eagerly discharge VCs and in the process instantiates schematic
variables.
2025-10-21 11:32:24 +00:00
Markus Himmel
b28daa6d60 chore: rename String.endPos -> String.rawEndPos (#10853)
This PR renames `String.endPos` to `String.rawEndPos`, as in a future
release the name `String.endPos` will be taken by the function that is
currently called `String.endValidPos`.
2025-10-21 11:25:30 +00:00
Markus Himmel
196d50156a fix: logic error in String.Slice.takeWhile (#10868)
This PR fixes a bug in `String.Slice.takeWhile` which caused it to get
its bookkeeping wrong and panic. The new version only uses safe
operations on `String.Slice.Pos`.
2025-10-21 09:52:11 +00:00
Sebastian Graf
2eb44b3cdc chore: fix a typo in the docstring for SeqRight and add documentation to getLevel (#10866) 2025-10-21 09:07:24 +00:00
Sebastian Graf
916125ae1c fix: make Std.Do.Spec.forIn'_list and friends more universe polymorphic (#10865)
This PR makes the spec `Std.Do.Spec.forIn'_list` and friends more
universe polymorphic.

The regression test came from a dicussion on the public Zulip.
2025-10-21 09:04:52 +00:00
Henrik Böving
bd0b91de07 perf: reduce amount of symbols in DLLs (#10864)
This PR reduces the amount of symbols in our DLLs by cutting open a
linking cycle of the shape:

`Environment -> Compiler -> Meta -> Environment`

This is achieved by introducing a dynamic call to the compiler hidden
behind a `Ref` as previously
done in the pretty printer.
2025-10-21 09:00:56 +00:00
Sebastian Ullrich
37b78bd53d chore: more module system fixes and refinements for finishing batteries port (#10819) 2025-10-21 08:19:50 +00:00
Marc Huisinga
2e3d947e07 feat: flag on TermInfo to force rendering of term in hover (#10805)
This PR adds a field `isDisplayableTerm` to `TermInfo` and all utility
functions which create `TermInfo` that can be set to force the language
server to render the term in hover popups.
2025-10-21 08:19:20 +00:00
Markus Himmel
a5a8f2779c chore: rename String.Range to Lean.Syntax.Range (#10852)
This PR renames `String.Range` to `Lean.Syntax.Range`, to reflect that
it is not part of the standard library.
2025-10-21 07:32:25 +00:00
Mac Malone
efbbb0b230 fix: lake: recurse directories in input_dir (#10861)
This PR fixes `input_dir` tracking to also recurse through
subdirectories. The `filter` of an `input_dir` will be applied to each
file in the directory tree (the path names of directories will not be
checked).

Closes #10827.
2025-10-21 04:19:10 +00:00
Leonardo de Moura
a366cbcd20 feat: show_term for grind interactive mode (#10862)
This PR implements the `show_term` combinator in `grind` interactive
mode.
2025-10-21 02:30:16 +00:00
Kim Morrison
756fee3e96 feat: improvements to release automation for v4.25.0-rc1 (#10860)
This PR improves the release automation. We link to CI output for
building the release tag, don't give instructions for bumping downstream
repositories until the release it ready, and improve documentation and
prompts.
2025-10-21 00:59:44 +00:00
Leonardo de Moura
2ead798d87 fix: set_option auto-completion in grind mode (#10859)
This PR fixes auto-completion for `set_option` in `grind` interactive
mode.
2025-10-21 00:53:35 +00:00
Leonardo de Moura
baacf86e7f feat: improve done tactic in grind interactive mode (#10858)
This PR improves the `done` tactic in `grind` interactive mode. It now
displays the `grind` state diagnostics for all unsolved subgoals.
2025-10-21 00:25:40 +00:00
Joachim Breitner
e3a5369bd7 perf: match compilation to use exfalso early (#10851)
This PR lets match compilation use exfalso as soon as no alternatives
are left. This way, the compiler does not have to look at subsequent
case splits.
2025-10-20 12:23:23 +00:00
Markus Himmel
c981ebc546 feat: split and splitInclusive iterators are finite (#10820)
This PR shows that the iterators returned by `String.Slice.split` and
`String.Slice.splitInclusive` are finite as long as the forward matcher
iterator for the pattern is finite (which we already know for all of our
patterns).

At actually also completely redefines the iterators to avoid the inner
loop in `Internal.nextMatch` which generates inefficient code. Instead,
when encountering a mismach from the matcher, we `skip` the split
iterator.
2025-10-20 10:21:21 +00:00
Paul Reichert
71f1a6c164 feat: Iterator find? consumer and variants (#10769)
This PR adds a `find?` consumer in analogy to `List.find?` and variants
thereof.
2025-10-20 09:12:53 +00:00
Sebastian Ullrich
0d5869bb71 fix: stuck "Missing alternative name" with incremental processing (#10848)
This PR fixes an issue where adding a missing case name after the pipe
in `induction` would not remove the now-obsolete error message.

Fixes #10847
2025-10-20 08:00:48 +00:00
Leonardo de Moura
135e7e7bd3 fix: instance tactic generation at finish? (#10846)
This PR fixes a few issues on `instance only [...]` tactic generation at
`finish?`.
2025-10-20 03:10:38 +00:00
Kim Morrison
58a884ef06 chore: update actions/checkout action in lake new template (#10845)
This PR update the `lake new` template to use the current version of the
`actions/checkout` Github workflow.
2025-10-20 02:32:52 +00:00
Kim Morrison
77e72afe0a chore: tweak error message about weak options (#10844)
This PR tweaks the error message about options defined in libraries.
This was relevant for an option defined in Mathlib, but set in FLT.
2025-10-20 02:28:20 +00:00
Leonardo de Moura
823671f744 feat: set_option tactic in grind interactive mode (#10843)
This PR implements the `set_option` tactic in `grind` interactive mode.
2025-10-20 00:44:59 +00:00
Leonardo de Moura
681724a8cf feat: generate instantiate only [...] at finish? (#10841)
This PR improves the `grind` tactic generated by the `instantiate`
action in tracing mode. It also updates the syntax for the `instantiate`
tactic, making it similar to `simp`. For example:

* `instantiate only [thm1, thm2]` instantiates only theorems `thm1` and
`thm2`.
* `instantiate [thm1, thm2]` instantiates theorems marked with the
`@[grind]` attribute **and** theorems `thm1` and `thm2`.

The action produces `instantiate only [...]` tactics. Example:

```lean
/--
info: Try this:
  [apply] ⏎
    instantiate only [= Array.getElem_set]
    instantiate only [= Array.getElem_set]
-/
#guard_msgs in
example (as bs cs : Array α) (v₁ v₂ : α)
        (i₁ i₂ j : Nat)
        (h₁ : i₁ < as.size)
        (h₂ : bs = as.set i₁ v₁)
        (h₃ : i₂ < bs.size)
        (h₄ : cs = bs.set i₂ v₂)
        (h₅ : i₁ ≠ j ∧ i₂ ≠ j)
        (h₆ : j < cs.size)
        (h₇ : j < as.size) :
    cs[j] = as[j] := by
  grind => finish?
```

Recall that `finish?` replays generated tactics before suggesting them.

The `instantiate` action inspects the generated proof term to decide
which theorems to include as parameters in the `instantiate only [...]`
tactic. However, in some cases, a theorem contributes only by adding a
term to the state. In such cases, the generated tactic cannot be fully
replayed, and the action uses
`instantiate approx [<thms instantiated>]` to indicate which parts of
the tactic script are approximate. The `approx` is just a hint for
users.
2025-10-19 23:35:27 +00:00
Lean stage0 autoupdater
28dd72d514 chore: update stage0 2025-10-19 23:45:51 +00:00
Leonardo de Moura
61ee3b2711 feat: expose optionValue parser (#10839)
This PR exposes the `optionValue` parser used to implement the
`set_option` notation.
2025-10-19 22:57:47 +00:00
Leonardo de Moura
206eb73cd9 feat: finish? tactic for grind interactive mode (#10837)
This PR implements the `finish?` tactic for the `grind` interactive
mode. When it successfully closes the goal, it produces a code action
that allows the user to close the goal using explicit grind tactic
steps, i.e., without any search. It also makes explicit which solvers
have been used.

This is just the first version, we will add many "bells and whistles"
later. For example, `instantiate` steps will clearly show which theorems
have been instantiated.

Example:

```lean
/--
info: Try this:
  [apply] ⏎
    cases #b0f4
    next => cases #50fc
    next => cases #50fc <;> lia
-/
#guard_msgs in
example (p : Nat → Prop) (x y z w : Int) :
    (x = 1 ∨ x = 2) →
    (w = 1 ∨ w = 4) →
    (y = 1 ∨ (∃ x : Nat, y = 3 - x ∧ p x)) →
    (z = 1 ∨ z = 0) → x + y ≤ 6 := by
  grind => finish?
```

The anchors in the generated script are based on stable hash codes.
Moreover, users can hover over them to see the exact term used in the
case split. `grind?` will also be implemented using the new framework.
2025-10-19 03:52:32 +00:00
Leonardo de Moura
09f22203f8 feat: add SolverExtension.action and Solvers.mkAction (#10836)
This PR implements support for `Action` in the `grind` solver extensions
(`SolverExtension`). It also provides the `Solvers.mkAction` function
that constructs an `Action` using all registered solvers. The generated
action is "fair," that is, a solver cannot prevent other solvers from
making progress.
2025-10-19 00:53:45 +00:00
Leonardo de Moura
ef23782608 feat: ring action (#10834)
This PR implements the `ring` action for `grind`.
2025-10-18 22:01:51 +00:00
Leonardo de Moura
e2b5747f4b feat: evalTactic in GrindM (#10833)
This PR implements infrastructure for evaluating `grind` tactics in the
`GrindM` monad. We are going to use it to check whether auto-generated
tactics can effectively close the original goal.
2025-10-18 17:02:36 +00:00
Markus Himmel
dad541265c refactor: move operations on String.Pos.Raw to the String.Pos.Raw namespace (#10735)
This PR moves many operations involving `String.Pos.Raw` to a the
`String.Pos.Raw` namespace with the eventual aim of freeing up the
`String` namespace to contain operations using `String.ValidPos` (to be
renamed to `String.Pos`) instead.

This PR adds the `String.ValidPos.set` and `String.ValidPos.modify`
functions.

After this PR, `String.pos_lt_eq` is no longer a `simp` lemma. Add
`String.Pos.Raw.lt_iff` as a `simp` lemma if your proofs break.
2025-10-18 12:12:55 +00:00
Markus Himmel
ca7a8e18b7 refactor: rename String.split to String.splitToList (#10822)
This PR renames `String.split` to `String.splitToList`, because soon the
name `String.split` will be used by a new implementation which is
superior because it is polymorphic over the pattern kind and it returns
an iterator of slices instead of a list of strings.
2025-10-18 12:12:54 +00:00
Sebastian Ullrich
721ffe5713 chore: CI: disable tree-less clone on nightly release 2025-10-18 13:32:04 +02:00
Leonardo de Moura
c76411d6c5 feat: compact notation for inspecting grind state (#10828)
This PR implements a compact notation for inspecting the `grind` state
in interactive mode. Within a `grind` tactic block, each tactic may
optionally have a suffix of the form `| filter?`.

Examples:

```lean
instantiate | gen > 0  -- Displays terms in the `grind` state after executing `instantiate` with generation greater than zero
```

```lean
instantiate |  -- Displays the `grind` state after executing `instantiate`
```

Remark: If the user places the cursor one space before `|`, the state
*before* executing `instantiate` is displayed.
This PR removes the code that was silently displaying the `grind` state
after each tactic step, as it was too noisy.
It also updates the notation for the `first` combinator in the `grind`
tactic mode to avoid conflicts with the new syntax.
2025-10-17 19:54:23 +00:00
Joachim Breitner
c22100036c fix: more pedantic checking of inaccessible patterns (#10796)
This PR changes match compilation to reject some pattern matches that
were previously accepted due to inaccessible patterns sometimes treated
like accessible ones. Fixes #10794.
2025-10-17 17:02:54 +00:00
Sebastian Ullrich
5800ce17b3 chore: CI: upgrade all git checkouts to tree-less clones (#10814) 2025-10-17 16:23:42 +00:00
Leonardo de Moura
78ab60d045 feat: cases? tactic for grind interactive mode (#10824)
This PR implements the `cases?` tactic for the `grind` interactive mode.
It provides a convenient way to select anchors. Users can filter the
candidates using the filter language. Examples:

<img width="1454" height="399" alt="image"
src="https://github.com/user-attachments/assets/fc370c2e-97f9-4d68-93a6-f0ebf33499f8"
/>

<img width="1447" height="166" alt="image"
src="https://github.com/user-attachments/assets/6c9c3707-79f7-4c63-8007-8d0aaedecc45"
/>
2025-10-17 15:44:19 +00:00
Sofia Rodrigues
f9adafe54d feat: adds acceptSelector and modified selectors (#10667)
This PR adds more selectors for TCP and Signals.

It also fixes a problem with `Selectors` that they cannot be closures
over a promise, otherwise it causes the waiter promise to never be
dropped.
2025-10-17 14:53:46 +00:00
Sebastian Ullrich
69d8d63d58 feat: hint about inaccessible private declaration on dot notation failure (#10803)
This PR improves the error message of generalized field notation if the
issue is that the resolved declaration is not visible in the current
context.
2025-10-17 09:31:56 +00:00
Sebastian Ullrich
dc7c184ee2 chore: CI: introduce fast-ci label 2025-10-17 08:45:41 +02:00
Sebastian Ullrich
e43ff50e76 chore: CI: revert macOS tests accidentally run on PRs 2025-10-17 08:45:41 +02:00
Leonardo de Moura
4ce7ad19ce feat: lia, linarith, and ac actions (#10812)
This PR implements `lia`, `linarith`, and `ac` actions for `grind`
interactive mode.
2025-10-17 03:56:21 +00:00
Leonardo de Moura
2a70da50c1 feat: proper case-split anchor generation in splitNext for grind? and finish? (#10811)
This PR implements proper case-split anchor generation in the
`splitNext` action, which will be used to implement `grind?` and
`finish?`.
2025-10-17 03:07:13 +00:00
3277 changed files with 45910 additions and 17989 deletions

14
.claude/CLAUDE.md Normal file
View File

@@ -0,0 +1,14 @@
When asked to implement new features:
* begin by reviewing existing relevant code and tests
* write comprehensive tests first (expecting that these will initially fail)
* and then iterate on the implementation until the tests pass.
To build Lean you should use `make -j$(nproc) -C build/release`.
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
*Never* report success on a task unless you have verified both a clean build without errors, and that the relevant tests pass. You have to keep working until you have verified both of these.
All new tests should go in `tests/lean/run/`. Note that these tests don't have expected output, and just run on a success or failure basis. So you should use `#guard_msgs` to check for specific messages.
If you are not following best practices specific to this repository and the user expresses frustration, stop and ask them to help update this `.claude/CLAUDE.md` file with the missing guidance.

View File

@@ -16,14 +16,26 @@ These comments explain the scripts' behavior, which repositories get special han
## Process
1. Run `script/release_checklist.py {version}` to check the current status
2. Create a todo list tracking all repositories that need updates
3. For each repository that needs updating:
2. **CRITICAL: If preliminary lean4 checks fail, STOP immediately and alert the user**
- Check for: release branch exists, CMake version correct, tag exists, release page exists, release notes exist
- **IMPORTANT**: The release page is created AUTOMATICALLY by CI after pushing the tag - DO NOT create it manually
- Do NOT create any PRs or proceed with repository updates if these checks fail
3. Create a todo list tracking all repositories that need updates
4. **CRITICAL RULE: You can ONLY run `release_steps.py` for a repository if `release_checklist.py` explicitly says to do so**
- The checklist output will say "Run `script/release_steps.py {version} {repo_name}` to create it"
- If a repository shows "🟡 Dependencies not ready", you CANNOT create a PR for it yet
- You MUST rerun `release_checklist.py` before attempting to create PRs for any new repositories
5. For each repository that the checklist says needs updating:
- Run `script/release_steps.py {version} {repo_name}` to create the PR
- Mark it complete when the PR is created
4. After creating PRs, notify the user which PRs need review and merging
5. Continuously rerun `script/release_checklist.py {version}` to check progress
6. As PRs are merged, dependent repositories will become ready - create PRs for those as well
7. Continue until all repositories are updated and the release is complete
6. After creating PRs, notify the user which PRs need review and merging
7. **MANDATORY: Rerun `release_checklist.py` to check current status**
- Do this after creating each batch of PRs
- Do this after the user reports PRs have been merged
- NEVER assume a repository is ready without checking the checklist output
8. As PRs are merged and tagged, dependent repositories will become ready
9. Continue the cycle: run checklist → create PRs for ready repos → wait for merges → repeat
10. Continue until all repositories are updated and the release is complete
## Important Notes

View File

@@ -213,7 +213,7 @@ jobs:
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
if: matrix.release
with:
name: build-${{ matrix.name }}

View File

@@ -11,8 +11,8 @@ jobs:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
filter: blob:none
fetch-depth: 0
filter: tree:0
- name: Find base commit
if: github.event_name == 'pull_request'

View File

@@ -116,6 +116,7 @@ jobs:
# rerun the workflow run after setting the `release-ci`/`merge-ci` labels.
run: |
check_level=0
fast=false
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
@@ -128,9 +129,13 @@ jobs:
elif echo "$labels" | grep -q "merge-ci"; then
check_level=1
fi
if echo "$labels" | grep -q "fast-ci"; then
fast=true
fi
fi
echo "check-level=$check_level" >> "$GITHUB_OUTPUT"
echo "fast=$fast" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
@@ -140,7 +145,8 @@ jobs:
with:
script: |
const level = ${{ steps.set-level.outputs.check-level }};
console.log(`level: ${level}`);
const fast = ${{ steps.set-level.outputs.fast }};
console.log(`level: ${level}, fast: ${fast}`);
// use large runners where available (original repo)
let large = ${{ github.repository == 'leanprover/lean4' }};
const isPr = "${{ github.event_name }}" == "pull_request";
@@ -165,7 +171,8 @@ jobs:
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
"os": "ubuntu-latest",
// usually not a bottleneck so make exclusive to `fast-ci`
"os": large && fast ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"release": true,
// Special handling for release jobs. We want:
// 1. To run it in PRs so developers get PR toolchains (so secondary without tests is sufficient)
@@ -193,8 +200,8 @@ jobs:
"test": true,
// NOTE: `test-speedcenter` currently seems to be broken on `ubuntu-latest`
"test-speedcenter": large && level >= 2,
// made explicit until it can be assumed to have propagated to PRs
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
// We are not warning-free yet on all platforms, start here
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-Werror",
},
{
"name": "Linux Reldebug",
@@ -230,10 +237,9 @@ jobs:
{
"name": "macOS aarch64",
// standard GH runner only comes with 7GB so use large runner if possible when running tests
"os": large && level >= 1 ? "nscloud-macos-sequoia-arm64-6x14" : "macos-15",
"os": large && (fast || level >= 1) ? "nscloud-macos-sequoia-arm64-6x14" : "macos-15",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"test": true,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-aarch64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
@@ -241,11 +247,12 @@ jobs:
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
// See "Linux release" for release job levels; Grove is not a concern here
"enabled": isPr || level != 1,
"test": level >= 1,
"secondary": level == 0,
},
{
"name": "Windows",
"os": large && level == 2 ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"os": large && (fast || level == 2) ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"release": true,
"enabled": level >= 2,
"test": true,
@@ -368,11 +375,11 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -397,8 +404,10 @@ jobs:
with:
# needed for tagging
fetch-depth: 0
# Doesn't seem to be working when additionally fetching from lean4-nightly
#filter: tree:0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: artifacts
- name: Prepare Nightly Release
@@ -416,7 +425,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@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
body_path: diff.md
prerelease: true

View File

@@ -48,17 +48,17 @@ 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}"
@@ -71,7 +71,7 @@ jobs:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -86,7 +86,7 @@ jobs:
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
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.
@@ -200,7 +200,7 @@ jobs:
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
| jq -r '.[].name')"
if echo "$LABELS" | grep -q "^force-mathlib-ci$"; then
echo "force-mathlib-ci label detected, forcing CI despite issues"
MESSAGE="Forcing Mathlib CI because the \`force-mathlib-ci\` label is present, despite problem: $MESSAGE"
@@ -301,7 +301,7 @@ jobs:
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
| jq -r '.[].name')"
if echo "$LABELS" | grep -q "^force-manual-ci$"; then
echo "force-manual-ci label detected, forcing CI despite issues"
MESSAGE="Forcing reference manual CI because the \`force-manual-ci\` label is present, despite problem: $MESSAGE"
@@ -401,6 +401,7 @@ jobs:
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
- name: Check if tag exists
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
@@ -425,7 +426,7 @@ jobs:
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ 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 }}"
git commit --allow-empty -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, updating lean-toolchain."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
@@ -434,7 +435,7 @@ jobs:
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
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit --allow-empty -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
@@ -460,6 +461,7 @@ jobs:
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
- name: install elan
run: |
@@ -494,7 +496,7 @@ jobs:
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 }}"
git commit --allow-empty -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, updating lean-toolchain and bumping Batteries."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
@@ -505,7 +507,7 @@ jobs:
git add lean-toolchain
lake update batteries
git add lake-manifest.json
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit --allow-empty -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
@@ -530,6 +532,7 @@ jobs:
token: ${{ secrets.MANUAL_PR_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
- name: Check if tag in reference manual exists
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
@@ -555,7 +558,7 @@ jobs:
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
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 }}"
git commit --allow-empty -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, updating lean-toolchain."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
@@ -565,7 +568,7 @@ jobs:
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git add lake-manifest.json
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit --allow-empty -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes

View File

@@ -15,6 +15,6 @@ jobs:
script: |
const msg = context.payload.pull_request? context.payload.pull_request.title : context.payload.merge_group.head_commit.message;
console.log(`Message: ${msg}`)
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): .*[^.]($|\n\n)/.test(msg)) {
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): (?![A-Z][a-z]).*[^.]($|\n\n)/.test(msg)) {
core.setFailed('PR title does not follow the Commit Convention (https://leanprover.github.io/lean4/doc/dev/commit_convention.html).');
}

View File

@@ -52,7 +52,7 @@ In the case of `@[extern]` all *irrelevant* types are removed first; see next se
Similarly, the signed integer types `Int8`, ..., `Int64`, `ISize` are also represented by the unsigned C types `uint8_t`, ..., `uint64_t`, `size_t`, respectively, because they have a trivial structure.
* `Nat` and `Int` are represented by `lean_object *`.
Their runtime values is either a pointer to an opaque bignum object or, if the lowest bit of the "pointer" is 1 (`lean_is_scalar`), an encoded unboxed natural number or integer (`lean_box`/`lean_unbox`).
* A universe `Sort u`, type constructor `... → Sort u`, or proposition `p : Prop` is *irrelevant* and is either statically erased (see above) or represented as a `lean_object *` with the runtime value `lean_box(0)`
* A universe `Sort u`, type constructor `... → Sort u`, `Void α` or proposition `p : Prop` is *irrelevant* and is either statically erased (see above) or represented as a `lean_object *` with the runtime value `lean_box(0)`
* Any other type is represented by `lean_object *`.
Its runtime value is a pointer to an object of a subtype of `lean_object` (see the "Inductive types" section below) or the unboxed value `lean_box(cidx)` for the `cidx`th constructor of an inductive type if this constructor does not have any relevant parameters.
@@ -129,8 +129,7 @@ For all other modules imported by `lean`, the initializer is run without `builti
Thus `[init]` functions are run iff their module is imported, regardless of whether they have native code available or not, while `[builtin_init]` functions are only run for native executable or plugins, regardless of whether their module is imported or not.
`lean` uses built-in initializers for e.g. registering basic parsers that should be available even without importing their module (which is necessary for bootstrapping).
The initializer for module `A.B` is called `initialize_A_B` and will automatically initialize any imported modules.
Module initializers are idempotent (when run with the same `builtin` flag), but not thread-safe.
The initializer for module `A.B` in a package `foo` is called `initialize_foo_A_B`. For modules in the Lean core (e.g., `Init.Prelude`), the initializer is called `initialize_Init_Prelude`. Module initializers will automatically initialize any imported modules. They are also idempotent (when run with the same `builtin` flag), but not thread-safe.
**Important for process-related functionality**: If your application needs to use process-related functions from libuv, such as `Std.Internal.IO.Process.getProcessTitle` and `Std.Internal.IO.Process.setProcessTitle`, you must call `lean_setup_args(argc, argv)` (which returns a potentially modified `argv` that must be used in place of the original) **before** calling `lean_initialize()` or `lean_initialize_runtime_module()`. This sets up process handling capabilities correctly, which is essential for certain system-level operations that Lean's runtime may depend on.
@@ -141,8 +140,8 @@ void lean_initialize_runtime_module();
void lean_initialize();
char ** lean_setup_args(int argc, char ** argv);
lean_object * initialize_A_B(uint8_t builtin, lean_object *);
lean_object * initialize_C(uint8_t builtin, lean_object *);
lean_object * initialize_A_B(uint8_t builtin);
lean_object * initialize_C(uint8_t builtin);
...
argv = lean_setup_args(argc, argv); // if using process-related functionality
@@ -152,7 +151,7 @@ lean_initialize_runtime_module();
lean_object * res;
// use same default as for Lean executables
uint8_t builtin = 1;
res = initialize_A_B(builtin, lean_io_mk_world());
res = initialize_A_B(builtin);
if (lean_io_result_is_ok(res)) {
lean_dec_ref(res);
} else {
@@ -160,7 +159,7 @@ if (lean_io_result_is_ok(res)) {
lean_dec(res);
return ...; // do not access Lean declarations if initialization failed
}
res = initialize_C(builtin, lean_io_mk_world());
res = initialize_C(builtin);
if (lean_io_result_is_ok(res)) {
...

View File

@@ -94,10 +94,8 @@ theorem List.palindrome_of_eq_reverse (h : as.reverse = as) : Palindrome as := b
next => exact Palindrome.nil
next a => exact Palindrome.single a
next a b as ih =>
have : a = b := by simp_all
subst this
have : as.reverse = as := by simp_all
exact Palindrome.sandwich a (ih this)
obtain rfl, h, - := by simpa using h
exact Palindrome.sandwich b (ih h)
/-!
We now define a function that returns `true` iff `as` is a palindrome.

View File

@@ -1,132 +0,0 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean
namespace Lean.Meta.Grind.Analyzer
/-!
A simple E-matching annotation analyzer.
For each theorem annotated as an E-matching candidate, it creates an artificial goal, executes `grind` and shows the
number of instances created.
For a theorem of the form `params -> type`, the artificial goal is of the form `params -> type -> False`.
-/
/--
`grind` configuration for the analyzer. We disable case-splits and lookahead,
increase the number of generations, and limit the number of instances generated.
-/
def config : Grind.Config := {
splits := 0
lookahead := false
mbtc := false
ematch := 20
instances := 100
gen := 10
}
structure Config where
/-- Minimum number of instantiations to trigger summary report -/
min : Nat := 10
/-- Minimum number of instantiations to trigger detailed report -/
detailed : Nat := 50
def mkParams : MetaM Params := do
let params Grind.mkParams config
let ematch getEMatchTheorems
let casesTypes Grind.getCasesTypes
return { params with ematch, casesTypes }
/-- Returns the total number of generated instances. -/
private def sum (cs : PHashMap Origin Nat) : Nat := Id.run do
let mut r := 0
for (_, c) in cs do
r := r + c
return r
private def thmsToMessageData (thms : PHashMap Origin Nat) : MetaM MessageData := do
let data := thms.toArray.filterMap fun (origin, c) =>
match origin with
| .decl declName => some (declName, c)
| _ => none
let data := data.qsort fun (d₁, c₁) (d₂, c₂) => if c₁ == c₂ then Name.lt d₁ d₂ else c₁ > c₂
let data data.mapM fun (declName, counter) =>
return .trace { cls := `thm } m!"{.ofConst (← mkConstWithLevelParams declName)} ↦ {counter}" #[]
return .trace { cls := `thm } "instances" data
/--
Analyzes theorem `declName`. That is, creates the artificial goal based on `declName` type,
and invokes `grind` on it.
-/
def analyzeEMatchTheorem (declName : Name) (c : Config) : MetaM Unit := do
let info getConstInfo declName
let mvarId forallTelescope info.type fun _ type => do
withLocalDeclD `h type fun _ => do
return ( mkFreshExprMVar (mkConst ``False)).mvarId!
let result Grind.main mvarId ( mkParams) (pure ())
let thms := result.counters.thm
let s := sum thms
if s > c.min then
IO.println s!"{declName} : {s}"
if s > c.detailed then
logInfo m!"{declName}\n{← thmsToMessageData thms}"
-- Not sure why this is failing: `down_pure` perhaps has an unnecessary universe parameter?
run_meta analyzeEMatchTheorem ``Std.Do.SPred.down_pure {}
/-- Analyzes all theorems in the standard library marked as E-matching theorems. -/
def analyzeEMatchTheorems (c : Config := {}) : MetaM Unit := do
let origins := ( getEMatchTheorems).getOrigins
let decls := origins.filterMap fun | .decl declName => some declName | _ => none
for declName in decls.mergeSort Name.lt do
try
analyzeEMatchTheorem declName c
catch e =>
logError m!"{declName} failed with {e.toMessageData}"
logInfo m!"Finished analyzing {decls.length} theorems"
/-- Macro for analyzing E-match theorems with unlimited heartbeats -/
macro "#analyzeEMatchTheorems" : command => `(
set_option maxHeartbeats 0 in
run_meta analyzeEMatchTheorems
)
#analyzeEMatchTheorems
-- -- We can analyze specific theorems using commands such as
set_option trace.grind.ematch.instance true
-- 1. grind immediately sees `(#[] : Array α) = ([] : List α).toArray` but probably this should be hidden.
-- 2. `Vector.toArray_empty` keys on `Array.mk []` rather than `#v[].toArray`
-- I guess we could add `(#[].extract _ _).extract _ _` as a stop pattern.
run_meta analyzeEMatchTheorem ``Array.extract_empty {}
-- Neither `Option.bind_some` nor `Option.bind_fun_some` fire, because the terms appear inside
-- lambdas. So we get crazy things like:
-- `fun x => ((some x).bind some).bind fun x => (some x).bind fun x => (some x).bind some`
-- We could consider replacing `filterMap_some` with
-- `filterMap g (filterMap f xs) = filterMap (f >=> g) xs`
-- to avoid the lambda that `grind` struggles with, but this would require more API around the fish.
run_meta analyzeEMatchTheorem ``Array.filterMap_some {}
-- Not entirely certain what is wrong here, but certainly
-- `eq_empty_of_append_eq_empty` is firing too often.
-- Ideally we could instantiate this is we fine `xs ++ ys` in the same equivalence class,
-- note just as soon as we see `xs ++ ys`.
-- I've tried removing this in https://github.com/leanprover/lean4/pull/10162
run_meta analyzeEMatchTheorem ``Array.range'_succ {}
-- Perhaps the same story here.
run_meta analyzeEMatchTheorem ``Array.range_succ {}
-- `zip_map_left` and `zip_map_right` are bad grind lemmas,
-- checking if they can be removed in https://github.com/leanprover/lean4/pull/10163
run_meta analyzeEMatchTheorem ``Array.zip_map {}
-- It seems crazy to me that as soon as we have `0 >>> n = 0`, we instantiate based on the
-- pattern `0 >>> n >>> m` by substituting `0` into `0 >>> n` to produce the `0 >>> n >>> n`.
-- I don't think any forbidden subterms can help us here. I don't know what to do. :-(
run_meta analyzeEMatchTheorem ``Int.zero_shiftRight {}

View File

@@ -57,19 +57,19 @@ def main (args : List String) : IO Unit := do
sec := "\n\n" ++ sec
if insertPos?.isNone then
sec := sec ++ "\n\n"
text := text.extract 0 insertPos ++ sec ++ text.extract insertPos text.endPos
text := text.extract 0 insertPos ++ sec ++ text.extract insertPos text.rawEndPos
-- prepend each import with `public `
for imp in imps.reverse do
let insertPos := imp.raw.getPos?.get!
let prfx := if doMeta then "public meta " else "public "
text := text.extract 0 insertPos ++ prfx ++ text.extract insertPos text.endPos
text := text.extract 0 insertPos ++ prfx ++ text.extract insertPos text.rawEndPos
-- insert `module` header
let mut initText := text.extract 0 startPos
if !initText.trim.isEmpty then
-- If there is a header comment, preserve it and put `module` in the line after
initText := initText.trimRight ++ "\n"
text := initText ++ "module\n\n" ++ text.extract startPos text.endPos
text := initText ++ "module\n\n" ++ text.extract startPos text.rawEndPos
IO.FS.writeFile path text

View File

@@ -131,10 +131,11 @@ structure State where
`transDeps[i]` is the (non-reflexive) transitive closure of `mods[i].imports`. More specifically,
* `j ∈ transDeps[i].pub` if `i -(public import)->+ j`
* `j ∈ transDeps[i].priv` if `i -(import ...)-> _ -(public import)->* j`
* `j ∈ transDeps[i].priv` if `i -(import all)->+ -(public import ...)-> _ -(public import)->* j`
* `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import ...)->* j`
* `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import ...)->* j`
* `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ -(public meta import ...)-> _ -(public (meta)? import ...)->* j`
* `j ∈ transDeps[i].priv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].pub/priv`
* `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import)->* j`
* `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import)->* j`
* `j ∈ transDeps[i].metaPriv` if `i -(import ...)-> i'` and `j ∈ transDeps[i'].metaPub`
* `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].metaPub/metaPriv`
-/
transDeps : Array Needs := #[]
/--
@@ -162,10 +163,10 @@ def addTransitiveImps (transImps : Needs) (imp : Import) (j : Nat) (impTransImps
-- `j ∈ transDeps[i].priv` if `i -(import ...)-> _ -(public import)->* j`
transImps := transImps.union .priv {j} |>.union .priv (impTransImps.get .pub)
if imp.importAll then
-- `j ∈ transDeps[i].priv` if `i -(import all)->+ -(public import ...)-> _ -(public import)->* j`
transImps := transImps.union .priv (impTransImps.get .pub)
-- `j ∈ transDeps[i].priv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].pub/priv`
transImps := transImps.union .priv (impTransImps.get .pub impTransImps.get .priv)
-- `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import ...)->* j`
-- `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import)->* j`
if imp.isExported then
transImps := transImps.union .metaPub (impTransImps.get .metaPub)
if imp.isMeta then
@@ -173,10 +174,13 @@ def addTransitiveImps (transImps : Needs) (imp : Import) (j : Nat) (impTransImps
if !imp.isExported then
if imp.isMeta then
-- `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import ...)->* j`
-- `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import)->* j`
transImps := transImps.union .metaPriv {j} |>.union .metaPriv (impTransImps.get .pub impTransImps.get .metaPub)
if imp.importAll then
-- `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ -(public meta import ...)-> _ -(public (meta)? import ...)->* j`
-- `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].metaPub/metaPriv`
transImps := transImps.union .metaPriv (impTransImps.get .metaPub impTransImps.get .metaPriv)
else
-- `j ∈ transDeps[i].metaPriv` if `i -(import ...)-> i'` and `j ∈ transDeps[i'].metaPub`
transImps := transImps.union .metaPriv (impTransImps.get .metaPub)
transImps
@@ -185,7 +189,8 @@ def addTransitiveImps (transImps : Needs) (imp : Import) (j : Nat) (impTransImps
def calcNeeds (env : Environment) (i : ModuleIdx) : Needs := Id.run do
let mut needs := default
for ci in env.header.moduleData[i]!.constants do
let pubCI? := env.setExporting true |>.find? ci.name
-- Added guard for cases like `structure` that are still exported even if private
let pubCI? := guard (!isPrivateName ci.name) *> (env.setExporting true).find? ci.name
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
needs := visitExpr k ci.type needs
if let some e := ci.value? (allowOpaque := true) then
@@ -216,7 +221,8 @@ def getExplanations (env : Environment) (i : ModuleIdx) :
Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name)) := Id.run do
let mut deps := default
for ci in env.header.moduleData[i]!.constants do
let pubCI? := env.setExporting true |>.find? ci.name
-- Added guard for cases like `structure` that are still exported even if private
let pubCI? := guard (!isPrivateName ci.name) *> (env.setExporting true).find? ci.name
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
deps := visitExpr k ci.name ci.type deps
if let some e := ci.value? (allowOpaque := true) then
@@ -286,7 +292,7 @@ and `endPos` is the position of the end of the header.
-/
def parseHeaderFromString (text path : String) :
IO (System.FilePath × Parser.InputContext ×
TSyntaxArray ``Parser.Module.import × String.Pos) := do
TSyntax ``Parser.Module.header × String.Pos.Raw) := do
let inputCtx := Parser.mkInputContext text path
let (header, parserState, msgs) Parser.parseHeader inputCtx
if !msgs.toList.isEmpty then -- skip this file if there are parse errors
@@ -294,8 +300,8 @@ def parseHeaderFromString (text path : String) :
throw <| .userError "parse errors in file"
-- the insertion point for `add` is the first newline after the imports
let insertion := header.raw.getTailPos?.getD parserState.pos
let insertion := text.findAux (· == '\n') text.endPos insertion + 1
pure (path, inputCtx, .mk header.raw[2].getArgs, insertion)
let insertion := text.findAux (· == '\n') text.endPos insertion + '\n'
pure (path, inputCtx, header, insertion)
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
@@ -304,13 +310,18 @@ and `endPos` is the position of the end of the header.
-/
def parseHeader (srcSearchPath : SearchPath) (mod : Name) :
IO (System.FilePath × Parser.InputContext ×
TSyntaxArray ``Parser.Module.import × String.Pos) := do
TSyntax ``Parser.Module.header × String.Pos.Raw) := do
-- Parse the input file
let some path srcSearchPath.findModuleWithExt "lean" mod
| throw <| .userError s!"error: failed to find source file for {mod}"
let text IO.FS.readFile path
parseHeaderFromString text path.toString
def decodeHeader : TSyntax ``Parser.Module.header Option (TSyntax `module) × Option (TSyntax `prelude) × TSyntaxArray ``Parser.Module.import
| `(Parser.Module.header| $[module%$moduleTk?]? $[prelude%$preludeTk?]? $imports*) =>
(moduleTk?.map .mk, preludeTk?.map .mk, imports)
| _ => unreachable!
def decodeImport : TSyntax ``Parser.Module.import Import
| `(Parser.Module.import| $[public%$pubTk?]? $[meta%$metaTk?]? import $[all%$allTk?]? $id) =>
{ module := id.getId, isExported := pubTk?.isSome, isMeta := metaTk?.isSome, importAll := allTk?.isSome }
@@ -326,11 +337,20 @@ def decodeImport : TSyntax ``Parser.Module.import → Import
* `addOnly`: if true, only add missing imports, do not remove unused ones
-/
def visitModule (srcSearchPath : SearchPath)
(i : Nat) (needs : Needs) (preserve : Needs) (edits : Edits)
(i : Nat) (needs : Needs) (preserve : Needs) (edits : Edits) (headerStx : TSyntax ``Parser.Module.header)
(addOnly := false) (githubStyle := false) (explain := false) : StateT State IO Edits := do
let s get
-- Do transitive reduction of `needs` in `deps`.
let mut deps := needs
let (_, prelude?, imports) := decodeHeader headerStx
if prelude?.isNone then
deps := deps.union .pub {s.env.getModuleIdx? `Init |>.get!}
for imp in imports do
if addOnly || imp.raw.getTrailing?.any (·.toString.toSlice.contains "shake: keep") then
let imp := decodeImport imp
let j := s.env.getModuleIdx? imp.module |>.get!
let k := NeedsKind.ofImport imp
deps := deps.union k {j}
for j in [0:s.mods.size] do
let transDeps := s.transDeps[j]!
for k in NeedsKind.all do
@@ -354,7 +374,8 @@ def visitModule (srcSearchPath : SearchPath)
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
else
let k := NeedsKind.ofImport imp
if !addOnly && !deps.has k j && !deps.has { k with isExported := false } j then
-- A private import should also be removed if the public version is needed
if !deps.has k j || !k.isExported && deps.has { k with isExported := true } j then
toRemove := toRemove.push imp
else
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
@@ -385,7 +406,8 @@ def visitModule (srcSearchPath : SearchPath)
if githubStyle then
try
let (path, inputCtx, imports, endHeader) parseHeader srcSearchPath s.modNames[i]!
let (path, inputCtx, stx, endHeader) parseHeader srcSearchPath s.modNames[i]!
let (_, _, imports) := decodeHeader stx
for stx in imports do
if toRemove.any fun imp => imp == decodeImport stx then
let pos := inputCtx.fileMap.toPosition stx.raw.getPos?.get!
@@ -529,33 +551,43 @@ def main (args : List String) : IO UInt32 := do
let needs := s.mods.mapIdx fun i _ =>
Task.spawn fun _ => calcNeeds s.env i
-- Parse headers in parallel
let headers s.mods.mapIdxM fun i _ =>
BaseIO.asTask (parseHeader srcSearchPath s.modNames[i]! |>.toBaseIO)
if args.fix then
println! "The following changes will be made automatically:"
-- Check all selected modules
let mut edits : Edits :=
let mut revNeeds : Needs := default
for i in [0:s.mods.size], t in needs do
edits visitModule (addOnly := !pkg.isPrefixOf s.modNames[i]!) srcSearchPath i t.get revNeeds edits args.githubStyle args.explain
if isExtraRevModUse s.env i then
revNeeds := revNeeds.union .priv {i}
for i in [0:s.mods.size], t in needs, header in headers do
match header.get with
| .ok (_, _, stx, _) =>
edits visitModule (addOnly := !pkg.isPrefixOf s.modNames[i]!)
srcSearchPath i t.get revNeeds edits stx args.githubStyle args.explain
if isExtraRevModUse s.env i then
revNeeds := revNeeds.union .priv {i}
| .error e =>
println! e.toString
if !args.fix then
-- return error if any issues were found
return if edits.isEmpty then 0 else 1
-- Apply the edits to existing files
let count edits.foldM (init := 0) fun count mod (remove, add) => do
let mut count := 0
for mod in s.modNames, header? in headers do
let some (remove, add) := edits[mod]? | continue
let add : Array Import := add.qsortOrd
-- Parse the input file
let (path, inputCtx, imports, insertion)
try parseHeader srcSearchPath mod
catch e => println! e.toString; return count
let .ok (path, inputCtx, stx, insertion) := header?.get | continue
let (_, _, imports) := decodeHeader stx
let text := inputCtx.fileMap.source
-- Calculate the edit result
let mut pos : String.Pos := 0
let mut pos : String.Pos.Raw := 0
let mut out : String := ""
let mut seen : Std.HashSet Import := {}
for stx in imports do
@@ -563,17 +595,17 @@ def main (args : List String) : IO UInt32 := do
if remove.contains mod || seen.contains mod then
out := out ++ text.extract pos stx.raw.getPos?.get!
-- We use the end position of the syntax, but include whitespace up to the first newline
pos := text.findAux (· == '\n') text.endPos stx.raw.getTailPos?.get! + 1
pos := text.findAux (· == '\n') text.rawEndPos stx.raw.getTailPos?.get! + '\n'
seen := seen.insert mod
out := out ++ text.extract pos insertion
for mod in add do
if !seen.contains mod then
seen := seen.insert mod
out := out ++ s!"{mod}\n"
out := out ++ text.extract insertion text.endPos
out := out ++ text.extract insertion text.rawEndPos
IO.FS.writeFile path out
return count + 1
count := count + 1
-- Since we throw an error upon encountering issues, we can be sure that everything worked
-- if we reach this point of the script.

View File

@@ -60,7 +60,7 @@ if (arity == fixed + {n}) \{
for j in [n:max + 1] do
let fs := mkFsArgs (j - n)
let sep := if j = n then "" else ", "
emit s!" case {j}: \{ obj* r = FN{j}(f)({fs}{sep}{args}); lean_free_small_object(f); return r; }\n"
emit s!" case {j}: \{ obj* r = FN{j}(f)({fs}{sep}{args}); lean_free_object(f); return r; }\n"
emit " }
}
switch (arity) {\n"
@@ -162,7 +162,7 @@ static obj* fix_args(obj* f, unsigned n, obj*const* as) {
for (unsigned i = 0; i < fixed; i++, source++, target++) {
*target = *source;
}
lean_free_small_object(f);
lean_free_object(f);
}
for (unsigned i = 0; i < n; i++, as++, target++) {
*target = *as;

View File

@@ -13,8 +13,15 @@ What this script does:
- Checks that the release branch (releases/vX.Y.0) exists
- Verifies CMake version settings are correct
- Confirms the release tag exists
- Validates the release page exists on GitHub
- Checks the release notes page on lean-lang.org
- Validates the release page exists on GitHub (created automatically by CI after tag push)
- Checks the release notes page on lean-lang.org (updated while bumping the `reference-manual` repository)
**IMPORTANT: If the release page doesn't exist, the script will skip checking
downstream repositories and the master branch configuration. The preliminary
infrastructure must be in place before the release process can proceed.**
**NOTE: The GitHub release page is created AUTOMATICALLY by CI after the tag is pushed.
DO NOT create it manually. Wait for CI to complete after pushing the tag.**
2. For each downstream repository (batteries, mathlib4, etc.):
- Checks if dependencies are ready (e.g., mathlib4 depends on batteries)
@@ -122,6 +129,39 @@ def release_page_exists(repo_url, tag_name, github_token):
response = requests.get(api_url, headers=headers)
return response.status_code == 200
def get_tag_workflow_status(repo_url, tag_name, github_token):
"""Get the status of CI workflows running for a specific tag."""
api_base = repo_url.replace("https://github.com/", "https://api.github.com/repos/")
headers = {'Authorization': f'token {github_token}'} if github_token else {}
# Get workflow runs for the tag
# GitHub's workflow runs API uses the branch/tag name in the 'head_branch' field
api_url = f"{api_base}/actions/runs?event=push&head_branch={tag_name}"
response = requests.get(api_url, headers=headers)
if response.status_code != 200:
return None
data = response.json()
workflow_runs = data.get('workflow_runs', [])
if not workflow_runs:
return None
# Get the most recent workflow run for this tag
run = workflow_runs[0]
status = run.get('status')
conclusion = run.get('conclusion')
workflow_name = run.get('name', 'CI')
run_id = run.get('id')
return {
'status': status,
'conclusion': conclusion,
'workflow_name': workflow_name,
'run_id': run_id
}
def get_release_notes(tag_name):
"""Fetch release notes page title from lean-lang.org."""
# Strip -rcX suffix if present for the URL
@@ -130,20 +170,17 @@ def get_release_notes(tag_name):
try:
response = requests.get(reference_url)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
# Extract title using regex
match = re.search(r"<title>(.*?)</title>", response.text, re.IGNORECASE | re.DOTALL)
if match:
return match.group(1).strip()
else:
print(f" ⚠️ Could not find <title> tag in {reference_url}")
return None
except requests.exceptions.RequestException as e:
print(f" ❌ Error fetching release notes from {reference_url}: {e}")
except requests.exceptions.RequestException:
return None
except Exception as e:
print(f" ❌ An unexpected error occurred while processing release notes: {e}")
except Exception:
return None
def get_branch_content(repo_url, branch, file_path, github_token):
@@ -512,30 +549,57 @@ def main():
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")
release_page_ready = release_page_exists(lean_repo_url, toolchain, github_token)
if not release_page_ready:
print(f" ❌ Release page for {toolchain} does not exist (This will be created by CI.)")
# Check CI workflow status
workflow_status = get_tag_workflow_status(lean_repo_url, toolchain, github_token)
if workflow_status:
status = workflow_status['status']
conclusion = workflow_status['conclusion']
workflow_name = workflow_status['workflow_name']
run_id = workflow_status['run_id']
workflow_url = f"{lean_repo_url}/actions/runs/{run_id}"
if status == 'in_progress' or status == 'queued':
print(f" 🔄 {workflow_name} workflow is {status}: {workflow_url}")
elif status == 'completed':
if conclusion == 'success':
print(f"{workflow_name} workflow completed successfully: {workflow_url}")
elif conclusion == 'failure':
print(f"{workflow_name} workflow failed: {workflow_url}")
else:
print(f" ⚠️ {workflow_name} workflow completed with status: {conclusion}: {workflow_url}")
else:
print(f" {workflow_name} workflow status: {status}: {workflow_url}")
lean4_success = False
else:
print(f" ✅ Release page for {toolchain} exists")
# Check the actual release notes page title
# Check the actual release notes page title (informational only - does not block)
actual_title = get_release_notes(toolchain)
expected_title_prefix = f"Lean {toolchain.lstrip('v')}" # e.g., "Lean 4.19.0" or "Lean 4.19.0-rc1"
base_tag = toolchain.split('-')[0]
release_notes_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
if actual_title is None:
# Error already printed by get_release_notes
lean4_success = False
print(f" ⚠️ Release notes not found at {release_notes_url} (this will be fixed while updating the reference-manual repository)")
elif not actual_title.startswith(expected_title_prefix):
# Construct URL for the error message (using the base tag)
base_tag = toolchain.split('-')[0]
check_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
print(f" ❌ Release notes page title mismatch. Expected prefix '{expected_title_prefix}', got '{actual_title}'. Check {check_url}")
lean4_success = False
print(f" ⚠️ Release notes page title mismatch. Expected prefix '{expected_title_prefix}', got '{actual_title}'. Check {release_notes_url}")
else:
print(f" ✅ Release notes page title looks good ('{actual_title}').")
repo_status["lean4"] = lean4_success
# If the release page doesn't exist, skip repository checks and master branch checks
# The preliminary infrastructure must be in place first
if not release_page_exists(lean_repo_url, toolchain, github_token):
print("\n⚠️ Release process blocked: preliminary Lean4 infrastructure incomplete.")
print(" Complete the steps above, then rerun this script to proceed with downstream repositories.")
return
# Load repositories and perform further checks
print("\nChecking repositories...")

View File

@@ -589,8 +589,19 @@ def execute_release_steps(repo, version, config):
# Clean lake cache for a fresh build
print(blue("Cleaning lake cache..."))
run_command("rm -rf .lake", cwd=repo_path)
run_command("lake clean", cwd=repo_path)
# Check if downstream of Mathlib and get cache if so
mathlib_package_dir = repo_path / ".lake" / "packages" / "mathlib"
if mathlib_package_dir.exists():
print(blue("Project is downstream of Mathlib, fetching cache..."))
try:
run_command("lake exe cache get", cwd=repo_path, stream_output=True)
print(green("Cache fetched successfully"))
except subprocess.CalledProcessError as e:
print(yellow("Failed to fetch cache, continuing anyway..."))
print(yellow(f"Cache fetch error: {e}"))
try:
run_command("lake build", cwd=repo_path, stream_output=True)
print(green("Build completed successfully"))

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 25)
set(LEAN_VERSION_MINOR 27)
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'")

View File

@@ -14,7 +14,6 @@ public import Init.ByCases
public import Init.RCases
public import Init.Core
public import Init.Control
public import Init.Data.Basic
public import Init.WF
public import Init.WFTactics
public import Init.Data

View File

@@ -44,3 +44,10 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
@[simp] theorem dite_eq_ite [Decidable P] :
(dite P (fun _ => a) (fun _ => b)) = ite P a b := rfl
-- Remark: dite and ite are "defally equal" when we ignore the proofs.
@[deprecated dite_eq_ite (since := "2025-10-29")]
theorem dif_eq_if (c : Prop) {h : Decidable c} {α : Sort u} (t : α) (e : α) : dite c (fun _ => t) (fun _ => e) = ite c t e :=
match h with
| isTrue _ => rfl
| isFalse _ => rfl

View File

@@ -181,9 +181,6 @@ theorem not_imp_iff_and_not : ¬(a → b) ↔ a ∧ ¬b := Decidable.not_imp_iff
theorem not_and_iff_not_or_not : ¬(a b) ¬a ¬b := Decidable.not_and_iff_not_or_not
@[deprecated not_and_iff_not_or_not (since := "2025-03-18")]
abbrev not_and_iff_or_not_not := @not_and_iff_not_or_not
theorem not_iff : ¬(a b) (¬a b) := Decidable.not_iff
@[simp] theorem imp_iff_left_iff : (b a b) a b := Decidable.imp_iff_left_iff

View File

@@ -45,9 +45,6 @@ instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α
forIn x b f = forIn' x b (fun x h => binderNameHint x f <| binderNameHint h () <| f x) := by
rfl
@[deprecated forIn_eq_forIn' (since := "2025-04-04")]
abbrev forIn_eq_forin' := @forIn_eq_forIn'
/--
Extracts the value from a `ForInStep`, ignoring whether it is `ForInStep.done` or `ForInStep.yield`.
-/

View File

@@ -148,6 +148,23 @@ This is the inverse of `ExceptT.mk`.
@[always_inline, inline, expose]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
/--
Use a monadic action that may throw an exception by providing explicit success and failure
continuations.
-/
@[always_inline, inline, expose]
def ExceptT.runK [Monad m] (x : ExceptT ε m α) (ok : α m β) (error : ε m β) : m β :=
x.run >>= (·.casesOn error ok)
/--
Returns the value of a computation, forgetting whether it was an exception or a success.
This corresponds to early return.
-/
@[always_inline, inline, expose]
def ExceptT.runCatch [Monad m] (x : ExceptT α m α) : m α :=
x.runK pure pure
namespace ExceptT
variable {ε : Type u} {m : Type u Type v} [Monad m]

View File

@@ -170,6 +170,7 @@ theorem bind_pure_unit [Monad m] [LawfulMonad m] {x : m PUnit} : (x >>= fun _ =>
theorem map_congr [Functor m] {x : m α} {f g : α β} (h : a, f a = g a) : (f <$> x : m β) = g <$> x := by
simp [funext h]
@[deprecated seq_eq_bind_map (since := "2025-10-26")]
theorem seq_eq_bind {α β : Type u} [Monad m] [LawfulMonad m] (mf : m (α β)) (x : m α) : mf <*> x = mf >>= fun f => f <$> x := by
rw [bind_map]
@@ -255,20 +256,4 @@ instance : LawfulMonad Id := by
@[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 -/
instance : LawfulMonad Option := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun _ _ => rfl)
(bind_assoc := fun x _ _ => by cases x <;> rfl)
(bind_pure_comp := fun _ x => by cases x <;> rfl)
instance : LawfulApplicative Option := inferInstance
instance : LawfulFunctor Option := inferInstance

View File

@@ -189,12 +189,12 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (OptionT m) where
@[simp] theorem run_seq [Monad m] [LawfulMonad m] (f : OptionT m (α β)) (x : OptionT m α) :
(f <*> x).run = Option.elimM f.run (pure none) (fun f => Option.map f <$> x.run) := by
simp [seq_eq_bind, Option.elimM, Option.elim]
simp [seq_eq_bind_map, Option.elimM, Option.elim]
@[simp] theorem run_seqLeft [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) :
(x <* y).run = Option.elimM x.run (pure none)
(fun x => Option.map (Function.const β x) <$> y.run) := by
simp [seqLeft_eq, seq_eq_bind, Option.elimM, OptionT.run_bind]
simp [seqLeft_eq, seq_eq_bind_map, Option.elimM, OptionT.run_bind]
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) :
(x *> y).run = Option.elimM x.run (pure none) (Function.const α y.run) := by
@@ -219,7 +219,7 @@ instance : LawfulMonad Option := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun _ _ => by rfl)
(bind_assoc := fun a _ _ => by cases a <;> rfl)
(bind_pure_comp := bind_pure_comp)
(bind_pure_comp := fun _ x => by cases x <;> rfl)
instance : LawfulApplicative Option := inferInstance
instance : LawfulFunctor Option := inferInstance

View File

@@ -23,7 +23,7 @@ theorem monadLift_map [LawfulMonad m] [LawfulMonad n] (f : α → β) (ma : m α
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]
simp only [seq_eq_bind_map, 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

View File

@@ -27,7 +27,7 @@ failure occurred.
/--
Executes an action that might fail in the underlying monad `m`, returning `none` in case of failure.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def OptionT.run {m : Type u Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
x
@@ -69,7 +69,7 @@ instance {m : Type u → Type v} [Pure m] : Inhabited (OptionT m α) where
/--
Recovers from failures. Typically used via the `<|>` operator.
-/
@[always_inline, inline] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
@[always_inline, inline, expose] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
match ( x) with
| some a => pure (some a)
| _ => y ()
@@ -77,7 +77,7 @@ Recovers from failures. Typically used via the `<|>` operator.
/--
A recoverable failure.
-/
@[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do
@[always_inline, inline, expose] protected def fail : OptionT m α := OptionT.mk do
pure none
instance : Alternative (OptionT m) where
@@ -90,7 +90,7 @@ Converts a computation from the underlying monad into one that could fail, even
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
@[always_inline, inline, expose] protected def lift (x : m α) : OptionT m α := OptionT.mk do
return some ( x)
instance : MonadLift m (OptionT m) := OptionT.lift
@@ -100,11 +100,11 @@ instance : MonadFunctor m (OptionT m) := ⟨fun f x => f x⟩
/--
Handles failures by treating them as exceptions of type `Unit`.
-/
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle ()
@[always_inline, inline, expose] protected def tryCatch (x : OptionT m α) (handle : PUnit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle
pure <| some a
instance : MonadExceptOf Unit (OptionT m) where
instance : MonadExceptOf PUnit (OptionT m) where
throw := fun _ => OptionT.fail
tryCatch := OptionT.tryCatch

View File

@@ -600,17 +600,6 @@ export LawfulSingleton (insert_empty_eq)
attribute [simp] insert_empty_eq
@[deprecated insert_empty_eq (since := "2025-03-12")]
theorem insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
[LawfulSingleton α β] (x : α) : (insert x : β) = singleton x :=
insert_empty_eq _
@[deprecated insert_empty_eq (since := "2025-03-12")]
theorem LawfulSingleton.insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
[LawfulSingleton α β] (x : α) : (insert x : β) = singleton x :=
insert_empty_eq _
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
class Sep (α : outParam <| Type u) (γ : Type v) where
/-- Computes `{ a ∈ c | p a }`. -/
@@ -1095,14 +1084,6 @@ theorem of_toBoolUsing_eq_true {p : Prop} {d : Decidable p} (h : toBoolUsing d =
theorem of_toBoolUsing_eq_false {p : Prop} {d : Decidable p} (h : toBoolUsing d = false) : ¬p :=
of_decide_eq_false h
set_option linter.missingDocs false in
@[deprecated of_toBoolUsing_eq_true (since := "2025-04-04")]
abbrev ofBoolUsing_eq_true := @of_toBoolUsing_eq_true
set_option linter.missingDocs false in
@[deprecated of_toBoolUsing_eq_false (since := "2025-04-04")]
abbrev ofBoolUsing_eq_false := @of_toBoolUsing_eq_false
instance : Decidable True :=
isTrue trivial
@@ -1165,6 +1146,7 @@ end
else isFalse (fun h => absurd (h hp) hq)
else isTrue (fun h => absurd h hp)
@[inline]
instance {p q} [Decidable p] [Decidable q] : Decidable (p q) :=
if hp : p then
if hq : q then
@@ -1206,17 +1188,13 @@ theorem dif_neg {c : Prop} {h : Decidable c} (hnc : ¬c) {α : Sort u} {t : c
| isTrue hc => absurd hc hnc
| isFalse _ => rfl
-- Remark: dite and ite are "defally equal" when we ignore the proofs.
theorem dif_eq_if (c : Prop) {h : Decidable c} {α : Sort u} (t : α) (e : α) : dite c (fun _ => t) (fun _ => e) = ite c t e :=
match h with
| isTrue _ => rfl
| isFalse _ => rfl
@[macro_inline]
instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) :=
match dC with
| isTrue _ => dT
| isFalse _ => dE
@[inline]
instance {c : Prop} {t : c Prop} {e : ¬c Prop} [dC : Decidable c] [dT : h, Decidable (t h)] [dE : h, Decidable (e h)] : Decidable (if h : c then t h else e h) :=
match dC with
| isTrue hc => dT hc
@@ -1367,12 +1345,12 @@ namespace Subtype
theorem exists_of_subtype {α : Type u} {p : α Prop} : { x // p x } Exists (fun x => p x)
| a, h => a, h
set_option linter.missingDocs false in
@[deprecated exists_of_subtype (since := "2025-04-04")]
abbrev existsOfSubtype := @exists_of_subtype
variable {α : Sort u} {p : α Prop}
variable {α : Type u} {p : α Prop}
protected theorem ext : {a1 a2 : {x // p x}}, val a1 = val a2 a1 = a2
| _, _, _, _, rfl => rfl
@[deprecated Subtype.ext (since := "2025-10-26")]
protected theorem eq : {a1 a2 : {x // p x}}, val a1 = val a2 a1 = a2
| _, _, _, _, rfl => rfl
@@ -1387,9 +1365,9 @@ instance {α : Type u} {p : α → Prop} [BEq α] [ReflBEq α] : ReflBEq {x : α
rfl {x} := BEq.refl x.1
instance {α : Type u} {p : α Prop} [BEq α] [LawfulBEq α] : LawfulBEq {x : α // p x} where
eq_of_beq h := Subtype.eq (eq_of_beq h)
eq_of_beq h := Subtype.ext (eq_of_beq h)
instance {α : Type u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
instance {α : Sort u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
fun a, h₁ b, h₂ =>
if h : a = b then isTrue (by subst h; exact rfl)
else isFalse (fun h' => Subtype.noConfusion h' (fun h' => absurd h' h))
@@ -1490,6 +1468,8 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
@[simp] theorem Prod.map_apply (f : α β) (g : γ δ) (x) (y) :
Prod.map f g (x, y) = (f x, g y) := rfl
-- We add `@[grind =]` to these in `Init.Data.Prod`.
@[simp] theorem Prod.map_fst (f : α β) (g : γ δ) (x) : (Prod.map f g x).1 = f x.1 := rfl
@[simp] theorem Prod.map_snd (f : α β) (g : γ δ) (x) : (Prod.map f g x).2 = g x.2 := rfl
@@ -1506,20 +1486,24 @@ protected theorem PSigma.eta {α : Sort u} {β : α → Sort v} {a₁ a₂ : α}
/-! # Universe polymorphic unit -/
theorem PUnit.ext (a b : PUnit) : a = b := by
cases a; cases b; exact rfl
@[deprecated PUnit.ext (since := "2025-10-26")]
theorem PUnit.subsingleton (a b : PUnit) : a = b := by
cases a; cases b; exact rfl
theorem PUnit.eq_punit (a : PUnit) : a = :=
PUnit.subsingleton a
PUnit.ext a
instance : Subsingleton PUnit :=
Subsingleton.intro PUnit.subsingleton
Subsingleton.intro PUnit.ext
instance : Inhabited PUnit where
default :=
instance : DecidableEq PUnit :=
fun a b => isTrue (PUnit.subsingleton a b)
fun a b => isTrue (PUnit.ext a b)
/-! # Setoid -/
@@ -1606,7 +1590,7 @@ gen_injective_theorems% PSum
gen_injective_theorems% Sigma
gen_injective_theorems% String
gen_injective_theorems% String.Pos.Raw
gen_injective_theorems% Substring
gen_injective_theorems% Substring.Raw
gen_injective_theorems% Subtype
gen_injective_theorems% Sum
gen_injective_theorems% Task
@@ -2523,8 +2507,7 @@ class Antisymm (r : αα → Prop) : Prop where
/-- An antisymmetric relation `r` satisfies `r a b → r b a → a = b`. -/
antisymm (a b : α) : r a b r b a a = b
/-- `Asymm r` means that the binary relation `r` is asymmetric, that is,
`r a b → ¬ r b a`. -/
/-- `Asymm r` means that the binary relation `r` is asymmetric, that is, `r a b → ¬ r b a`. -/
class Asymm (r : α α Prop) : Prop where
/-- An asymmetric relation satisfies `r a b → ¬ r b a`. -/
asymm : a b, r a b ¬r b a
@@ -2534,16 +2517,19 @@ class Symm (r : αα → Prop) : Prop where
/-- A symmetric relation satisfies `r a b → r b a`. -/
symm : a b, r a b r b a
/-- `Total X r` means that the binary relation `r` on `X` is total, that is, that for any
`x y : X` we have `r x y` or `r y x`. -/
/-- `Total X r` means that the binary relation `r` on `X` is total, that is, `r a b` or `r b a`. -/
class Total (r : α α Prop) : Prop where
/-- A total relation satisfies `r a b r b a`. -/
/-- A total relation satisfies `r a b` or `r b a`. -/
total : a b, r a b r b a
/-- `Irrefl r` means the binary relation `r` is irreflexive, that is, `r x x` never
holds. -/
/-- `Irrefl r` means the binary relation `r` is irreflexive, that is, `r x x` never holds. -/
class Irrefl (r : α α Prop) : Prop where
/-- An irreflexive relation satisfies `¬ r a a`. -/
irrefl : a, ¬r a a
/-- `Trichotomous r` says that `r` is trichotomous, that is, `¬ r a b → ¬ r b a → a = b`. -/
class Trichotomous (r : α α Prop) : Prop where
/-- An trichotomous relation `r` satisfies `¬ r a b → ¬ r b a → a = b`. -/
trichotomous (a b : α) : ¬ r a b ¬ r b a a = b
end Std

View File

@@ -6,7 +6,6 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Basic
public import Init.Data.Nat
public import Init.Data.Bool
public import Init.Data.BitVec

View File

@@ -749,9 +749,6 @@ and simplifies these to the function directly taking the value.
(Array.replicate n x).unattach = Array.replicate n x.1 := by
simp [unattach]
@[deprecated unattach_replicate (since := "2025-03-18")]
abbrev unattach_mkArray := @unattach_replicate
/-! ### Well-founded recursion preprocessing setup -/
@[wf_preprocess] theorem map_wfParam {xs : Array α} {f : α β} :

View File

@@ -209,20 +209,6 @@ Examples:
def replicate {α : Type u} (n : Nat) (v : α) : Array α where
toList := List.replicate n v
/--
Creates an array that contains `n` repetitions of `v`.
The corresponding `List` function is `List.replicate`.
Examples:
* `Array.mkArray 2 true = #[true, true]`
* `Array.mkArray 3 () = #[(), (), ()]`
* `Array.mkArray 0 "anything" = #[]`
-/
@[extern "lean_mk_array", deprecated replicate (since := "2025-03-18")]
def mkArray {α : Type u} (n : Nat) (v : α) : Array α where
toList := List.replicate n v
/--
Swaps two elements of an array. The modification is performed in-place when the reference to the
array is unique.
@@ -240,7 +226,7 @@ def swap (xs : Array α) (i j : @& Nat) (hi : i < xs.size := by get_elem_tactic)
let xs' := xs.set i v₂
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
@[simp, grind =] theorem size_swap {xs : Array α} {i j : Nat} {hi hj} : (xs.swap i j hi hj).size = xs.size := by
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]
@@ -462,7 +448,7 @@ Examples:
-/
abbrev take (xs : Array α) (i : Nat) : Array α := extract xs 0 i
@[simp] theorem take_eq_extract {xs : Array α} {i : Nat} : xs.take i = xs.extract 0 i := rfl
@[simp, grind =] theorem take_eq_extract {xs : Array α} {i : Nat} : xs.take i = xs.extract 0 i := rfl
/--
Removes the first `i` elements of `xs`. If `xs` has fewer than `i` elements, the new array is empty.
@@ -476,7 +462,7 @@ Examples:
-/
abbrev drop (xs : Array α) (i : Nat) : Array α := extract xs i xs.size
@[simp] theorem drop_eq_extract {xs : Array α} {i : Nat} : xs.drop i = xs.extract i xs.size := rfl
@[simp, grind =] theorem drop_eq_extract {xs : Array α} {i : Nat} : xs.drop i = xs.extract i xs.size := rfl
@[inline]
unsafe def modifyMUnsafe [Monad m] (xs : Array α) (i : Nat) (f : α m α) : m (Array α) := do
@@ -1309,7 +1295,7 @@ decreasing_by simp_wf; decreasing_trivial_pre_omega
/--
Returns the index of the first element equal to `a`, or the size of the array if no element is equal
Returns the index of the first element equal to `a`, or `none` if no element is equal
to `a`. The index is returned as a `Fin`, which guarantees that it is in bounds.
Examples:
@@ -1718,7 +1704,7 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
as
decreasing_by simp_wf; decreasing_trivial_pre_omega
@[simp] theorem popWhile_empty {p : α Bool} :
@[simp, grind =] theorem popWhile_empty {p : α Bool} :
popWhile p #[] = #[] := by
simp [popWhile]
@@ -1765,7 +1751,8 @@ termination_by xs.size - i
decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ h
-- This is required in `Lean.Data.PersistentHashMap`.
@[simp] theorem size_eraseIdx {xs : Array α} (i : Nat) (h) : (xs.eraseIdx i h).size = xs.size - 1 := by
@[simp, grind =]
theorem size_eraseIdx {xs : Array α} (i : Nat) (h) : (xs.eraseIdx i h).size = xs.size - 1 := by
induction xs, i, h using Array.eraseIdx.induct with
| @case1 xs i h h' xs' ih =>
unfold eraseIdx
@@ -2147,5 +2134,3 @@ instance [ToString α] : ToString (Array α) where
toString xs := String.Internal.append "#" (toString xs.toList)
end Array
export Array (mkArray)

View File

@@ -31,7 +31,7 @@ theorem foldlM_toList.aux [Monad m]
· cases Nat.not_le_of_gt _ (Nat.zero_add _ H)
· rename_i i; rw [Nat.succ_add] at H
simp [foldlM_toList.aux (j := j+1) H]
rw (occs := [2]) [ List.getElem_cons_drop_succ_eq_drop _]
rw (occs := [2]) [ List.getElem_cons_drop _]
simp
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; simp
@@ -100,9 +100,15 @@ abbrev push_toList := @toList_push
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, List.nil_append]
@[simp, grind _=_] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
@[simp] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
apply ext'; simp only [toList_append, List.append_assoc]
grind_pattern append_assoc => (xs ++ ys) ++ zs where
xs =/= #[]; ys =/= #[]; zs =/= #[]
grind_pattern append_assoc => xs ++ (ys ++ zs) where
xs =/= #[]; ys =/= #[]; zs =/= #[]
@[simp] theorem appendList_eq_append {xs : Array α} {l : List α} : xs.appendList l = xs ++ l := rfl
@[simp, grind =] theorem toList_appendList {xs : Array α} {l : List α} :
@@ -110,6 +116,4 @@ abbrev push_toList := @toList_push
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing xs <;> simp [*]
end Array

View File

@@ -99,9 +99,6 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
theorem countP_replicate {a : α} {n : Nat} : countP p (replicate n a) = if p a then n else 0 := by
simp [ List.toArray_replicate, List.countP_replicate]
@[deprecated countP_replicate (since := "2025-03-18")]
abbrev countP_mkArray := @countP_replicate
theorem boole_getElem_le_countP {xs : Array α} {i : Nat} (h : i < xs.size) :
(if p xs[i] then 1 else 0) xs.countP p := by
rcases xs with xs
@@ -262,15 +259,9 @@ theorem count_eq_size {xs : Array α} : count a xs = xs.size ↔ ∀ b ∈ xs, a
@[simp] theorem count_replicate_self {a : α} {n : Nat} : count a (replicate n a) = n := by
simp [ List.toArray_replicate]
@[deprecated count_replicate_self (since := "2025-03-18")]
abbrev count_mkArray_self := @count_replicate_self
theorem count_replicate {a b : α} {n : Nat} : count a (replicate n b) = if b == a then n else 0 := by
simp [ List.toArray_replicate, List.count_replicate]
@[deprecated count_replicate (since := "2025-03-18")]
abbrev count_mkArray := @count_replicate
theorem filter_beq {xs : Array α} (a : α) : xs.filter (· == a) = replicate (count a xs) a := by
rcases xs with xs
simp [List.filter_beq]
@@ -284,9 +275,6 @@ theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs
rw [ toList_inj]
simp [List.replicate_count_eq_of_count_eq_length (by simpa using h)]
@[deprecated replicate_count_eq_of_count_eq_size (since := "2025-03-18")]
abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
@[simp] theorem count_filter {xs : Array α} (h : p a) : count a (filter p xs) = count a xs := by
rcases xs with xs
simp [List.count_filter, h]

View File

@@ -139,25 +139,16 @@ theorem eraseP_replicate {n : Nat} {a : α} {p : α → Bool} :
simp only [ List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
split <;> simp
@[deprecated eraseP_replicate (since := "2025-03-18")]
abbrev eraseP_mkArray := @eraseP_replicate
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
(replicate n a).eraseP p = replicate (n - 1) a := by
simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h]
@[deprecated eraseP_replicate_of_pos (since := "2025-03-18")]
abbrev eraseP_mkArray_of_pos := @eraseP_replicate_of_pos
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
(replicate n a).eraseP p = replicate n a := by
simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h]
@[deprecated eraseP_replicate_of_neg (since := "2025-03-18")]
abbrev eraseP_mkArray_of_neg := @eraseP_replicate_of_neg
theorem eraseP_eq_iff {p} {xs : Array α} :
xs.eraseP p = ys
(( a xs, ¬ p a) xs = ys)
@@ -278,9 +269,6 @@ theorem erase_replicate [LawfulBEq α] {n : Nat} {a b : α} :
simp only [List.erase_replicate, beq_iff_eq, List.toArray_replicate]
split <;> simp
@[deprecated erase_replicate (since := "2025-03-18")]
abbrev erase_mkArray := @erase_replicate
-- The arguments `a b` are explicit,
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
@[grind =]
@@ -308,17 +296,11 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
simp only [ List.toArray_replicate, List.erase_toArray]
simp
@[deprecated erase_replicate_self (since := "2025-03-18")]
abbrev erase_mkArray_self := @erase_replicate_self
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(replicate n a).erase b = replicate n a := by
rw [erase_of_not_mem]
simp_all
@[deprecated erase_replicate_ne (since := "2025-03-18")]
abbrev erase_mkArray_ne := @erase_replicate_ne
end erase
/-! ### eraseIdxIfInBounds -/
@@ -429,9 +411,6 @@ theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
simp only [ List.toArray_replicate, List.eraseIdx_toArray]
simp [List.eraseIdx_replicate, h]
@[deprecated eraseIdx_replicate (since := "2025-03-18")]
abbrev eraseIdx_mkArray := @eraseIdx_replicate
theorem mem_eraseIdx_iff_getElem {x : α} {xs : Array α} {k} {h} : x xs.eraseIdx k h i w, i k xs[i]'w = x := by
rcases xs with xs
simp [List.mem_eraseIdx_iff_getElem, *]

View File

@@ -200,7 +200,7 @@ theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
simp [getElem?_extract]
omega
@[simp, grind =] theorem extract_extract {as : Array α} {i j k l : Nat} :
@[simp] theorem extract_extract {as : Array α} {i j k l : Nat} :
(as.extract i j).extract k l = as.extract (i + k) (min (i + l) j) := by
ext m h₁ h₂
· simp
@@ -208,6 +208,9 @@ theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
· simp only [size_extract] at h₁ h₂
simp [Nat.add_assoc]
grind_pattern extract_extract => (as.extract i j).extract k l where
as =/= #[]
theorem extract_eq_empty_of_eq_empty {as : Array α} {i j : Nat} (h : as = #[]) :
as.extract i j = #[] := by
simp [h]
@@ -289,9 +292,6 @@ theorem extract_append_right {as bs : Array α} :
· simp only [size_extract, size_replicate] at h₁ h₂
simp only [getElem_extract, getElem_replicate]
@[deprecated extract_replicate (since := "2025-03-18")]
abbrev extract_mkArray := @extract_replicate
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} :
as.extract i j = as.extract i j' min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
rcases as with as
@@ -429,32 +429,20 @@ theorem popWhile_append {xs ys : Array α} :
(replicate n a).takeWhile p = (replicate n a).filter p := by
simp [ List.toArray_replicate]
@[deprecated takeWhile_replicate_eq_filter (since := "2025-03-18")]
abbrev takeWhile_mkArray_eq_filter := @takeWhile_replicate_eq_filter
theorem takeWhile_replicate {p : α Bool} :
(replicate n a).takeWhile p = if p a then replicate n a else #[] := by
simp [takeWhile_replicate_eq_filter, filter_replicate]
@[deprecated takeWhile_replicate (since := "2025-03-18")]
abbrev takeWhile_mkArray := @takeWhile_replicate
@[simp] theorem popWhile_replicate_eq_filter_not {p : α Bool} :
(replicate n a).popWhile p = (replicate n a).filter (fun a => !p a) := by
simp [ List.toArray_replicate, List.filter_reverse]
@[deprecated popWhile_replicate_eq_filter_not (since := "2025-03-18")]
abbrev popWhile_mkArray_eq_filter_not := @popWhile_replicate_eq_filter_not
theorem popWhile_replicate {p : α Bool} :
(replicate n a).popWhile p = if p a then #[] else replicate n a := by
simp only [popWhile_replicate_eq_filter_not, size_replicate, filter_replicate, Bool.not_eq_eq_eq_not,
Bool.not_true]
split <;> simp_all
@[deprecated popWhile_replicate (since := "2025-03-18")]
abbrev popWhile_mkArray := @popWhile_replicate
theorem extract_takeWhile {as : Array α} {i : Nat} :
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
rcases as with as

View File

@@ -129,31 +129,19 @@ theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [ List.toArray_replicate, List.findSome?_replicate]
@[deprecated findSome?_replicate (since := "2025-03-18")]
abbrev findSome?_mkArray := @findSome?_replicate
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
simp [findSome?_replicate, Nat.ne_of_gt h]
@[deprecated findSome?_replicate_of_pos (since := "2025-03-18")]
abbrev findSome?_mkArray_of_pos := @findSome?_replicate_of_pos
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) :
findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate]
@[deprecated findSome?_replicate_of_isSome (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isSome := @findSome?_replicate_of_isSome
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) :
findSome? f (replicate n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_replicate, h]
@[deprecated findSome?_replicate_of_isNone (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@[simp, grind =] theorem find?_empty : find? p #[] = none := rfl
@@ -318,16 +306,10 @@ theorem find?_replicate :
find? p (replicate n a) = if p a then some a else none := by
simp [find?_replicate, Nat.ne_of_gt h]
@[deprecated find?_replicate_of_size_pos (since := "2025-03-18")]
abbrev find?_mkArray_of_length_pos := @find?_replicate_of_size_pos
@[simp] theorem find?_replicate_of_pos (h : p a) :
find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[deprecated find?_replicate_of_pos (since := "2025-03-18")]
abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
@@ -583,9 +565,6 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
simp only [List.findIdx?_toArray]
simp
@[deprecated findIdx?_replicate (since := "2025-03-18")]
abbrev findIdx?_mkArray := @findIdx?_replicate
theorem findIdx?_eq_findSome?_zipIdx {xs : Array α} {p : α Bool} :
xs.findIdx? p = xs.zipIdx.findSome? fun a, i => if p a then some i else none := by
rcases xs with xs

View File

@@ -50,6 +50,6 @@ where
getLit_eq (xs : Array α) (i : Nat) (h₁ : xs.size = n) (h₂ : i < n) : xs.getLit i h₁ h₂ = getElem xs.toList i ((id (α := xs.toList.length = n) h₁) h₂) :=
rfl
go (i : Nat) (hi : i xs.size) : toListLitAux xs n hsz i hi (xs.toList.drop i) = xs.toList := by
induction i <;> simp only [List.drop, toListLitAux, getLit_eq, List.getElem_cons_drop_succ_eq_drop, *]
induction i <;> simp only [List.drop, toListLitAux, getLit_eq, List.getElem_cons_drop, *]
end Array

View File

@@ -245,12 +245,13 @@ theorem back_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.pus
replace h := List.append_inj_right' h (by simp)
simpa using h
theorem pop_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : xs = ys := by
theorem push_eq_push {a b : α} {xs ys : Array α} : xs.push a = ys.push b a = b xs = ys := by
cases xs
cases ys
simp at h
replace h := List.append_inj_left' h (by simp)
simp [h]
simp [And.comm]
theorem pop_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : xs = ys :=
(push_eq_push.1 h).2
theorem push_inj_left {a : α} {xs ys : Array α} : xs.push a = ys.push a xs = ys :=
pop_eq_of_push_eq, fun h => by simp [h]
@@ -258,15 +259,6 @@ theorem push_inj_left {a : α} {xs ys : Array α} : xs.push a = ys.push a ↔ xs
theorem push_inj_right {a b : α} {xs : Array α} : xs.push a = xs.push b a = b :=
back_eq_of_push_eq, fun h => by simp [h]
theorem push_eq_push {a b : α} {xs ys : Array α} : xs.push a = ys.push b a = b xs = ys := by
constructor
· intro h
exact back_eq_of_push_eq h, pop_eq_of_push_eq h
· rintro rfl, rfl
rfl
theorem push_eq_append_singleton {as : Array α} {x : α} : as.push x = as ++ #[x] := rfl
theorem exists_push_of_ne_empty {xs : Array α} (h : xs #[]) :
(ys : Array α) (a : α), xs = ys.push a := by
rcases xs with xs
@@ -317,41 +309,23 @@ theorem singleton_inj : #[a] = #[b] ↔ a = b := by
@[simp, grind =] theorem size_replicate {n : Nat} {v : α} : (replicate n v).size = n :=
List.length_replicate ..
@[deprecated size_replicate (since := "2025-03-18")]
abbrev size_mkArray := @size_replicate
@[simp] theorem toList_replicate : (replicate n a).toList = List.replicate n a := by
simp only [replicate]
@[deprecated toList_replicate (since := "2025-03-18")]
abbrev toList_mkArray := @toList_replicate
@[simp, grind =] theorem replicate_zero : replicate 0 a = #[] := rfl
@[deprecated replicate_zero (since := "2025-03-18")]
abbrev mkArray_zero := @replicate_zero
@[grind =]
theorem replicate_succ : replicate (n + 1) a = (replicate n a).push a := by
apply toList_inj.1
simp [List.replicate_succ']
@[deprecated replicate_succ (since := "2025-03-18")]
abbrev mkArray_succ := @replicate_succ
@[simp, grind =] theorem getElem_replicate {n : Nat} {v : α} {i : Nat} (h : i < (replicate n v).size) :
(replicate n v)[i] = v := by simp [ getElem_toList]
@[deprecated getElem_replicate (since := "2025-03-18")]
abbrev getElem_mkArray := @getElem_replicate
@[grind =] theorem getElem?_replicate {n : Nat} {v : α} {i : Nat} :
(replicate n v)[i]? = if i < n then some v else none := by
simp [getElem?_def]
@[deprecated getElem?_replicate (since := "2025-03-18")]
abbrev getElem?_mkArray := @getElem?_replicate
/-! ### mem -/
@[grind ]
@@ -835,6 +809,11 @@ theorem contains_eq_true_of_mem [BEq α] [ReflBEq α] {a : α} {as : Array α} (
theorem elem_iff [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
elem a xs = true a xs := mem_of_contains_eq_true, contains_eq_true_of_mem
@[grind =]
theorem contains_iff_mem [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
xs.contains a = true a xs := mem_of_contains_eq_true, contains_eq_true_of_mem
@[deprecated contains_iff_mem (since := "2025-10-26")]
theorem contains_iff [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
xs.contains a = true a xs := mem_of_contains_eq_true, contains_eq_true_of_mem
@@ -1074,12 +1053,6 @@ theorem mem_or_eq_of_mem_setIfInBounds
cases xs
simp
@[deprecated beq_empty_eq (since := "2025-04-04")]
abbrev beq_empty_iff := @beq_empty_eq
@[deprecated empty_beq_eq (since := "2025-04-04")]
abbrev empty_beq_iff := @empty_beq_eq
@[simp, grind =] theorem push_beq_push [BEq α] {a b : α} {xs ys : Array α} :
(xs.push a == ys.push b) = (xs == ys && a == b) := by
cases xs
@@ -1100,9 +1073,6 @@ theorem size_eq_of_beq [BEq α] {xs ys : Array α} (h : xs == ys) : xs.size = ys
rw [Bool.eq_iff_iff]
simp +contextual
@[deprecated replicate_beq_replicate (since := "2025-03-18")]
abbrev mkArray_beq_mkArray := @replicate_beq_replicate
private theorem beq_of_beq_singleton [BEq α] {a b : α} : #[a] == #[b] a == b := by
intro h
have : isEqv #[a] #[b] BEq.beq = true := h
@@ -1166,7 +1136,7 @@ where
aux (i bs) :
mapM.map f xs i bs = (xs.toList.drop i).foldlM (fun bs a => bs.push <$> f a) bs := by
unfold mapM.map; split
· rw [ List.getElem_cons_drop_succ_eq_drop _]
· rw [ List.getElem_cons_drop _]
simp only [aux (i + 1), map_eq_pure_bind, List.foldlM_cons, bind_assoc,
pure_bind]
rfl
@@ -1658,12 +1628,15 @@ theorem filterMap_eq_filter {p : α → Bool} (w : stop = as.size) :
cases as
simp
@[grind =]
theorem filterMap_filterMap {f : α Option β} {g : β Option γ} {xs : Array α} :
filterMap g (filterMap f xs) = filterMap (fun x => (f x).bind g) xs := by
cases xs
simp [List.filterMap_filterMap]
grind_pattern filterMap_filterMap => filterMap g (filterMap f xs) where
f =/= some
g =/= some
@[grind =]
theorem map_filterMap {f : α Option β} {g : β γ} {xs : Array α} :
map g (filterMap f xs) = filterMap (fun x => (f x).map g) xs := by
@@ -1718,9 +1691,6 @@ theorem forall_none_of_filterMap_eq_empty (h : filterMap f xs = #[]) : ∀ x ∈
cases xs
simp
@[deprecated filterMap_eq_empty_iff (since := "2025-04-04")]
abbrev filterMap_eq_nil_iff := @filterMap_eq_empty_iff
theorem filterMap_eq_push_iff {f : α Option β} {xs : Array α} {ys : Array β} {b : β} :
filterMap f xs = ys.push b as a bs,
xs = as.push a ++ bs filterMap f as = ys f a = some b ( x, x bs f x = none) := by
@@ -1883,6 +1853,9 @@ theorem getElem_of_append {xs ys zs : Array α} (eq : xs = ys.push a ++ zs) (h :
theorem push_eq_append {a : α} {as : Array α} : as.push a = as ++ #[a] := rfl
@[deprecated push_eq_append (since := "2025-10-26")]
theorem push_eq_append_singleton {as : Array α} {x : α} : as.push x = as ++ #[x] := rfl
theorem append_inj {xs₁ xs₂ ys₁ ys₂ : Array α} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) :
xs₁ = xs₂ ys₁ = ys₂ := by
rcases xs₁ with s₁
@@ -2083,11 +2056,22 @@ theorem append_eq_map_iff {f : α → β} :
| nil => simp
| cons as => induction as.toList <;> simp [*]
@[simp] theorem flatten_toArray_map {L : List (List α)} :
@[simp] theorem flatten_toArray_map_toArray {L : List (List α)} :
(L.map List.toArray).toArray.flatten = L.flatten.toArray := by
apply ext'
simp [Function.comp_def]
@[deprecated flatten_toArray_map_toArray (since := "2025-10-26")]
theorem flatten_toArray_map {L : List (List α)} :
(L.map List.toArray).toArray.flatten = L.flatten.toArray := by
simp
@[grind =]
theorem flatten_map_toArray_toArray {L : List (List α)} :
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
simp
@[deprecated flatten_map_toArray_toArray (since := "2025-10-26")]
theorem flatten_map_toArray {L : List (List α)} :
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
simp
@@ -2134,32 +2118,33 @@ theorem forall_mem_flatten {p : α → Prop} {xss : Array (Array α)} :
theorem flatten_eq_flatMap {xss : Array (Array α)} : flatten xss = xss.flatMap id := by
induction xss using array₂_induction
rw [flatten_toArray_map, List.flatten_eq_flatMap]
rw [flatten_toArray_map_toArray, List.flatten_eq_flatMap]
simp [List.flatMap_map]
@[simp, grind _=_] theorem map_flatten {f : α β} {xss : Array (Array α)} :
(flatten xss).map f = (map (map f) xss).flatten := by
induction xss using array₂_induction with
| of xss =>
simp only [flatten_toArray_map, List.map_toArray, List.map_flatten, List.map_map,
simp only [flatten_toArray_map_toArray, List.map_toArray, List.map_flatten, List.map_map,
Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
rw [ Function.comp_def, List.map_map, flatten_toArray_map_toArray]
@[simp, grind =] theorem filterMap_flatten {f : α Option β} {xss : Array (Array α)} {stop : Nat} (w : stop = xss.flatten.size) :
filterMap f (flatten xss) 0 stop = flatten (map (filterMap f) xss) := by
subst w
induction xss using array₂_induction
simp only [flatten_toArray_map, List.size_toArray, List.length_flatten, List.filterMap_toArray',
List.filterMap_flatten, List.map_toArray, List.map_map, Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
simp only [flatten_toArray_map_toArray, List.size_toArray, List.length_flatten,
List.filterMap_toArray', List.filterMap_flatten, List.map_toArray, List.map_map,
Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map_toArray]
@[simp, grind =] theorem filter_flatten {p : α Bool} {xss : Array (Array α)} {stop : Nat} (w : stop = xss.flatten.size) :
filter p (flatten xss) 0 stop = flatten (map (filter p) xss) := by
subst w
induction xss using array₂_induction
simp only [flatten_toArray_map, List.size_toArray, List.length_flatten, List.filter_toArray',
List.filter_flatten, List.map_toArray, List.map_map, Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
simp only [flatten_toArray_map_toArray, List.size_toArray, List.length_flatten,
List.filter_toArray', List.filter_flatten, List.map_toArray, List.map_map, Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map_toArray]
theorem flatten_filter_not_isEmpty {xss : Array (Array α)} :
flatten (xss.filter fun xs => !xs.isEmpty) = xss.flatten := by
@@ -2182,23 +2167,23 @@ theorem flatten_filter_ne_empty [DecidablePred fun xs : Array α => xs ≠ #[]]
induction xss using array₂_induction
rcases xs with l
have this : [l.toArray] = [l].map List.toArray := by simp
simp only [List.push_toArray, flatten_toArray_map, List.append_toArray]
rw [this, List.map_append, flatten_toArray_map]
simp only [List.push_toArray, flatten_toArray_map_toArray, List.append_toArray]
rw [this, List.map_append, flatten_toArray_map_toArray]
simp
theorem flatten_flatten {xss : Array (Array (Array α))} : flatten (flatten xss) = flatten (map flatten xss) := by
induction xss using array₃_induction with
| of xss =>
rw [flatten_toArray_map]
rw [flatten_toArray_map_toArray]
have : (xss.map (fun xs => xs.map List.toArray)).flatten = xss.flatten.map List.toArray := by
induction xss with
| nil => simp
| cons xs xss ih =>
simp only [List.map_cons, List.flatten_cons, ih, List.map_append]
rw [this, flatten_toArray_map, List.flatten_flatten, List.map_toArray, Array.map_map,
rw [this, flatten_toArray_map_toArray, List.flatten_flatten, List.map_toArray, Array.map_map,
List.map_toArray, map_map, Function.comp_def]
simp only [Function.comp_apply, flatten_toArray_map]
rw [List.map_toArray, Function.comp_def, List.map_map, flatten_toArray_map]
simp only [Function.comp_apply, flatten_toArray_map_toArray]
rw [List.map_toArray, Function.comp_def, List.map_map, flatten_toArray_map_toArray]
theorem flatten_eq_push_iff {xss : Array (Array α)} {ys : Array α} {y : α} :
xss.flatten = ys.push y
@@ -2207,13 +2192,13 @@ theorem flatten_eq_push_iff {xss : Array (Array α)} {ys : Array α} {y : α} :
induction xss using array₂_induction with
| of xs =>
rcases ys with ys
rw [flatten_toArray_map, List.push_toArray, mk.injEq, List.flatten_eq_append_iff]
rw [flatten_toArray_map_toArray, List.push_toArray, mk.injEq, List.flatten_eq_append_iff]
constructor
· rintro (as, bs, rfl, rfl, h | as, bs, c, cs, ds, rfl, rfl, h)
· rw [List.singleton_eq_flatten_iff] at h
obtain xs, ys, rfl, h₁, h₂ := h
exact ((as ++ xs).map List.toArray).toArray, #[], (ys.map List.toArray).toArray, by simp,
by simpa using h₂, by rw [flatten_toArray_map]; simpa
by simpa using h₂, by rw [flatten_toArray_map_toArray]; simpa
· rw [List.singleton_eq_append_iff] at h
obtain (h₁, h₂ | h₁, h₂) := h
· simp at h₁
@@ -2246,8 +2231,8 @@ theorem push_eq_flatten_iff {xss : Array (Array α)} {ys : Array α} {y : α} :
-- zs = cs ++ ds.flatten := by sorry
/-- Two arrays of subarrays are equal iff their flattens coincide, as well as the sizes of the
subarrays. -/
/-- Two arrays of arrays are equal iff their flattens coincide, as well as the sizes of the
arrays. -/
theorem eq_iff_flatten_eq {xss₁ xss₂ : Array (Array α)} :
xss₁ = xss₂ xss₁.flatten = xss₂.flatten map size xss₁ = map size xss₂ := by
cases xss₁ using array₂_induction with
@@ -2258,18 +2243,12 @@ theorem eq_iff_flatten_eq {xss₁ xss₂ : Array (Array α)} :
rw [List.map_inj_right]
simp +contextual
theorem flatten_toArray_map_toArray {xss : List (List α)} :
(xss.map List.toArray).toArray.flatten = xss.flatten.toArray := by
simp
/-! ### flatMap -/
theorem flatMap_def {xs : Array α} {f : α Array β} : xs.flatMap f = flatten (map f xs) := by
rcases xs with l
simp [flatten_toArray, Function.comp_def, List.flatMap_def]
@[simp, grind =] theorem flatMap_empty {β} {f : α Array β} : (#[] : Array α).flatMap f = #[] := rfl
theorem flatMap_toList {xs : Array α} {f : α List β} :
xs.toList.flatMap f = (xs.flatMap (fun a => (f a).toArray)).toList := by
rcases xs with l
@@ -2280,6 +2259,7 @@ theorem flatMap_toList {xs : Array α} {f : α → List β} :
rcases xs with l
simp
@[deprecated List.flatMap_toArray_cons (since := "2025-10-29")]
theorem flatMap_toArray_cons {β} {f : α Array β} {a : α} {as : List α} :
(a :: as).toArray.flatMap f = f a ++ as.toArray.flatMap f := by
simp [flatMap]
@@ -2290,6 +2270,7 @@ theorem flatMap_toArray_cons {β} {f : α → Array β} {a : α} {as : List α}
intro cs
induction as generalizing cs <;> simp_all
@[deprecated List.flatMap_toArray (since := "2025-10-29")]
theorem flatMap_toArray {β} {f : α Array β} {as : List α} :
as.toArray.flatMap f = (as.flatMap (fun a => (f a).toList)).toArray := by
simp
@@ -2390,77 +2371,44 @@ theorem flatMap_eq_foldl {f : α → Array β} {xs : Array α} :
@[simp] theorem replicate_one : replicate 1 a = #[a] := rfl
@[deprecated replicate_one (since := "2025-03-18")]
abbrev mkArray_one := @replicate_one
/-- Variant of `replicate_succ` that prepends `a` at the beginning of the array. -/
theorem replicate_succ' : replicate (n + 1) a = #[a] ++ replicate n a := by
apply Array.ext'
simp [List.replicate_succ]
@[deprecated replicate_succ' (since := "2025-03-18")]
abbrev mkArray_succ' := @replicate_succ'
@[simp, grind =] theorem mem_replicate {a b : α} {n} : b replicate n a n 0 b = a := by
unfold replicate
simp only [List.mem_toArray, List.mem_replicate]
@[deprecated mem_replicate (since := "2025-03-18")]
abbrev mem_mkArray := @mem_replicate
@[grind ] theorem eq_of_mem_replicate {a b : α} {n} (h : b replicate n a) : b = a := (mem_replicate.1 h).2
@[deprecated eq_of_mem_mkArray (since := "2025-03-18")]
abbrev eq_of_mem_mkArray := @eq_of_mem_replicate
theorem forall_mem_replicate {p : α Prop} {a : α} {n} :
( b, b replicate n a p b) n = 0 p a := by
cases n <;> simp [mem_replicate]
@[deprecated forall_mem_replicate (since := "2025-03-18")]
abbrev forall_mem_mkArray := @forall_mem_replicate
@[simp] theorem replicate_succ_ne_empty {n : Nat} {a : α} : replicate (n+1) a #[] := by
simp [replicate_succ]
@[deprecated replicate_succ_ne_empty (since := "2025-03-18")]
abbrev mkArray_succ_ne_empty := @replicate_succ_ne_empty
@[simp] theorem replicate_eq_empty_iff {n : Nat} {a : α} : replicate n a = #[] n = 0 := by
cases n <;> simp
@[deprecated replicate_eq_empty_iff (since := "2025-03-18")]
abbrev mkArray_eq_empty_iff := @replicate_eq_empty_iff
@[simp] theorem replicate_inj : replicate n a = replicate m b n = m (n = 0 a = b) := by
rw [ toList_inj]
simp
@[deprecated replicate_inj (since := "2025-03-18")]
abbrev mkArray_inj := @replicate_inj
theorem eq_replicate_of_mem {a : α} {xs : Array α} (h : (b) (_ : b xs), b = a) : xs = replicate xs.size a := by
rw [ toList_inj]
simpa using List.eq_replicate_of_mem (by simpa using h)
@[deprecated eq_replicate_of_mem (since := "2025-03-18")]
abbrev eq_mkArray_of_mem := @eq_replicate_of_mem
theorem eq_replicate_iff {a : α} {n} {xs : Array α} :
xs = replicate n a xs.size = n (b) (_ : b xs), b = a := by
rw [ toList_inj]
simpa using List.eq_replicate_iff (l := xs.toList)
@[deprecated eq_replicate_iff (since := "2025-03-18")]
abbrev eq_mkArray_iff := @eq_replicate_iff
theorem map_eq_replicate_iff {xs : Array α} {f : α β} {b : β} :
xs.map f = replicate xs.size b x xs, f x = b := by
simp [eq_replicate_iff]
@[deprecated map_eq_replicate_iff (since := "2025-03-18")]
abbrev map_eq_mkArray_iff := @map_eq_replicate_iff
@[simp] theorem map_const {xs : Array α} {b : β} : map (Function.const α b) xs = replicate xs.size b :=
map_eq_replicate_iff.mpr fun _ _ => rfl
@@ -2477,143 +2425,86 @@ theorem map_const' {xs : Array α} {b : β} : map (fun _ => b) xs = replicate xs
apply Array.ext'
simp
@[deprecated set_replicate_self (since := "2025-03-18")]
abbrev set_mkArray_self := @set_replicate_self
@[simp] theorem setIfInBounds_replicate_self : (replicate n a).setIfInBounds i a = replicate n a := by
apply Array.ext'
simp
@[deprecated setIfInBounds_replicate_self (since := "2025-03-18")]
abbrev setIfInBounds_mkArray_self := @setIfInBounds_replicate_self
@[simp] theorem replicate_append_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
apply Array.ext'
simp
@[deprecated replicate_append_replicate (since := "2025-03-18")]
abbrev mkArray_append_mkArray := @replicate_append_replicate
theorem append_eq_replicate_iff {xs ys : Array α} {a : α} :
xs ++ ys = replicate n a
xs.size + ys.size = n xs = replicate xs.size a ys = replicate ys.size a := by
simp [ toList_inj, List.append_eq_replicate_iff]
@[deprecated append_eq_replicate_iff (since := "2025-03-18")]
abbrev append_eq_mkArray_iff := @append_eq_replicate_iff
theorem replicate_eq_append_iff {xs ys : Array α} {a : α} :
replicate n a = xs ++ ys
xs.size + ys.size = n xs = replicate xs.size a ys = replicate ys.size a := by
rw [eq_comm, append_eq_replicate_iff]
@[deprecated replicate_eq_append_iff (since := "2025-03-18")]
abbrev replicate_eq_mkArray_iff := @replicate_eq_append_iff
@[simp] theorem map_replicate : (replicate n a).map f = replicate n (f a) := by
apply Array.ext'
simp
@[deprecated map_replicate (since := "2025-03-18")]
abbrev map_mkArray := @map_replicate
@[grind =] theorem filter_replicate (w : stop = n) :
(replicate n a).filter p 0 stop = if p a then replicate n a else #[] := by
apply Array.ext'
simp only [w]
split <;> simp_all
@[deprecated filter_replicate (since := "2025-03-18")]
abbrev filter_mkArray := @filter_replicate
@[simp] theorem filter_replicate_of_pos (w : stop = n) (h : p a) :
(replicate n a).filter p 0 stop = replicate n a := by
simp [filter_replicate, h, w]
@[deprecated filter_replicate_of_pos (since := "2025-03-18")]
abbrev filter_mkArray_of_pos := @filter_replicate_of_pos
@[simp] theorem filter_replicate_of_neg (w : stop = n) (h : ¬ p a) :
(replicate n a).filter p 0 stop = #[] := by
simp [filter_replicate, h, w]
@[deprecated filter_replicate_of_neg (since := "2025-03-18")]
abbrev filter_mkArray_of_neg := @filter_replicate_of_neg
theorem filterMap_replicate {f : α Option β} (w : stop = n := by simp) :
(replicate n a).filterMap f 0 stop = match f a with | none => #[] | .some b => replicate n b := by
apply Array.ext'
simp only [w, size_replicate, toList_filterMap', toList_replicate, List.filterMap_replicate]
split <;> simp_all
@[deprecated filterMap_replicate (since := "2025-03-18")]
abbrev filterMap_mkArray := @filterMap_replicate
-- This is not a useful `simp` lemma because `b` is unknown.
theorem filterMap_replicate_of_some {f : α Option β} (h : f a = some b) :
(replicate n a).filterMap f = replicate n b := by
simp [filterMap_replicate, h]
@[deprecated filterMap_replicate_of_some (since := "2025-03-18")]
abbrev filterMap_mkArray_of_some := @filterMap_replicate_of_some
@[simp] theorem filterMap_replicate_of_isSome {f : α Option β} (h : (f a).isSome) :
(replicate n a).filterMap f = replicate n (Option.get _ h) := by
match w : f a, h with
| some b, _ => simp [filterMap_replicate, w]
@[deprecated filterMap_replicate_of_isSome (since := "2025-03-18")]
abbrev filterMap_mkArray_of_isSome := @filterMap_replicate_of_isSome
@[simp] theorem filterMap_replicate_of_none {f : α Option β} (h : f a = none) :
(replicate n a).filterMap f = #[] := by
simp [filterMap_replicate, h]
@[deprecated filterMap_replicate_of_none (since := "2025-03-18")]
abbrev filterMap_mkArray_of_none := @filterMap_replicate_of_none
@[simp] theorem flatten_replicate_empty : (replicate n (#[] : Array α)).flatten = #[] := by
rw [ toList_inj]
simp
@[deprecated flatten_replicate_empty (since := "2025-03-18")]
abbrev flatten_mkArray_empty := @flatten_replicate_empty
@[simp] theorem flatten_replicate_singleton : (replicate n #[a]).flatten = replicate n a := by
rw [ toList_inj]
simp
@[deprecated flatten_replicate_singleton (since := "2025-03-18")]
abbrev flatten_mkArray_singleton := @flatten_replicate_singleton
@[simp] theorem flatten_replicate_replicate : (replicate n (replicate m a)).flatten = replicate (n * m) a := by
rw [ toList_inj]
simp
@[deprecated flatten_replicate_replicate (since := "2025-03-18")]
abbrev flatten_mkArray_replicate := @flatten_replicate_replicate
theorem flatMap_replicate {f : α Array β} : (replicate n a).flatMap f = (replicate n (f a)).flatten := by
rw [ toList_inj]
simp [List.flatMap_replicate]
@[deprecated flatMap_replicate (since := "2025-03-18")]
abbrev flatMap_mkArray := @flatMap_replicate
@[simp] theorem isEmpty_replicate : (replicate n a).isEmpty = decide (n = 0) := by
rw [ List.toArray_replicate, List.isEmpty_toArray]
simp
@[deprecated isEmpty_replicate (since := "2025-03-18")]
abbrev isEmpty_mkArray := @isEmpty_replicate
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
@[deprecated sum_replicate_nat (since := "2025-03-18")]
abbrev sum_mkArray_nat := @sum_replicate_nat
/-! ### Preliminaries about `swap` needed for `reverse`. -/
@[grind =]
@@ -2655,8 +2546,8 @@ theorem getElem?_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} : (xs.swap i
split <;> rename_i h₃
· simp only [ h₃, Nat.not_le.2 (Nat.lt_succ_self _), Nat.le_refl, false_and]
exact (List.getElem?_reverse' (Eq.trans (by simp +arith) h)).symm
simp only [Nat.succ_le, Nat.lt_iff_le_and_ne.trans (and_iff_left h₃),
Nat.lt_succ.symm.trans (Nat.lt_iff_le_and_ne.trans (and_iff_left (Ne.symm h₂)))]
simp only [Nat.succ_le_iff, Nat.lt_iff_le_and_ne.trans (and_iff_left h₃),
Nat.lt_succ_iff.symm.trans (Nat.lt_iff_le_and_ne.trans (and_iff_left (Ne.symm h₂)))]
· rw [H]; split <;> rename_i h₂
· cases Nat.le_antisymm (Nat.not_lt.1 h₁) (Nat.le_trans h₂.1 h₂.2)
cases Nat.le_antisymm h₂.1 h₂.2
@@ -2671,7 +2562,7 @@ theorem getElem?_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} : (xs.swap i
split
· rfl
· rename_i h
simp only [ show k < _ + 1 _ from Nat.lt_succ (n := xs.size - 1), this, Nat.zero_le,
simp only [ show k < _ + 1 _ from Nat.lt_succ_iff (n := xs.size - 1), this, Nat.zero_le,
true_and, Nat.not_lt] at h
rw [List.getElem?_eq_none_iff.2 _, List.getElem?_eq_none_iff.2 (xs.toList.length_reverse _)]
@@ -2800,9 +2691,6 @@ theorem flatten_reverse {xss : Array (Array α)} :
rw [ toList_inj]
simp
@[deprecated reverse_replicate (since := "2025-03-18")]
abbrev reverse_mkArray := @reverse_replicate
/-! ### extract -/
theorem extract_loop_zero {xs ys : Array α} {start : Nat} : extract.loop xs 0 start ys = ys := by
@@ -2822,8 +2710,8 @@ theorem extract_loop_eq_aux {xs ys : Array α} {size start : Nat} :
| zero => rw [extract_loop_zero, extract_loop_zero, append_empty]
| succ size ih =>
if h : start < xs.size then
rw [extract_loop_succ (h := h), ih, push_eq_append_singleton]
rw [extract_loop_succ (h := h), ih (ys := #[].push _), push_eq_append_singleton, empty_append]
rw [extract_loop_succ (h := h), ih, push_eq_append]
rw [extract_loop_succ (h := h), ih (ys := #[].push _), push_eq_append, empty_append]
rw [append_assoc]
else
rw [extract_loop_of_ge (h := Nat.le_of_not_lt h)]
@@ -3440,6 +3328,16 @@ theorem foldr_filterMap {f : α → Option β} {g : β → γγ} {xs : Arra
(xs.filterMap f).foldr g init = xs.foldr (fun x y => match f x with | some b => g b y | none => y) init := by
simp [foldr_filterMap']
theorem foldl_flatMap {f : α Array β} {g : γ β γ} {xs : Array α} {init : γ} :
(xs.flatMap f).foldl g init = xs.foldl (fun acc x => (f x).foldl g acc) init := by
rcases xs with l
simp [List.foldl_flatMap]
theorem foldr_flatMap {f : α Array β} {g : β γ γ} {xs : Array α} {init : γ} :
(xs.flatMap f).foldr g init = xs.foldr (fun x acc => (f x).foldr g acc) init := by
rcases xs with l
simp [List.foldr_flatMap]
theorem foldl_map_hom' {g : α β} {f : α α α} {f' : β β β} {a : α} {xs : Array α}
{stop : Nat} (h : x y, f' (g x) (g y) = g (f x y)) (w : stop = xs.size) :
(xs.map g).foldl f' (g a) 0 stop = g (xs.foldl f a) := by
@@ -3712,15 +3610,9 @@ theorem back?_replicate {a : α} {n : Nat} :
rw [replicate_eq_toArray_replicate]
simp only [List.back?_toArray, List.getLast?_replicate]
@[deprecated back?_replicate (since := "2025-03-18")]
abbrev back?_mkArray := @back?_replicate
@[simp] theorem back_replicate {xs : Array α} (w : 0 < n) : (replicate n xs).back (by simpa using w) = xs := by
simp [back_eq_getElem]
@[deprecated back_replicate (since := "2025-03-18")]
abbrev back_mkArray := @back_replicate
/-! ## Additional operations -/
/-! ### leftpad -/
@@ -3738,9 +3630,6 @@ theorem size_rightpad {n : Nat} {a : α} {xs : Array α} :
theorem elem_push_self [BEq α] [LawfulBEq α] {xs : Array α} {a : α} : (xs.push a).elem a = true := by simp
@[deprecated elem_push_self (since := "2025-04-04")]
abbrev elem_cons_self := @elem_push_self
theorem contains_eq_any_beq [BEq α] {xs : Array α} {a : α} : xs.contains a = xs.any (a == ·) := by
rcases xs with xs
simp [List.contains_eq_any_beq]
@@ -3754,11 +3643,6 @@ theorem contains_iff_exists_mem_beq [BEq α] {xs : Array α} {a : α} :
-- With `LawfulBEq α`, it would be better to use `contains_iff_mem` directly.
grind_pattern contains_iff_exists_mem_beq => xs.contains a
@[grind =]
theorem contains_iff_mem [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.contains a a xs := by
simp
@[simp, grind =]
theorem contains_toList [BEq α] {xs : Array α} {x : α} :
xs.toList.contains x = xs.contains x := by
@@ -3818,9 +3702,6 @@ theorem pop_append {xs ys : Array α} :
@[simp, grind =] theorem pop_replicate {n : Nat} {a : α} : (replicate n a).pop = replicate (n - 1) a := by
ext <;> simp
@[deprecated pop_replicate (since := "2025-03-18")]
abbrev pop_mkArray := @pop_replicate
/-! ## Logic -/
/-! ### any / all -/
@@ -4050,16 +3931,10 @@ theorem all_filterMap {xs : Array α} {f : α → Option β} {p : β → Bool} :
(replicate n a).any f = if n = 0 then false else f a := by
induction n <;> simp_all [replicate_succ']
@[deprecated any_replicate (since := "2025-03-18")]
abbrev any_mkArray := @any_replicate
@[simp] theorem all_replicate {n : Nat} {a : α} :
(replicate n a).all f = if n = 0 then true else f a := by
induction n <;> simp_all +contextual [replicate_succ']
@[deprecated all_replicate (since := "2025-03-18")]
abbrev all_mkArray := @all_replicate
/-! ### modify -/
@[simp, grind =] theorem size_modify {xs : Array α} {i : Nat} {f : α α} : (xs.modify i f).size = xs.size := by
@@ -4234,17 +4109,11 @@ theorem replace_extract {xs : Array α} {i : Nat} :
(replicate n a).replace a b = #[b] ++ replicate (n - 1) a := by
cases n <;> simp_all [replicate_succ', replace_append]
@[deprecated replace_replicate_self (since := "2025-03-18")]
abbrev replace_mkArray_self := @replace_replicate_self
@[simp] theorem replace_replicate_ne {a b c : α} (h : !b == a) :
(replicate n a).replace b c = replicate n a := by
rw [replace_of_not_mem]
simp_all
@[deprecated replace_replicate_ne (since := "2025-03-18")]
abbrev replace_mkArray_ne := @replace_replicate_ne
end replace
/-! ### toListRev -/
@@ -4412,9 +4281,6 @@ theorem getElem!_eq_getD [Inhabited α] {xs : Array α} {i} : xs[i]! = xs.getD i
@[deprecated mem_toList_iff (since := "2025-05-26")]
theorem mem_toList {a : α} {xs : Array α} : a xs.toList a xs := mem_def.symm
@[deprecated not_mem_empty (since := "2025-03-25")]
theorem not_mem_nil (a : α) : ¬ a #[] := nofun
/-! # get lemmas -/
theorem lt_of_getElem {x : α} {xs : Array α} {i : Nat} {hidx : i < xs.size} (_ : xs[i] = x) :
@@ -4426,6 +4292,7 @@ theorem getElem_fin_eq_getElem_toList {xs : Array α} {i : Fin xs.size} : xs[i]
@[simp] theorem ugetElem_eq_getElem {xs : Array α} {i : USize} (h : i.toNat < xs.size) :
xs[i] = xs[i.toNat] := rfl
@[deprecated getElem?_eq_none (since := "2025-10-26")]
theorem getElem?_size_le {xs : Array α} {i : Nat} (h : xs.size i) : xs[i]? = none := by
simp [getElem?_neg, h]
@@ -4445,6 +4312,7 @@ theorem getElem?_push_lt {xs : Array α} {x : α} {i : Nat} (h : i < xs.size) :
(xs.push x)[i]? = some xs[i] := by
rw [getElem?_pos (xs.push x) i (size_push _ Nat.lt_succ_of_lt h), getElem_push_lt]
@[deprecated getElem?_push_size (since := "2025-10-26")]
theorem getElem?_push_eq {xs : Array α} {x : α} : (xs.push x)[xs.size]? = some x := by
rw [getElem?_pos (xs.push x) xs.size (size_push _ Nat.lt_succ_self xs.size), getElem_push_eq]
@@ -4463,12 +4331,6 @@ theorem getElem?_push_eq {xs : Array α} {x : α} : (xs.push x)[xs.size]? = some
cases xs
simp
/-! ### contains -/
@[deprecated contains_iff (since := "2025-04-07")]
abbrev contains_def [DecidableEq α] {a : α} {xs : Array α} : xs.contains a a xs :=
contains_iff
/-! ### isPrefixOf -/
@[simp, grind =] theorem isPrefixOf_toList [BEq α] {xs ys : Array α} :
@@ -4582,7 +4444,8 @@ theorem uset_toArray {l : List α} {i : USize} {a : α} {h : i.toNat < l.toArray
apply ext'
simp
@[simp, grind =] theorem flatten_toArray {L : List (List α)} :
@[deprecated Array.flatten_map_toArray_toArray (since := "2025-10-26")]
theorem flatten_toArray {L : List (List α)} :
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
apply ext'
simp

View File

@@ -34,7 +34,18 @@ grind_pattern _root_.List.le_toArray => l₁.toArray ≤ l₂.toArray
grind_pattern lt_toList => xs.toList < ys.toList
grind_pattern le_toList => xs.toList ys.toList
@[simp]
protected theorem not_lt [LT α] {xs ys : Array α} : ¬ xs < ys ys xs := Iff.rfl
@[deprecated Array.not_lt (since := "2025-10-26")]
protected theorem not_lt_iff_ge [LT α] {xs ys : Array α} : ¬ xs < ys ys xs := Iff.rfl
@[simp]
protected theorem not_le [LT α] {xs ys : Array α} :
¬ xs ys ys < xs :=
Classical.not_not
@[deprecated Array.not_le (since := "2025-10-26")]
protected theorem not_le_iff_gt [LT α] {xs ys : Array α} :
¬ xs ys ys < xs :=
Classical.not_not
@@ -64,11 +75,11 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
Nat.add_min_add_left, Nat.add_lt_add_iff_left, Std.Rco.forIn'_eq_forIn'_toList]
conv =>
lhs; congr; congr
rw [cons_lex_cons.forIn'_congr_aux Std.Rco.toList_eq_if rfl (fun _ _ _ => rfl)]
rw [cons_lex_cons.forIn'_congr_aux Std.Rco.toList_eq_if_roo rfl (fun _ _ _ => rfl)]
simp only [bind_pure_comp, map_pure]
rw [cons_lex_cons.forIn'_congr_aux (if_pos (by omega)) rfl (fun _ _ _ => rfl)]
simp only [Std.toList_Roo_eq_toList_Rco_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_Rco_succ_succ,
simp only [Std.toList_roo_eq_toList_rco_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_rco_succ_succ,
Option.get_some, List.forIn'_cons, List.size_toArray, List.length_cons, List.length_nil,
Nat.lt_add_one, getElem_append_left, List.getElem_toArray, List.getElem_cons_zero]
cases lt a b
@@ -140,7 +151,7 @@ protected theorem lt_of_le_of_lt [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrd
@[deprecated Array.lt_of_le_of_lt (since := "2025-08-01")]
protected theorem lt_of_le_of_lt' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₂ : Std.Trichotomous (· < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
letI := LE.ofLT α
@@ -154,7 +165,7 @@ protected theorem le_trans [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α]
@[deprecated Array.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₂ : Std.Trichotomous (· < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
letI := LE.ofLT α
@@ -178,12 +189,6 @@ protected theorem le_total [LT α]
[i : Std.Asymm (· < · : α α Prop)] (xs ys : Array α) : xs ys ys xs :=
List.le_total xs.toList ys.toList
@[simp] protected theorem not_lt [LT α]
{xs ys : Array α} : ¬ xs < ys ys xs := Iff.rfl
@[simp] protected theorem not_le [LT α]
{xs ys : Array α} : ¬ ys xs xs < ys := Classical.not_not
protected theorem le_of_lt [LT α]
[i : Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} (h : xs < ys) : xs ys :=
@@ -191,7 +196,7 @@ protected theorem le_of_lt [LT α]
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Trichotomous (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} : xs ys xs < ys xs = ys := by
simpa using List.le_iff_lt_or_eq (l₁ := xs.toList) (l₂ := ys.toList)
@@ -280,7 +285,7 @@ protected theorem lt_iff_exists [LT α] {xs ys : Array α} :
protected theorem le_iff_exists [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {xs ys : Array α} :
[Std.Trichotomous (· < · : α α Prop)] {xs ys : Array α} :
xs ys
(xs = ys.take xs.size)
( (i : Nat) (h₁ : i < xs.size) (h₂ : i < ys.size),
@@ -299,7 +304,7 @@ theorem append_left_lt [LT α] {xs ys zs : Array α} (h : ys < zs) :
theorem append_left_le [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Trichotomous (· < · : α α Prop)]
{xs ys zs : Array α} (h : ys zs) :
xs ++ ys xs ++ zs := by
cases xs
@@ -322,9 +327,9 @@ protected theorem map_lt [LT α] [LT β]
protected theorem map_le [LT α] [LT β]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Trichotomous (· < · : α α Prop)]
[Std.Asymm (· < · : β β Prop)]
[Std.Antisymm (¬ · < · : β β Prop)]
[Std.Trichotomous (· < · : β β Prop)]
{xs ys : Array α} {f : α β} (w : x y, x < y f x < f y) (h : xs ys) :
map f xs map f ys := by
cases xs

View File

@@ -296,9 +296,6 @@ theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) → α → (h
rw [ toList_inj]
simp [List.mapFinIdx_eq_replicate_iff]
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapFinIdx_eq_mkArray_iff := @mapFinIdx_eq_replicate_iff
@[simp, 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
@@ -438,9 +435,6 @@ theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat → α → β} {b : β}
rw [ toList_inj]
simp [List.mapIdx_eq_replicate_iff]
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapIdx_eq_mkArray_iff := @mapIdx_eq_replicate_iff
@[simp, 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

View File

@@ -84,9 +84,6 @@ theorem Perm.size_eq {xs ys : Array α} (p : xs ~ ys) : xs.size = ys.size := by
simp only [perm_iff_toList_perm] at p
simpa using p.length_eq
@[deprecated Perm.size_eq (since := "2025-04-17")]
abbrev Perm.length_eq := @Perm.size_eq
theorem Perm.mem_iff {a : α} {xs ys : Array α} (p : xs ~ ys) : a xs a ys := by
rcases xs with xs
rcases ys with ys
@@ -107,7 +104,7 @@ 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]
rw [push_eq_append]
exact p.append .rfl
grind_pattern Perm.push => xs ~ ys, xs.push x

View File

@@ -166,9 +166,6 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
simp [ List.toArray_replicate]
@[deprecated zipWith_replicate (since := "2025-03-18")]
abbrev zipWith_mkArray := @zipWith_replicate
theorem map_uncurry_zip_eq_zipWith {f : α β γ} {as : Array α} {bs : Array β} :
map (Function.uncurry f) (as.zip bs) = zipWith f as bs := by
cases as
@@ -294,9 +291,6 @@ theorem zip_eq_append_iff {as : Array α} {bs : Array β} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
simp [ List.toArray_replicate]
@[deprecated zip_replicate (since := "2025-03-18")]
abbrev zip_mkArray := @zip_replicate
theorem zip_eq_zip_take_min {as : Array α} {bs : Array β} :
zip as bs = zip (as.take (min as.size bs.size)) (bs.take (min as.size bs.size)) := by
cases as
@@ -348,9 +342,6 @@ theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option
zipWithAll f (replicate n a) (replicate n b) = replicate n (f (some a) (some b)) := by
simp [ List.toArray_replicate]
@[deprecated zipWithAll_replicate (since := "2025-03-18")]
abbrev zipWithAll_mkArray := @zipWithAll_replicate
/-! ### zipWithM -/
@[simp, grind =]
@@ -408,7 +399,4 @@ theorem zip_of_prod {as : Array α} {bs : Array β} {xs : Array (α × β)} (hl
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

@@ -1,10 +0,0 @@
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Data.UInt
public import Init.Data.String.Extra

View File

@@ -203,8 +203,8 @@ If `n` is `0`, then one digit is returned. Otherwise, `⌊(n + 3) / 4⌋` digits
-- `Internal` string functions by moving this definition out to a separate file that can live
-- downstream of `Init.Data.String.Basic`.
protected def toHex {n : Nat} (x : BitVec n) : String :=
let s := (Nat.toDigits 16 x.toNat).asString
let t := (List.replicate ((n+3) / 4 - String.Internal.length s) '0').asString
let s := String.ofList (Nat.toDigits 16 x.toNat)
let t := String.ofList (List.replicate ((n+3) / 4 - String.Internal.length s) '0')
String.Internal.append t s
/-- `BitVec` representation. -/

View File

@@ -635,12 +635,11 @@ theorem mulRec_eq_mul_signExtend_setWidth (x y : BitVec w) (s : Nat) :
simp only [mulRec_zero_eq, ofNat_eq_ofNat, Nat.reduceAdd]
by_cases y.getLsbD 0
case pos hy =>
simp only [hy, reduceIte, setWidth_one_eq_ofBool_getLsb_zero,
ofBool_true, ofNat_eq_ofNat]
simp only [hy, reduceIte, setWidth_one, ofBool_true, ofNat_eq_ofNat]
rw [setWidth_ofNat_one_eq_ofNat_one_of_lt (by omega)]
simp
case neg hy =>
simp [hy, setWidth_one_eq_ofBool_getLsb_zero]
simp [hy, setWidth_one]
case succ s' hs =>
rw [mulRec_succ_eq, hs]
have heq :
@@ -1025,7 +1024,7 @@ theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
case neg.hrWidth =>
simp only
have hdr' : d (qr.r.shiftConcat (n.getLsbD (qr.wn - 1))) :=
BitVec.not_lt_iff_le.mp rltd
BitVec.not_lt.mp rltd
have hr' : ((qr.r.shiftConcat (n.getLsbD (qr.wn - 1)))).toNat < 2 ^ (qr.wr + 1) := by
apply toNat_shiftConcat_lt_of_lt <;> bv_omega
rw [BitVec.toNat_sub_of_le hdr']
@@ -1033,7 +1032,7 @@ theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
case neg.hqWidth =>
apply toNat_shiftConcat_lt_of_lt <;> omega
case neg.hdiv =>
have rltd' := (BitVec.not_lt_iff_le.mp rltd)
have rltd' := (BitVec.not_lt.mp rltd)
simp only [qr.toNat_shiftRight_sub_one_eq h,
BitVec.toNat_sub_of_le rltd',
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth]
@@ -1407,7 +1406,7 @@ theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x,
have := congrArg g h'
simpa [h] using this
@[simp]
@[deprecated BitVec.ne_intMin_of_msb_eq_false (since := "2025-10-26")]
theorem ne_intMin_of_lt_of_msb_false {x : BitVec w} (hw : 0 < w) (hx : x.msb = false) :
x intMin w := by
have := toNat_lt_of_msb_false hx
@@ -1512,7 +1511,7 @@ theorem sdiv_ne_intMin_of_ne_intMin {x y : BitVec w} (h : x ≠ intMin w) :
by_cases hx : x.msb <;> by_cases hy : y.msb
<;> simp only [hx, hy, neg_ne_intMin_inj]
<;> simp only [Bool.not_eq_true] at hx hy
<;> apply ne_intMin_of_lt_of_msb_false (by omega)
<;> apply ne_intMin_of_msb_eq_false (by omega)
<;> rw [msb_udiv]
<;> try simp only [hx, Bool.false_and]
· simp [h, ne_zero_of_msb_true, hx]
@@ -1624,7 +1623,7 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
· have ry := (intMin_udiv_eq_intMin_iff b).mp
simp only [hb1, imp_false] at ry
simp [msb_udiv, ha_intMin, hb1, ry, intMin_udiv_ne_zero_of_ne_zero, hb, hb0]
· have := @BitVec.ne_intMin_of_lt_of_msb_false w ((-a) / b) wpos (by simp [ha, ha0, ha_intMin])
· have := @BitVec.ne_intMin_of_msb_eq_false w wpos ((-a) / b) (by simp [ha, ha0, ha_intMin])
simp [msb_neg, h', this, ha, ha_intMin]
rw [toInt_eq_toNat_of_msb hb, toInt_eq_neg_toNat_neg_of_msb_true ha, Int.neg_tdiv,
Int.tdiv_eq_ediv_of_nonneg (by omega), sdiv_toInt_of_msb_true_of_msb_false]
@@ -1635,7 +1634,7 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
rw [toInt_udiv_of_msb ha, toInt_eq_toNat_of_msb ha]
rw [toInt_eq_neg_toNat_neg_of_msb_true hb, Int.tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
· apply sdiv_ne_intMin_of_ne_intMin
apply ne_intMin_of_lt_of_msb_false (by omega) ha
apply ne_intMin_of_msb_eq_false (by omega) ha
· rw [sdiv, Int.tdiv_cases, udiv_eq, neg_eq, if_pos (toInt_nonneg_of_msb_false ha),
if_pos (toInt_nonneg_of_msb_false hb), ha, hb, toInt_udiv_of_msb ha,
toInt_eq_toNat_of_msb ha, toInt_eq_toNat_of_msb hb]
@@ -1927,7 +1926,7 @@ theorem toInt_sub_neg_umod {x y : BitVec w} (hxmsb : x.msb = true) (hymsb : y.ms
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]
simp only [hdvd, reduceIte, Int.natAbs_natCast]
theorem srem_zero_of_dvd {x y : BitVec w} (h : y.toInt x.toInt) :
x.srem y = 0#w := by

View File

@@ -82,7 +82,7 @@ theorem iunfoldr_getLsbD' {f : Fin w → αα × Bool} (state : Nat → α)
intro i
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
cases (Nat.lt_or_eq_of_le (Nat.lt_succ_iff.mp i.isLt)) with
| inl h3 => simp [(Nat.ne_of_lt h3)]
exact (ih hj2).1 i.val, h3
| inr h3 => simp [h3]

View File

@@ -34,14 +34,6 @@ namespace BitVec
simp only [Bool.and_eq_false_imp, decide_eq_true_eq]
omega
set_option linter.missingDocs false in
@[deprecated getLsbD_of_ge (since := "2025-04-04")]
abbrev getLsbD_ge := @getLsbD_of_ge
set_option linter.missingDocs false in
@[deprecated getMsbD_of_ge (since := "2025-04-04")]
abbrev getMsbD_ge := @getMsbD_of_ge
theorem lt_of_getLsbD {x : BitVec w} {i : Nat} : getLsbD x i = true i < w := by
if h : i < w then
simp [h]
@@ -72,6 +64,7 @@ theorem getElem?_eq_none_iff {l : BitVec w} : l[n]? = none ↔ w ≤ n := by
theorem none_eq_getElem?_iff {l : BitVec w} : none = l[n]? w n := by
simp
@[simp]
theorem getElem?_eq_none {l : BitVec w} (h : w n) : l[n]? = none := getElem?_eq_none_iff.mpr h
theorem getElem?_eq (l : BitVec w) (i : Nat) :
@@ -140,9 +133,6 @@ theorem two_pow_le_toNat_of_getElem_eq_true {i : Nat} {x : BitVec w}
rw [ getElem_eq_testBit_toNat x i hi]
exact hx
@[grind =] theorem msb_eq_getMsbD (x : BitVec w) : x.msb = x.getMsbD 0 := by
simp [BitVec.msb]
@[grind =] theorem getMsb_eq_getLsb (x : BitVec w) (i : Fin w) :
x.getMsb i = x.getLsb w - 1 - i, by omega := by
simp only [getMsb, getLsb]
@@ -169,18 +159,13 @@ theorem getLsbD_eq_getMsbD (x : BitVec w) (i : Nat) : x.getLsbD i = (decide (i <
apply getLsbD_of_ge
omega
@[simp] theorem getElem?_of_ge (x : BitVec w) (i : Nat) (ge : w i) : x[i]? = none := by
@[deprecated getElem?_eq_none (since := "2025-10-29")]
theorem getElem?_of_ge (x : BitVec w) (i : Nat) (ge : w i) : x[i]? = none := by
simp [ge]
@[simp] theorem getMsb?_of_ge (x : BitVec w) (i : Nat) (ge : w i) : getMsb? x i = none := by
simp [getMsb?_eq_getLsb?]; omega
set_option linter.missingDocs false in
@[deprecated getElem?_of_ge (since := "2025-04-04")] abbrev getLsb?_ge := @getElem?_of_ge
set_option linter.missingDocs false in
@[deprecated getMsb?_of_ge (since := "2025-04-04")] abbrev getMsb?_ge := @getMsb?_of_ge
theorem lt_of_getElem?_eq_some (x : BitVec w) (i : Nat) : x[i]? = some b i < w := by
cases h : x[i]? with
| none => simp
@@ -203,18 +188,6 @@ theorem lt_of_isSome_getMsb? (x : BitVec w) (i : Nat) : (getMsb? x i).isSome →
else
simp [Nat.ge_of_not_lt h]
set_option linter.missingDocs false in
@[deprecated lt_of_getElem?_eq_some (since := "2025-04-04")]
abbrev lt_of_getLsb?_eq_some := @lt_of_getElem?_eq_some
set_option linter.missingDocs false in
@[deprecated lt_of_isSome_getElem? (since := "2025-04-04")]
abbrev lt_of_getLsb?_isSome := @lt_of_isSome_getElem?
set_option linter.missingDocs false in
@[deprecated lt_of_isSome_getMsb? (since := "2025-04-04")]
abbrev lt_of_getMsb?_isSome := @lt_of_isSome_getMsb?
theorem getMsbD_eq_getMsb?_getD (x : BitVec w) (i : Nat) :
x.getMsbD i = (x.getMsb? i).getD false := by
rw [getMsbD_eq_getLsbD]
@@ -444,12 +417,18 @@ theorem getElem?_zero_ofNat_one : (BitVec.ofNat (w+1) 1)[0]? = some true := by
-- This does not need to be a `@[simp]` theorem as it is already handled by `getElem?_eq_getElem`.
theorem getElem?_zero_ofBool (b : Bool) : (ofBool b)[0]? = some b := by
simp only [ofBool, ofNat_eq_ofNat, cond_eq_if]
simp only [ofBool, ofNat_eq_ofNat, cond_eq_ite]
split <;> simp_all
@[simp, grind =] theorem getElem_zero_ofBool (b : Bool) : (ofBool b)[0] = b := by
@[simp, grind =]
theorem getElem_ofBool_zero {b : Bool} : (ofBool b)[0] = b := by
rw [getElem_eq_iff, getElem?_zero_ofBool]
@[deprecated getElem_ofBool_zero (since := "2025-10-29")]
theorem getElem_zero_ofBool (b : Bool) : (ofBool b)[0] = b := by
simp
theorem getElem?_succ_ofBool (b : Bool) (i : Nat) : (ofBool b)[i + 1]? = none := by
simp
@@ -460,8 +439,6 @@ theorem getLsbD_ofBool (b : Bool) (i : Nat) : (ofBool b).getLsbD i = ((i = 0) &&
· simp only [ofBool, ofNat_eq_ofNat, cond_true, getLsbD_ofNat, Bool.and_true]
by_cases hi : i = 0 <;> simp [hi] <;> omega
theorem getElem_ofBool_zero {b : Bool} : (ofBool b)[0] = b := by simp
@[simp]
theorem getElem_ofBool {b : Bool} {h : i < 1}: (ofBool b)[i] = b := by
simp [ getLsbD_eq_getElem]
@@ -544,6 +521,10 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
@[grind _=_] theorem msb_eq_getMsbD_zero (x : BitVec w) : x.msb = x.getMsbD 0 := by
cases w <;> simp [getMsbD_eq_getLsbD, msb_eq_getLsbD_last]
@[deprecated msb_eq_getMsbD_zero (since := "2025-10-26")]
theorem msb_eq_getMsbD (x : BitVec w) : x.msb = x.getMsbD 0 := by
simp [BitVec.msb]
/-! ### cast -/
@[simp, grind =] theorem toFin_cast (h : w = v) (x : BitVec w) :
@@ -605,7 +586,7 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
simp only [toInt_eq_toNat_cond]
split
next g =>
rw [Int.bmod_pos] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
rw [Int.bmod_eq_emod_of_lt] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
omega
next g =>
rw [Int.bmod_neg] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
@@ -1013,7 +994,14 @@ theorem msb_setWidth' (x : BitVec w) (h : w ≤ v) : (x.setWidth' h).msb = (deci
theorem msb_setWidth'' (x : BitVec w) : (x.setWidth (k + 1)).msb = x.getLsbD k := by
simp [BitVec.msb, getMsbD]
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
theorem setWidth_one {x : BitVec w} :
x.setWidth 1 = ofBool (x.getLsbD 0) := by
ext i
simp [show i = 0 by omega]
/-- zero extending a bitvector to width 1 equals the boolean of the lsb. -/
@[deprecated setWidth_one (since := "2025-10-29")]
theorem setWidth_one_eq_ofBool_getLsb_zero (x : BitVec w) :
x.setWidth 1 = BitVec.ofBool (x.getLsbD 0) := by
ext i h
@@ -1029,12 +1017,6 @@ theorem setWidth_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
have hv := (@Nat.testBit_one_eq_true_iff_self_eq_zero i)
by_cases h : Nat.testBit 1 i = true <;> simp_all
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
theorem setWidth_one {x : BitVec w} :
x.setWidth 1 = ofBool (x.getLsbD 0) := by
ext i
simp [show i = 0 by omega]
@[simp, grind =] theorem setWidth_ofNat_of_le (h : v w) (x : Nat) : setWidth v (BitVec.ofNat w x) = BitVec.ofNat v x := by
apply BitVec.eq_of_toNat_eq
simp only [toNat_setWidth, toNat_ofNat]
@@ -1208,7 +1190,7 @@ let x' = x.extractLsb' 7 5 = _ _ 9 8 7
@[simp] theorem getLsbD_extract (hi lo : Nat) (x : BitVec n) (i : Nat) :
getLsbD (extractLsb hi lo x) i = (i (hi-lo) && getLsbD x (lo+i)) := by
simp [getLsbD, Nat.lt_succ]
simp [getLsbD, Nat.lt_succ_iff]
@[simp] theorem getLsbD_extractLsb {hi lo : Nat} {x : BitVec n} {i : Nat} :
(extractLsb hi lo x).getLsbD i = (decide (i < hi - lo + 1) && x.getLsbD (lo + i)) := by
@@ -1664,11 +1646,11 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem ofInt_negSucc_eq_not_ofNat {w n : Nat} :
BitVec.ofInt w (Int.negSucc n) = ~~~.ofNat w n := by
simp only [BitVec.ofInt, Int.toNat, Int.ofNat_eq_coe, toNat_eq, toNat_ofNatLT, toNat_not,
simp only [BitVec.ofInt, Int.toNat, Int.ofNat_eq_natCast, toNat_eq, toNat_ofNatLT, toNat_not,
toNat_ofNat]
cases h : Int.negSucc n % ((2 ^ w : Nat) : Int)
case ofNat =>
rw [Int.ofNat_eq_coe, Int.negSucc_emod] at h
rw [Int.ofNat_eq_natCast, Int.negSucc_emod] at h
· dsimp only
omega
· omega
@@ -1750,9 +1732,6 @@ theorem not_eq_comm {x y : BitVec w} : ~~~ x = y ↔ x = ~~~ y := by
rw [h]
simp
set_option linter.missingDocs false in
@[deprecated getMsbD_not (since := "2025-04-04")] abbrev getMsb_not := @getMsbD_not
@[simp] theorem msb_not {x : BitVec w} : (~~~x).msb = (decide (0 < w) && !x.msb) := by
simp [BitVec.msb]
@@ -2572,10 +2551,6 @@ theorem signExtend_eq_setWidth_of_le (x : BitVec w) {v : Nat} (hv : v ≤ w) :
ext i h
simp [getElem_signExtend, show i < w by omega]
@[deprecated signExtend_eq_setWidth_of_le (since := "2025-03-07")]
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v w) :
x.signExtend v = x.setWidth v := signExtend_eq_setWidth_of_le x hv
/-- Sign extending to the same bitwidth is a no op. -/
@[simp] theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_setWidth_of_le _ (Nat.le_refl _), setWidth_eq]
@@ -3635,9 +3610,6 @@ theorem sub_eq_add_neg {n} (x y : BitVec n) : x - y = x + - y := by
simp only [toNat_sub, toNat_add, toNat_neg, Nat.add_mod_mod]
rw [Nat.add_comm]
set_option linter.missingDocs false in
@[deprecated sub_eq_add_neg (since := "2025-04-04")] abbrev sub_toAdd := @sub_eq_add_neg
theorem add_left_neg (x : BitVec w) : -x + x = 0#w := by
apply toInt_inj.mp
simp [toInt_neg, Int.add_left_neg]
@@ -3677,10 +3649,6 @@ theorem neg_one_eq_allOnes : -1#w = allOnes w := by
have r : (2^w - 1) < 2^w := by omega
simp [Nat.mod_eq_of_lt q, Nat.mod_eq_of_lt r]
set_option linter.missingDocs false in
@[deprecated neg_one_eq_allOnes (since := "2025-04-04")]
abbrev negOne_eq_allOnes := @neg_one_eq_allOnes
theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1#w := by
apply eq_of_toNat_eq
simp only [toNat_neg, toNat_add, toNat_not, toNat_ofNat, Nat.add_mod_mod]
@@ -4097,6 +4065,7 @@ protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y → x % y < y :=
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
apply Nat.mod_lt
@[deprecated BitVec.not_lt (since := "2025-10-26")]
theorem not_lt_iff_le {x y : BitVec w} : (¬ x < y) y x := by
constructor <;>
(intro h; simp only [lt_def, Nat.not_lt, le_def] at h ; omega)
@@ -4113,7 +4082,7 @@ theorem not_lt_zero {x : BitVec w} : ¬x < 0#w := of_decide_eq_false rfl
theorem le_zero_iff {x : BitVec w} : x 0#w x = 0#w := by
constructor
· intro h
have : x 0 := not_lt_iff_le.mp not_lt_zero
have : x 0 := BitVec.not_lt.mp not_lt_zero
exact Eq.symm (BitVec.le_antisymm this h)
· simp_all
@@ -4136,7 +4105,7 @@ theorem not_allOnes_lt {x : BitVec w} : ¬allOnes w < x := by
theorem allOnes_le_iff {x : BitVec w} : allOnes w x x = allOnes w := by
constructor
· intro h
have : x allOnes w := not_lt_iff_le.mp not_allOnes_lt
have : x allOnes w := BitVec.not_lt.mp not_allOnes_lt
exact Eq.symm (BitVec.le_antisymm h this)
· simp_all
@@ -4682,9 +4651,6 @@ theorem zero_smod {x : BitVec w} : (0#w).smod x = 0#w := by
@[simp, grind =] theorem getLsbD_ofBoolListLE : (ofBoolListLE bs).getLsbD i = bs.getD i false := by
induction bs generalizing i <;> cases i <;> simp_all [ofBoolListLE]
set_option linter.missingDocs false in
@[deprecated getLsbD_ofBoolListLE (since := "2025-04-04")] abbrev getLsb_ofBoolListLE := @getLsbD_ofBoolListLE
@[simp, grind =] theorem getMsbD_ofBoolListLE :
(ofBoolListLE bs).getMsbD i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false) := by
simp [getMsbD_eq_getLsbD]
@@ -4755,14 +4721,6 @@ theorem getLsbD_rotateLeftAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i
apply getLsbD_of_ge
omega
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateLeftAux_of_lt (since := "2025-04-04")]
abbrev getLsbD_rotateLeftAux_of_le := @getLsbD_rotateLeftAux_of_lt
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateLeftAux_of_ge (since := "2025-04-04")]
abbrev getLsbD_rotateLeftAux_of_geq := @getLsbD_rotateLeftAux_of_ge
/-- When `r < w`, we give a formula for `(x.rotateLeft r).getLsbD i`. -/
theorem getLsbD_rotateLeft_of_le {x : BitVec w} {r i : Nat} (hr: r < w) :
(x.rotateLeft r).getLsbD i =
@@ -4919,14 +4877,6 @@ theorem getLsbD_rotateRightAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i
apply getLsbD_of_ge
omega
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateRightAux_of_lt (since := "2025-04-04")]
abbrev getLsbD_rotateRightAux_of_le := @getLsbD_rotateRightAux_of_lt
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateRightAux_of_ge (since := "2025-04-04")]
abbrev getLsbD_rotateRightAux_of_geq := @getLsbD_rotateRightAux_of_ge
/-- `rotateRight` equals the bit fiddling definition of `rotateRightAux` when the rotation amount is
smaller than the bitwidth. -/
theorem rotateRight_eq_rotateRightAux_of_lt {x : BitVec w} {r : Nat} (hr : r < w) :

View File

@@ -111,35 +111,11 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
@[simp] theorem eq_self_and : {a b : Bool}, (a = (a && b)) (a b) := by decide
@[simp] theorem eq_and_self : {a b : Bool}, (b = (a && b)) (b a) := by decide
@[deprecated and_eq_left_iff_imp (since := "2025-04-04")]
abbrev and_iff_left_iff_imp := @and_eq_left_iff_imp
@[deprecated and_eq_right_iff_imp (since := "2025-04-04")]
abbrev and_iff_right_iff_imp := @and_eq_right_iff_imp
@[deprecated eq_self_and (since := "2025-04-04")]
abbrev iff_self_and := @eq_self_and
@[deprecated eq_and_self (since := "2025-04-04")]
abbrev iff_and_self := @eq_and_self
@[simp] theorem not_and_eq_left_iff_and : {a b : Bool}, ((!a && b) = a) !a !b := by decide
@[simp] theorem and_not_eq_right_iff_and : {a b : Bool}, ((a && !b) = b) !a !b := by decide
@[simp] theorem eq_not_self_and : {a b : Bool}, (a = (!a && b)) !a !b := by decide
@[simp] theorem eq_and_not_self : {a b : Bool}, (b = (a && !b)) !a !b := by decide
@[deprecated not_and_eq_left_iff_and (since := "2025-04-04")]
abbrev not_and_iff_left_iff_imp := @not_and_eq_left_iff_and
@[deprecated and_not_eq_right_iff_and (since := "2025-04-04")]
abbrev and_not_iff_right_iff_imp := @and_not_eq_right_iff_and
@[deprecated eq_not_self_and (since := "2025-04-04")]
abbrev iff_not_self_and := @eq_not_self_and
@[deprecated eq_and_not_self (since := "2025-04-04")]
abbrev iff_and_not_self := @eq_and_not_self
/-! ### or -/
@[simp] theorem or_self_left : (a b : Bool), (a || (a || b)) = (a || b) := by decide
@@ -169,35 +145,11 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
@[simp] theorem eq_self_or : {a b : Bool}, (a = (a || b)) (b a) := by decide
@[simp] theorem eq_or_self : {a b : Bool}, (b = (a || b)) (a b) := by decide
@[deprecated or_eq_left_iff_imp (since := "2025-04-04")]
abbrev or_iff_left_iff_imp := @or_eq_left_iff_imp
@[deprecated or_eq_right_iff_imp (since := "2025-04-04")]
abbrev or_iff_right_iff_imp := @or_eq_right_iff_imp
@[deprecated eq_self_or (since := "2025-04-04")]
abbrev iff_self_or := @eq_self_or
@[deprecated eq_or_self (since := "2025-04-04")]
abbrev iff_or_self := @eq_or_self
@[simp] theorem not_or_eq_left_iff_and : {a b : Bool}, ((!a || b) = a) a b := by decide
@[simp] theorem or_not_eq_right_iff_and : {a b : Bool}, ((a || !b) = b) a b := by decide
@[simp] theorem eq_not_self_or : {a b : Bool}, (a = (!a || b)) a b := by decide
@[simp] theorem eq_or_not_self : {a b : Bool}, (b = (a || !b)) a b := by decide
@[deprecated not_or_eq_left_iff_and (since := "2025-04-04")]
abbrev not_or_iff_left_iff_imp := @not_or_eq_left_iff_and
@[deprecated or_not_eq_right_iff_and (since := "2025-04-04")]
abbrev or_not_iff_right_iff_imp := @or_not_eq_right_iff_and
@[deprecated eq_not_self_or (since := "2025-04-04")]
abbrev iff_not_self_or := @eq_not_self_or
@[deprecated eq_or_not_self (since := "2025-04-04")]
abbrev iff_or_not_self := @eq_or_not_self
theorem or_comm : (x y : Bool), (x || y) = (y || x) := by decide
instance : Std.Commutative (· || ·) := or_comm
@@ -562,6 +514,7 @@ theorem exists_bool {p : Bool → Prop} : (∃ b, p b) ↔ p false p true :=
theorem cond_eq_ite {α} (b : Bool) (t e : α) : cond b t e = if b then t else e := by
cases b <;> simp
@[deprecated cond_eq_ite (since := "2025-10-29")]
theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := cond_eq_ite b x y
@[simp] theorem cond_not (b : Bool) (t e : α) : cond (!b) t e = cond b e t := by
@@ -621,11 +574,6 @@ protected theorem cond_false {α : Sort u} {a b : α} : cond false a b = b := co
@[simp] theorem cond_then_self : (c b : Bool), cond c c b = (c || b) := by decide
@[simp] theorem cond_else_self : (c b : Bool), cond c b c = (c && b) := by decide
@[deprecated cond_then_not_self (since := "2025-04-04")] abbrev cond_true_not_same := @cond_then_not_self
@[deprecated cond_else_not_self (since := "2025-04-04")] abbrev cond_false_not_same := @cond_else_not_self
@[deprecated cond_then_self (since := "2025-04-04")] abbrev cond_true_same := @cond_then_self
@[deprecated cond_else_self (since := "2025-04-04")] abbrev cond_false_same := @cond_else_self
theorem cond_pos {b : Bool} {a a' : α} (h : b = true) : (bif b then a else a') = a := by
rw [h, cond_true]
@@ -665,7 +613,7 @@ theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidab
end Bool
export Bool (cond_eq_if xor and or not)
export Bool (cond_eq_if cond_eq_ite xor and or not)
/-! ### decide -/

View File

@@ -24,9 +24,6 @@ attribute [ext] ByteArray
instance : DecidableEq ByteArray :=
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
instance : Inhabited ByteArray where
default := empty

View File

@@ -10,6 +10,8 @@ public import Init.Data.ByteArray.Basic
public section
namespace ByteArray
-- At present the preferred normal form for empty byte arrays is `ByteArray.empty`
@[simp]
theorem emptyc_eq_empty : ( : ByteArray) = ByteArray.empty := rfl
@@ -18,10 +20,10 @@ theorem emptyc_eq_empty : (∅ : ByteArray) = ByteArray.empty := rfl
theorem emptyWithCapacity_eq_empty : ByteArray.emptyWithCapacity 0 = ByteArray.empty := rfl
@[simp]
theorem ByteArray.data_empty : ByteArray.empty.data = #[] := rfl
theorem data_empty : ByteArray.empty.data = #[] := rfl
@[simp]
theorem ByteArray.data_extract {a : ByteArray} {b e : Nat} :
theorem data_extract {a : ByteArray} {b e : Nat} :
(a.extract b e).data = a.data.extract b e := by
simp [extract, copySlice]
by_cases b e
@@ -29,39 +31,39 @@ theorem ByteArray.data_extract {a : ByteArray} {b e : Nat} :
· rw [Array.extract_eq_empty_of_le (by omega), Array.extract_eq_empty_of_le (by omega)]
@[simp]
theorem ByteArray.extract_zero_size {b : ByteArray} : b.extract 0 b.size = b := by
theorem extract_zero_size {b : ByteArray} : b.extract 0 b.size = b := by
ext1
simp
@[simp]
theorem ByteArray.extract_same {b : ByteArray} {i : Nat} : b.extract i i = ByteArray.empty := by
theorem extract_same {b : ByteArray} {i : Nat} : b.extract i i = ByteArray.empty := by
ext1
simp [Nat.min_le_left]
theorem ByteArray.fastAppend_eq_copySlice {a b : ByteArray} :
theorem fastAppend_eq_copySlice {a b : ByteArray} :
a.fastAppend b = b.copySlice 0 a a.size b.size false := rfl
@[simp]
theorem List.toByteArray_append {l l' : List UInt8} :
theorem _root_.List.toByteArray_append {l l' : List UInt8} :
(l ++ l').toByteArray = l.toByteArray ++ l'.toByteArray := by
simp [List.toByteArray_append']
@[simp]
theorem ByteArray.toList_data_append {l l' : ByteArray} :
theorem toList_data_append {l l' : ByteArray} :
(l ++ l').data.toList = l.data.toList ++ l'.data.toList := by
simp [ append_eq]
@[simp]
theorem ByteArray.data_append {l l' : ByteArray} :
theorem data_append {l l' : ByteArray} :
(l ++ l').data = l.data ++ l'.data := by
simp [ Array.toList_inj]
@[simp]
theorem ByteArray.size_empty : ByteArray.empty.size = 0 := by
theorem size_empty : ByteArray.empty.size = 0 := by
simp [ ByteArray.size_data]
@[simp]
theorem List.data_toByteArray {l : List UInt8} :
theorem _root_.List.data_toByteArray {l : List UInt8} :
l.toByteArray.data = l.toArray := by
rw [List.toByteArray]
suffices a b, (List.toByteArray.loop a b).data = b.data ++ a.toArray by
@@ -70,153 +72,159 @@ theorem List.data_toByteArray {l : List UInt8} :
fun_induction List.toByteArray.loop a b with simp_all
@[simp]
theorem List.size_toByteArray {l : List UInt8} :
theorem _root_.List.size_toByteArray {l : List UInt8} :
l.toByteArray.size = l.length := by
simp [ ByteArray.size_data]
@[simp]
theorem List.toByteArray_nil : List.toByteArray [] = ByteArray.empty := rfl
theorem _root_.List.toByteArray_nil : List.toByteArray [] = ByteArray.empty := rfl
@[simp]
theorem ByteArray.empty_append {b : ByteArray} : ByteArray.empty ++ b = b := by
theorem empty_append {b : ByteArray} : ByteArray.empty ++ b = b := by
ext1
simp
@[simp]
theorem ByteArray.append_empty {b : ByteArray} : b ++ ByteArray.empty = b := by
theorem append_empty {b : ByteArray} : b ++ ByteArray.empty = b := by
ext1
simp
@[simp, grind =]
theorem ByteArray.size_append {a b : ByteArray} : (a ++ b).size = a.size + b.size := by
theorem size_append {a b : ByteArray} : (a ++ b).size = a.size + b.size := by
simp [ size_data]
@[simp]
theorem ByteArray.size_eq_zero_iff {a : ByteArray} : a.size = 0 a = ByteArray.empty := by
theorem size_eq_zero_iff {a : ByteArray} : a.size = 0 a = ByteArray.empty := by
refine fun h => ?_, fun h => h ByteArray.size_empty
ext1
simp [ Array.size_eq_zero_iff, h]
theorem ByteArray.getElem_eq_getElem_data {a : ByteArray} {i : Nat} {h : i < a.size} :
theorem getElem_eq_getElem_data {a : ByteArray} {i : Nat} {h : i < a.size} :
a[i] = a.data[i]'(by simpa [ size_data]) := rfl
@[simp]
theorem ByteArray.getElem_append_left {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
theorem getElem_append_left {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
(hlt : i < a.size) : (a ++ b)[i] = a[i] := by
simp only [getElem_eq_getElem_data, data_append]
rw [Array.getElem_append_left (by simpa)]
theorem ByteArray.getElem_append_right {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
theorem getElem_append_right {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
(hle : a.size i) : (a ++ b)[i] = b[i - a.size]'(by simp_all; omega) := by
simp only [getElem_eq_getElem_data, data_append]
rw [Array.getElem_append_right (by simpa)]
simp
@[simp]
theorem List.getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.toByteArray.size} :
theorem _root_.List.getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.toByteArray.size} :
l.toByteArray[i]'h = l[i]'(by simp_all) := by
simp [ByteArray.getElem_eq_getElem_data]
theorem List.getElem_eq_getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.length} :
theorem _root_.List.getElem_eq_getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.length} :
l[i]'h = l.toByteArray[i]'(by simp_all) := by
simp
@[simp]
theorem ByteArray.size_extract {a : ByteArray} {b e : Nat} :
theorem size_extract {a : ByteArray} {b e : Nat} :
(a.extract b e).size = min e a.size - b := by
simp [ size_data]
@[simp]
theorem ByteArray.extract_eq_empty_iff {b : ByteArray} {i j : Nat} : b.extract i j = ByteArray.empty min j b.size i := by
theorem extract_eq_empty_iff {b : ByteArray} {i j : Nat} : b.extract i j = ByteArray.empty min j b.size i := by
rw [ size_eq_zero_iff, size_extract]
omega
@[simp]
theorem ByteArray.extract_add_left {b : ByteArray} {i j : Nat} : b.extract (i + j) i = ByteArray.empty := by
theorem extract_add_left {b : ByteArray} {i j : Nat} : b.extract (i + j) i = ByteArray.empty := by
simp only [extract_eq_empty_iff]
exact Nat.le_trans (Nat.min_le_left _ _) (by simp)
@[simp]
theorem ByteArray.append_eq_empty_iff {a b : ByteArray} :
theorem append_eq_empty_iff {a b : ByteArray} :
a ++ b = ByteArray.empty a = ByteArray.empty b = ByteArray.empty := by
simp [ size_eq_zero_iff, size_append]
@[simp]
theorem List.toByteArray_eq_empty {l : List UInt8} :
theorem toByteArray_eq_empty {l : List UInt8} :
l.toByteArray = ByteArray.empty l = [] := by
simp [ ByteArray.size_eq_zero_iff]
theorem ByteArray.append_right_inj {ys₁ ys₂ : ByteArray} (xs : ByteArray) :
@[simp]
theorem append_right_inj {ys₁ ys₂ : ByteArray} (xs : ByteArray) :
xs ++ ys₁ = xs ++ ys₂ ys₁ = ys₂ := by
simp [ByteArray.ext_iff, Array.append_right_inj]
@[simp]
theorem ByteArray.extract_append_extract {a : ByteArray} {i j k : Nat} :
theorem append_left_inj {xs₁ xs₂ : ByteArray} (ys : ByteArray) :
xs₁ ++ ys = xs₂ ++ ys xs₁ = xs₂ := by
simp [ByteArray.ext_iff, Array.append_left_inj]
@[simp]
theorem extract_append_extract {a : ByteArray} {i j k : Nat} :
a.extract i j ++ a.extract j k = a.extract (min i j) (max j k) := by
ext1
simp
theorem ByteArray.extract_eq_extract_append_extract {a : ByteArray} {i k : Nat} (j : Nat)
theorem extract_eq_extract_append_extract {a : ByteArray} {i k : Nat} (j : Nat)
(hi : i j) (hk : j k) :
a.extract i k = a.extract i j ++ a.extract j k := by
simp
rw [Nat.min_eq_left hi, Nat.max_eq_right hk]
theorem ByteArray.append_inj_left {xs₁ xs₂ ys₁ ys₂ : ByteArray} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) : xs₁ = xs₂ := by
theorem append_inj_left {xs₁ xs₂ ys₁ ys₂ : ByteArray} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) : xs₁ = xs₂ := by
simp only [ByteArray.ext_iff, ByteArray.size_data, ByteArray.data_append] at *
exact Array.append_inj_left h hl
theorem ByteArray.extract_append_eq_right {a b : ByteArray} {i j : Nat} (hi : i = a.size) (hj : j = a.size + b.size) :
theorem extract_append_eq_right {a b : ByteArray} {i j : Nat} (hi : i = a.size) (hj : j = a.size + b.size) :
(a ++ b).extract i j = b := by
subst hi hj
ext1
simp [ size_data]
theorem ByteArray.extract_append_eq_left {a b : ByteArray} {i : Nat} (hi : i = a.size) :
theorem extract_append_eq_left {a b : ByteArray} {i : Nat} (hi : i = a.size) :
(a ++ b).extract 0 i = a := by
subst hi
ext1
simp
theorem ByteArray.extract_append_size_left {a b : ByteArray} {i : Nat} :
theorem extract_append_size_left {a b : ByteArray} {i : Nat} :
(a ++ b).extract i a.size = a.extract i a.size := by
ext1
simp
theorem ByteArray.extract_append_size_add {a b : ByteArray} {i j : Nat} :
theorem extract_append_size_add {a b : ByteArray} {i j : Nat} :
(a ++ b).extract (a.size + i) (a.size + j) = b.extract i j := by
ext1
simp
theorem ByteArray.extract_append {as bs : ByteArray} {i j : Nat} :
theorem extract_append {as bs : ByteArray} {i j : Nat} :
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
ext1
simp
theorem ByteArray.extract_append_size_add' {a b : ByteArray} {i j k : Nat} (h : k = a.size) :
theorem extract_append_size_add' {a b : ByteArray} {i j k : Nat} (h : k = a.size) :
(a ++ b).extract (k + i) (k + j) = b.extract i j := by
cases h
rw [extract_append_size_add]
theorem ByteArray.extract_extract {a : ByteArray} {i j k l : Nat} :
theorem extract_extract {a : ByteArray} {i j k l : Nat} :
(a.extract i j).extract k l = a.extract (i + k) (min (i + l) j) := by
ext1
simp
theorem ByteArray.getElem_extract_aux {xs : ByteArray} {start stop : Nat} (h : i < (xs.extract start stop).size) :
theorem getElem_extract_aux {xs : ByteArray} {start stop : Nat} (h : i < (xs.extract start stop).size) :
start + i < xs.size := by
rw [size_extract] at h; apply Nat.add_lt_of_lt_sub'; apply Nat.lt_of_lt_of_le h
apply Nat.sub_le_sub_right; apply Nat.min_le_right
theorem ByteArray.getElem_extract {i : Nat} {b : ByteArray} {start stop : Nat}
theorem getElem_extract {i : Nat} {b : ByteArray} {start stop : Nat}
(h) : (b.extract start stop)[i]'h = b[start + i]'(getElem_extract_aux h) := by
simp [getElem_eq_getElem_data]
theorem ByteArray.extract_eq_extract_left {a : ByteArray} {i i' j : Nat} :
theorem extract_eq_extract_left {a : ByteArray} {i i' j : Nat} :
a.extract i j = a.extract i' j min j a.size - i = min j a.size - i' := by
simp [ByteArray.ext_iff, Array.extract_eq_extract_left]
theorem ByteArray.extract_add_one {a : ByteArray} {i : Nat} (ha : i + 1 a.size) :
theorem extract_add_one {a : ByteArray} {i : Nat} (ha : i + 1 a.size) :
a.extract i (i + 1) = [a[i]].toByteArray := by
ext
· simp
@@ -225,34 +233,57 @@ theorem ByteArray.extract_add_one {a : ByteArray} {i : Nat} (ha : i + 1 ≤ a.si
obtain rfl : j = 0 := by simpa using hj'
simp [ByteArray.getElem_eq_getElem_data]
theorem ByteArray.extract_add_two {a : ByteArray} {i : Nat} (ha : i + 2 a.size) :
theorem extract_add_two {a : ByteArray} {i : Nat} (ha : i + 2 a.size) :
a.extract i (i + 2) = [a[i], a[i + 1]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_one (by omega)]
simp [ List.toByteArray_append]
theorem ByteArray.extract_add_three {a : ByteArray} {i : Nat} (ha : i + 3 a.size) :
theorem extract_add_three {a : ByteArray} {i : Nat} (ha : i + 3 a.size) :
a.extract i (i + 3) = [a[i], a[i + 1], a[i + 2]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_two (by omega)]
simp [ List.toByteArray_append]
theorem ByteArray.extract_add_four {a : ByteArray} {i : Nat} (ha : i + 4 a.size) :
theorem extract_add_four {a : ByteArray} {i : Nat} (ha : i + 4 a.size) :
a.extract i (i + 4) = [a[i], a[i + 1], a[i + 2], a[i + 3]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_three (by omega)]
simp [ List.toByteArray_append]
theorem ByteArray.append_assoc {a b c : ByteArray} : a ++ b ++ c = a ++ (b ++ c) := by
theorem append_assoc {a b c : ByteArray} : a ++ b ++ c = a ++ (b ++ c) := by
ext1
simp
@[simp]
theorem ByteArray.toList_empty : ByteArray.empty.toList = [] := by
theorem toList_empty : ByteArray.empty.toList = [] := by
simp [ByteArray.toList, ByteArray.toList.loop]
theorem ByteArray.copySlice_eq_append {src : ByteArray} {srcOff : Nat} {dest : ByteArray} {destOff len : Nat} {exact : Bool} :
theorem copySlice_eq_append {src : ByteArray} {srcOff : Nat} {dest : ByteArray} {destOff len : Nat} {exact : Bool} :
ByteArray.copySlice src srcOff dest destOff len exact =
dest.extract 0 destOff ++ src.extract srcOff (srcOff +len) ++ dest.extract (destOff + min len (src.data.size - srcOff)) dest.data.size := by
ext1
simp [copySlice]
@[simp]
theorem data_set {as : ByteArray} {i : Nat} {h : i < as.size} {a : UInt8} :
(as.set i a h).data = as.data.set i a (by simpa) := by
simp [set]
theorem set_eq_push_extract_append_extract {as : ByteArray} {i : Nat} (h : i < as.size) {a : UInt8} :
as.set i a h = (as.extract 0 i).push a ++ as.extract (i + 1) as.size := by
ext1
simpa using Array.set_eq_push_extract_append_extract _
@[simp]
theorem append_toByteArray_singleton {as : ByteArray} {a : UInt8} :
as ++ [a].toByteArray = as.push a := by
ext1
simp
@[simp]
theorem extract_zero_max_size {a : ByteArray} {i : Nat} : a.extract 0 (max i a.size) = a := by
ext1
simp [Nat.le_max_right]
end ByteArray

View File

@@ -13,12 +13,16 @@ public section
namespace Char
@[ext] protected theorem ext : {a b : Char} a.val = b.val a = b
@[deprecated Char.ext (since := "2025-10-26")]
protected theorem eq_of_val_eq : {a b : Char} a.val = b.val a = b
| _,_, _,_, rfl => rfl
theorem le_def {a b : Char} : a b a.1 b.1 := .rfl
theorem lt_def {a b : Char} : a < b a.1 < b.1 := .rfl
@[deprecated lt_def (since := "2025-10-26")]
theorem lt_iff_val_lt_val {a b : Char} : a < b a.val < b.val := Iff.rfl
@[simp] protected theorem not_le {a b : Char} : ¬ a b b < a := UInt32.not_le
@[simp] protected theorem not_lt {a b : Char} : ¬ a < b b a := UInt32.not_lt
@[simp] protected theorem le_refl (a : Char) : a a := by simp [le_def]
@@ -51,8 +55,12 @@ instance leAntisymm : Std.Antisymm (· ≤ · : Char → Char → Prop) where
antisymm _ _ := Char.le_antisymm
-- This instance is useful while setting up instances for `String`.
instance ltTrichotomous : Std.Trichotomous (· < · : Char Char Prop) where
trichotomous _ _ h₁ h₂ := Char.le_antisymm (by simpa using h₂) (by simpa using h₁)
@[deprecated ltTrichotomous (since := "2025-10-27")]
def notLTAntisymm : Std.Antisymm (¬ · < · : Char Char Prop) where
antisymm _ _ h₁ h₂ := Char.le_antisymm (by simpa using h₂) (by simpa using h₁)
antisymm := Char.ltTrichotomous.trichotomous
instance ltAsymm : Std.Asymm (· < · : Char Char Prop) where
asymm _ _ := Char.lt_asymm
@@ -69,4 +77,9 @@ def notLTTotal : Std.Total (¬ · < · : Char → Char → Prop) where
rw [Char.ofNat, dif_pos]
rfl
@[simp]
theorem toUInt8_val {c : Char} : c.val.toUInt8 = c.toUInt8 := rfl
theorem toString_eq_singleton {c : Char} : c.toString = String.singleton c := rfl
end Char

View File

@@ -287,7 +287,7 @@ theorem toRat_add (x y : Dyadic) : toRat (x + y) = toRat x + toRat y := by
· rename_i h
cases Int.sub_eq_iff_eq_add.mp h
rw [toRat_ofOdd_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [succ_eq_add_one, Int.ofNat_eq_coe, Int.add_shiftLeft, Int.shiftLeft_add,
simp only [succ_eq_add_one, Int.ofNat_eq_natCast, Int.add_shiftLeft, Int.shiftLeft_add,
Int.natCast_mul, Int.natCast_shiftLeft, Int.shiftLeft_mul_shiftLeft, Int.add_mul]
congr 2 <;> omega
· rename_i h
@@ -438,13 +438,13 @@ theorem toDyadic_mkRat (a : Int) (b : Nat) (prec : Int) :
rcases h : mkRat a b with n, d, hnz, hr
obtain m, hm, rfl, rfl := Rat.mkRat_num_den hb h
cases prec
· simp only [Rat.toDyadic, Int.ofNat_eq_coe, Int.toNat_natCast, Int.toNat_neg_natCast,
· simp only [Rat.toDyadic, Int.ofNat_eq_natCast, Int.toNat_natCast, Int.toNat_neg_natCast,
shiftLeft_zero, Int.natCast_mul]
rw [Int.mul_comm d, Int.ediv_ediv (by simp), Int.shiftLeft_mul,
rw [Int.mul_comm d, Int.ediv_ediv_of_nonneg (by simp), Int.shiftLeft_mul,
Int.mul_ediv_cancel _ (by simpa using hm)]
· simp only [Rat.toDyadic, Int.natCast_shiftLeft, Int.negSucc_eq, Int.natCast_add_one,
Int.toNat_neg_natCast, Int.shiftLeft_zero, Int.neg_neg, Int.toNat_natCast, Int.natCast_mul]
rw [Int.mul_comm d, Int.mul_shiftLeft, Int.ediv_ediv (by simp),
rw [Int.mul_comm d, Int.mul_shiftLeft, Int.ediv_ediv_of_nonneg (by simp),
Int.mul_ediv_cancel _ (by simpa using hm)]
theorem toDyadic_eq_ofIntWithPrec (x : Rat) (prec : Int) :
@@ -463,7 +463,7 @@ theorem toRat_toDyadic (x : Rat) (prec : Int) :
rw [Rat.floor_def, Int.shiftLeft_eq, Nat.shiftLeft_eq]
match prec with
| .ofNat prec =>
simp only [Int.ofNat_eq_coe, Int.toNat_natCast, Int.toNat_neg_natCast, Nat.pow_zero,
simp only [Int.ofNat_eq_natCast, Int.toNat_natCast, Int.toNat_neg_natCast, Nat.pow_zero,
Nat.mul_one]
have : (2 ^ prec : Rat) = ((2 ^ prec : Nat) : Rat) := by simp
rw [Rat.zpow_natCast, this, Rat.mul_def']
@@ -472,7 +472,7 @@ theorem toRat_toDyadic (x : Rat) (prec : Int) :
Rat.den_ofNat, Nat.one_pow, Nat.mul_one]
split
· simp_all
· rw [Int.ediv_ediv (Int.ofNat_zero_le _)]
· rw [Int.ediv_ediv_of_nonneg (Int.natCast_nonneg _)]
congr 1
rw [Int.natCast_ediv, Int.mul_ediv_cancel']
rw [Int.natCast_dvd_natCast]
@@ -495,7 +495,7 @@ theorem toRat_toDyadic (x : Rat) (prec : Int) :
simp only [this, Int.mul_one]
split
· simp_all
· rw [Int.ediv_ediv (Int.ofNat_zero_le _)]
· rw [Int.ediv_ediv_of_nonneg (Int.natCast_nonneg _)]
congr 1
rw [Int.natCast_ediv, Int.mul_ediv_cancel']
· simp
@@ -682,9 +682,11 @@ instance : LE Dyadic where
instance : DecidableLT Dyadic := fun _ _ => inferInstanceAs (Decidable (_ = true))
instance : DecidableLE Dyadic := fun _ _ => inferInstanceAs (Decidable (_ = true))
theorem lt_iff_toRat {x y : Dyadic} : x < y x.toRat < y.toRat := blt_iff_toRat
@[simp]
theorem toRat_lt_toRat_iff {x y : Dyadic} : x.toRat < y.toRat x < y := blt_iff_toRat.symm
theorem le_iff_toRat {x y : Dyadic} : x y x.toRat y.toRat := ble_iff_toRat
@[simp]
theorem toRat_le_toRat_iff {x y : Dyadic} : x.toRat y.toRat x y := ble_iff_toRat.symm
@[simp]
protected theorem not_le {x y : Dyadic} : ¬x < y y x := by
@@ -696,20 +698,20 @@ protected theorem not_lt {x y : Dyadic} : ¬x ≤ y ↔ y < x := by
@[simp]
protected theorem le_refl (x : Dyadic) : x x := by
rw [le_iff_toRat]
rw [ toRat_le_toRat_iff]
exact Rat.le_refl
protected theorem le_trans {x y z : Dyadic} (h : x y) (h' : y z) : x z := by
rw [le_iff_toRat] at h h'
rw [ toRat_le_toRat_iff] at h h'
exact Rat.le_trans h h'
protected theorem le_antisymm {x y : Dyadic} (h : x y) (h' : y x) : x = y := by
rw [le_iff_toRat] at h h'
rw [ toRat_le_toRat_iff] at h h'
rw [ toRat_inj]
exact Rat.le_antisymm h h'
protected theorem le_total (x y : Dyadic) : x y y x := by
rw [le_iff_toRat, le_iff_toRat]
rw [ toRat_le_toRat_iff, toRat_le_toRat_iff]
exact Rat.le_total
instance : Std.LawfulOrderLT Dyadic where

View File

@@ -52,8 +52,8 @@ instance : NoNatZeroDivisors Dyadic where
instance : OrderedRing Dyadic where
zero_lt_one := by decide
add_le_left_iff _ := by simp [le_iff_toRat, Rat.add_le_add_right]
mul_lt_mul_of_pos_left {_ _ _} := by simpa [lt_iff_toRat] using Rat.mul_lt_mul_of_pos_left
mul_lt_mul_of_pos_right {_ _ _} := by simpa [lt_iff_toRat] using Rat.mul_lt_mul_of_pos_right
add_le_left_iff _ := by simp [ toRat_le_toRat_iff, Rat.add_le_add_right]
mul_lt_mul_of_pos_left {_ _ _} := by simpa [ toRat_lt_toRat_iff] using Rat.mul_lt_mul_of_pos_left
mul_lt_mul_of_pos_right {_ _ _} := by simpa [ toRat_lt_toRat_iff] using Rat.mul_lt_mul_of_pos_right
end Dyadic

View File

@@ -27,7 +27,7 @@ def invAtPrec (x : Dyadic) (prec : Int) : Dyadic :=
/-- For a positive dyadic `x`, `invAtPrec x prec * x ≤ 1`. -/
theorem invAtPrec_mul_le_one {x : Dyadic} (hx : 0 < x) (prec : Int) :
invAtPrec x prec * x 1 := by
rw [le_iff_toRat]
rw [ toRat_le_toRat_iff]
rw [toRat_mul]
rw [show (1 : Dyadic).toRat = (1 : Rat) from rfl]
unfold invAtPrec
@@ -39,19 +39,19 @@ theorem invAtPrec_mul_le_one {x : Dyadic} (hx : 0 < x) (prec : Int) :
simp only
have h_le : ((ofOdd n k hn).toRat.inv.toDyadic prec).toRat (ofOdd n k hn).toRat.inv := Rat.toRat_toDyadic_le
have h_pos : 0 (ofOdd n k hn).toRat := by
rw [lt_iff_toRat, toRat_zero] at hx
rw [ toRat_lt_toRat_iff, toRat_zero] at hx
exact Rat.le_of_lt hx
calc ((ofOdd n k hn).toRat.inv.toDyadic prec).toRat * (ofOdd n k hn).toRat
(ofOdd n k hn).toRat.inv * (ofOdd n k hn).toRat := Rat.mul_le_mul_of_nonneg_right h_le h_pos
_ = 1 := by
apply Rat.inv_mul_cancel
rw [lt_iff_toRat, toRat_zero] at hx
rw [ toRat_lt_toRat_iff, toRat_zero] at hx
exact Rat.ne_of_gt hx
/-- For a positive dyadic `x`, `1 < (invAtPrec x prec + 2^(-prec)) * x`. -/
theorem one_lt_invAtPrec_add_inc_mul {x : Dyadic} (hx : 0 < x) (prec : Int) :
1 < (invAtPrec x prec + ofIntWithPrec 1 prec) * x := by
rw [lt_iff_toRat]
rw [ toRat_lt_toRat_iff]
rw [toRat_mul]
rw [show (1 : Dyadic).toRat = (1 : Rat) from rfl]
unfold invAtPrec
@@ -64,12 +64,12 @@ theorem one_lt_invAtPrec_add_inc_mul {x : Dyadic} (hx : 0 < x) (prec : Int) :
have h_le : (ofOdd n k hn).toRat.inv < ((ofOdd n k hn).toRat.inv.toDyadic prec + ofIntWithPrec 1 prec).toRat :=
Rat.lt_toRat_toDyadic_add
have h_pos : 0 < (ofOdd n k hn).toRat := by
rwa [lt_iff_toRat, toRat_zero] at hx
rwa [ toRat_lt_toRat_iff, toRat_zero] at hx
calc
1 = (ofOdd n k hn).toRat.inv * (ofOdd n k hn).toRat := by
symm
apply Rat.inv_mul_cancel
rw [lt_iff_toRat, toRat_zero] at hx
rw [ toRat_lt_toRat_iff, toRat_zero] at hx
exact Rat.ne_of_gt hx
_ < ((ofOdd n k hn).toRat.inv.toDyadic prec + ofIntWithPrec 1 prec).toRat * (ofOdd n k hn).toRat :=
Rat.mul_lt_mul_of_pos_right h_le h_pos

View File

@@ -28,7 +28,7 @@ theorem roundDown_le {x : Dyadic} {prec : Int} : roundDown x prec ≤ x :=
match h : k - prec with
| .ofNat l =>
dsimp
rw [ofOdd_eq_ofIntWithPrec, le_iff_toRat]
rw [ofOdd_eq_ofIntWithPrec, toRat_le_toRat_iff]
replace h : k = Int.ofNat l + prec := by omega
subst h
simp only [toRat_ofIntWithPrec_eq_mul_two_pow]
@@ -36,7 +36,7 @@ theorem roundDown_le {x : Dyadic} {prec : Int} : roundDown x prec ≤ x :=
refine Lean.Grind.OrderedRing.mul_le_mul_of_nonneg_right ?_ (Rat.zpow_nonneg (by decide))
rw [Int.shiftRight_eq_div_pow]
rw [ Lean.Grind.Field.IsOrdered.mul_le_mul_iff_of_pos_right (c := 2^(Int.ofNat l)) (Rat.zpow_pos (by decide))]
simp only [Int.natCast_pow, Int.cast_ofNat_Int, Int.ofNat_eq_coe]
simp only [Int.natCast_pow, Int.cast_ofNat_Int, Int.ofNat_eq_natCast]
rw [Rat.mul_assoc, Rat.zpow_add (by decide), Int.add_left_neg, Rat.zpow_zero, Rat.mul_one]
have : (2 : Rat) ^ (l : Int) = (2 ^ l : Int) := by
rw [Rat.zpow_natCast, Rat.intCast_pow, Rat.intCast_ofNat]

View File

@@ -157,6 +157,7 @@ theorem le_def {a b : Fin n} : a ≤ b ↔ a.1 ≤ b.1 := .rfl
theorem lt_def {a b : Fin n} : a < b a.1 < b.1 := .rfl
@[deprecated lt_def (since := "2025-10-26")]
theorem lt_iff_val_lt_val {a b : Fin n} : a < b a.val < b.val := Iff.rfl
@[simp] protected theorem not_le {a b : Fin n} : ¬ a b b < a := Nat.not_le
@@ -264,7 +265,7 @@ instance : LawfulOrderLT (Fin n) where
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@[simp] theorem rev_le_rev {i j : Fin n} : rev i rev j j i := by
simp only [le_def, val_rev, Nat.sub_le_sub_iff_left (Nat.succ_le.2 j.is_lt)]
simp only [le_def, val_rev, Nat.sub_le_sub_iff_left (Nat.succ_le_iff.2 j.is_lt)]
exact Nat.succ_le_succ_iff
@[simp] theorem rev_inj {i j : Fin n} : rev i = rev j i = j :=
@@ -383,9 +384,10 @@ theorem add_one_pos (i : Fin (n + 1)) (h : i < Fin.last n) : (0 : Fin (n + 1)) <
rw [Fin.lt_def, val_add, val_zero, val_one, Nat.mod_eq_of_lt h]
exact Nat.zero_lt_succ _
@[deprecated zero_lt_one (since := "2025-10-26")]
theorem one_pos : (0 : Fin (n + 2)) < 1 := Nat.succ_pos 0
theorem zero_ne_one : (0 : Fin (n + 2)) 1 := Fin.ne_of_lt one_pos
theorem zero_ne_one : (0 : Fin (n + 2)) 1 := Fin.ne_of_lt zero_lt_one
/-! ### succ and casts into larger Fin types -/
@@ -540,12 +542,12 @@ theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
@[simp] theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[simp] theorem castSucc_mk (n i : Nat) (h : i < n) : castSucc i, h = i, Nat.lt.step h := rfl
@[simp] theorem castSucc_mk (n i : Nat) (h : i < n) : castSucc i, h = i, Nat.lt_succ_of_lt h := rfl
@[simp] theorem cast_castSucc {n' : Nat} {h : n + 1 = n' + 1} {i : Fin n} :
i.castSucc.cast h = (i.cast (Nat.succ.inj h)).castSucc := rfl
theorem castSucc_lt_succ (i : Fin n) : i.castSucc < i.succ :=
theorem castSucc_lt_succ {i : Fin n} : i.castSucc < i.succ :=
lt_def.2 <| by simp only [coe_castSucc, val_succ, Nat.lt_succ_self]
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i j.castSucc i < j.succ := by
@@ -585,8 +587,12 @@ theorem castSucc_pos [NeZero n] {i : Fin n} (h : 0 < i) : 0 < i.castSucc := by
theorem castSucc_ne_zero_iff [NeZero n] {a : Fin n} : a.castSucc 0 a 0 :=
not_congr <| castSucc_eq_zero_iff
@[simp, grind _=_]
theorem castSucc_succ (i : Fin n) : i.succ.castSucc = i.castSucc.succ := rfl
@[deprecated castSucc_succ (since := "2025-10-29")]
theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
j.succ.castSucc = (j.castSucc).succ := by simp [Fin.ext_iff]
j.succ.castSucc = (j.castSucc).succ := by simp
@[simp]
theorem coeSucc_eq_succ {a : Fin n} : a.castSucc + 1 = a.succ := by
@@ -594,6 +600,7 @@ theorem coeSucc_eq_succ {a : Fin n} : a.castSucc + 1 = a.succ := by
· exact a.elim0
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
@[deprecated castSucc_lt_succ (since := "2025-10-29")]
theorem lt_succ {a : Fin n} : a.castSucc < a.succ := by
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
@@ -697,9 +704,6 @@ 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
@@ -754,7 +758,7 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
| 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
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt zero_lt_one)) = 0 := rfl
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
@@ -1137,6 +1141,7 @@ theorem mul_ofNat [NeZero n] (x : Fin n) (y : Nat) :
theorem val_mul {n : Nat} : a b : Fin n, (a * b).val = a.val * b.val % n
| _, _, _, _ => rfl
@[deprecated val_mul (since := "2025-10-26")]
theorem coe_mul {n : Nat} : a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
| _, _, _, _ => rfl

View File

@@ -29,9 +29,6 @@ attribute [ext] FloatArray
def emptyWithCapacity (c : @& Nat) : FloatArray :=
{ data := #[] }
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
def empty : FloatArray :=
emptyWithCapacity 0

View File

@@ -170,7 +170,7 @@ private def spaceUptoLine : Format → Bool → Int → Nat → SpaceResult
| text s, flatten, _, _ =>
let p := String.Internal.posOf s '\n'
let off := String.Internal.offsetOfPos s p
{ foundLine := p != s.endPos, foundFlattenedHardLine := flatten && p != s.endPos, space := off }
{ foundLine := p != s.rawEndPos, foundFlattenedHardLine := flatten && p != s.rawEndPos, space := off }
| append f₁ f₂, flatten, m, w => merge w (spaceUptoLine f₁ flatten m w) (spaceUptoLine f₂ flatten m)
| nest n f, flatten, m, w => spaceUptoLine f flatten (m - n) w
| group f _, _, m, w => spaceUptoLine f true m w
@@ -264,14 +264,14 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
| nest n f => be w (gs' ({ i with f, indent := i.indent + n }::is))
| text s =>
let p := String.Internal.posOf s '\n'
if p == s.endPos then
if p == s.rawEndPos then
pushOutput s
endTags i.activeTags
be w (gs' is)
else
pushOutput (String.Internal.extract s {} p)
pushNewline i.indent.toNat
let is := { i with f := text (String.Internal.extract s (String.Internal.next s p) s.endPos) }::is
let is := { i with f := text (String.Internal.extract s (String.Internal.next s p) s.rawEndPos) }::is
-- after a hard line break, re-evaluate whether to flatten the remaining group
-- note that we shouldn't start flattening after a hard break outside a group
if g.fla == .disallow then
@@ -411,7 +411,6 @@ Renders a `Format` to a string.
* `column`: begin the first line wrap `column` characters earlier than usual
(this is useful when the output String will be printed starting at `column`)
-/
@[export lean_format_pretty]
def pretty (f : Format) (width : Nat := defWidth) (indent : Nat := 0) (column := 0) : String :=
let act : StateM State Unit := prettyM f width indent
State.out <| act (State.mk "" column) |>.snd

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Data.Array.Basic
import Init.Data.String.Basic
import Init.Data.String.Search
public section
@@ -47,7 +47,7 @@ Converts a string to a pretty-printer document, replacing newlines in the string
`Std.Format.line`.
-/
def String.toFormat (s : String) : Std.Format :=
Std.Format.joinSep (s.splitOn "\n") Std.Format.line
Std.Format.joinSep (s.split '\n').toList Std.Format.line
instance : ToFormat String.Pos.Raw where
format p := format p.byteIdx

View File

@@ -80,7 +80,10 @@ protected theorem zero_ne_one : (0 : Int) ≠ 1 := nofun
/-! ## Coercions -/
@[simp] theorem ofNat_eq_coe : Int.ofNat n = Nat.cast n := rfl
@[simp] theorem ofNat_eq_natCast (n : Nat) : Int.ofNat n = n := rfl
@[deprecated ofNat_eq_natCast (since := "2025-10-29")]
theorem ofNat_eq_coe : Int.ofNat n = Nat.cast n := rfl
@[simp] theorem ofNat_zero : ((0 : Nat) : Int) = 0 := rfl
@@ -313,7 +316,7 @@ the logical model.
Examples:
* `(7 : Int).natAbs = 7`
* `(0 : Int).natAbs = 0`
* `((-11 : Int).natAbs = 11`
* `(-11 : Int).natAbs = 11`
-/
@[extern "lean_nat_abs", expose]
def natAbs (m : @& Int) : Nat :=
@@ -369,9 +372,6 @@ def toNat? : Int → Option Nat
| (n : Nat) => some n
| -[_+1] => none
@[deprecated toNat? (since := "2025-03-11"), inherit_doc toNat?]
abbrev toNat' := toNat?
/-! ## divisibility -/
/--
@@ -392,9 +392,9 @@ Examples:
* `(0 : Int) ^ 10 = 0`
* `(-7 : Int) ^ 3 = -343`
-/
protected def pow (m : Int) : Nat Int
| 0 => 1
| succ n => Int.pow m n * m
protected def pow : Int Nat Int
| (m : Nat), n => Int.ofNat (m ^ n)
| m@-[_+1], n => if n % 2 = 0 then Int.ofNat (m.natAbs ^ n) else - Int.ofNat (m.natAbs ^ n)
instance : NatPow Int where
pow := Int.pow

View File

@@ -24,12 +24,17 @@ theorem natCast_shiftRight (n s : Nat) : n >>> s = (n : Int) >>> s := rfl
theorem negSucc_shiftRight (m n : Nat) :
-[m+1] >>> n = -[m >>>n +1] := rfl
@[grind _=_]
theorem shiftRight_add (i : Int) (m n : Nat) :
i >>> (m + n) = i >>> m >>> n := by
simp only [shiftRight_eq, Int.shiftRight]
cases i <;> simp [Nat.shiftRight_add]
grind_pattern shiftRight_add => i >>> (m + n) where
i =/= 0
grind_pattern shiftRight_add => i >>> m >>> n where
i =/= 0
theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
m >>> n = m / ((2 ^ n) : Nat) := by
simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow]
@@ -47,10 +52,10 @@ theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow]
theorem le_shiftRight_of_nonpos {n : Int} {s : Nat} (h : n 0) : n n >>> s := by
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_natCast]
split
case _ _ _ m =>
simp only [ofNat_eq_coe] at h
simp only [ofNat_eq_natCast] at h
by_cases hm : m = 0
· simp [hm]
· omega
@@ -61,14 +66,14 @@ theorem le_shiftRight_of_nonpos {n : Int} {s : Nat} (h : n ≤ 0) : n ≤ n >>>
omega
theorem shiftRight_le_of_nonneg {n : Int} {s : Nat} (h : 0 n) : n >>> s n := by
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_natCast]
split
case _ _ _ m =>
simp only [Int.ofNat_eq_coe] at h
simp only [Int.ofNat_eq_natCast] at h
by_cases hm : m = 0
· simp [hm]
· have := Nat.shiftRight_le m s
rw [ofNat_eq_coe]
rw [ofNat_eq_natCast]
omega
case _ _ _ m =>
omega
@@ -108,7 +113,7 @@ theorem shiftLeft_succ (m : Int) (n : Nat) : m <<< (n + 1) = (m <<< n) * 2 := by
change Int.shiftLeft _ _ = Int.shiftLeft _ _ * 2
match m with
| (m : Nat) =>
dsimp only [Int.shiftLeft, Int.ofNat_eq_coe]
dsimp only [Int.shiftLeft, Int.ofNat_eq_natCast]
rw [Nat.shiftLeft_succ, Nat.mul_comm, natCast_mul, ofNat_two]
| Int.negSucc m =>
dsimp only [Int.shiftLeft]

View File

@@ -9,3 +9,4 @@ prelude
public import Init.Data.Int.DivMod.Basic
public import Init.Data.Int.DivMod.Bootstrap
public import Init.Data.Int.DivMod.Lemmas
public import Init.Data.Int.DivMod.Pow

View File

@@ -118,9 +118,6 @@ instance : Mod Int where
@[simp, norm_cast] theorem natCast_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
@[deprecated natCast_ediv (since := "2025-04-17")]
theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := natCast_ediv m n
theorem ofNat_ediv_ofNat {a b : Nat} : (a / b : Int) = (a / b : Nat) := rfl
@[norm_cast]
theorem negSucc_ediv_ofNat_succ {a b : Nat} : ((-[a+1]) / (b+1) : Int) = -[a / succ b +1] := rfl

View File

@@ -38,7 +38,7 @@ protected theorem dvd_trans : ∀ {a b c : Int}, a b → b c → a c
refine fun a, ae => ?_, fun k, e => k, by rw [e, Int.natCast_mul]
match Int.le_total a 0 with
| .inl h =>
have := ae.symm Int.mul_nonpos_of_nonneg_of_nonpos (ofNat_zero_le _) h
have := ae.symm Int.mul_nonpos_of_nonneg_of_nonpos (natCast_nonneg _) h
rw [Nat.le_antisymm (ofNat_le.1 this) (Nat.zero_le _)]
apply Nat.dvd_zero
| .inr h => match a, eq_ofNat_of_zero_le h with
@@ -92,9 +92,6 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) z ↔ n z.natA
@[simp, norm_cast] theorem natCast_emod (m n : Nat) : ((m % n) : Int) = m % n := rfl
@[deprecated natCast_emod (since := "2025-04-17")]
theorem ofNat_emod (m n : Nat) : ((m % n) : Int) = m % n := natCast_emod m n
/-! ### mod definitions -/
theorem emod_add_mul_ediv : a b : Int, a % b + b * (a / b) = a
@@ -209,7 +206,7 @@ theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 ≤ a / b ↔ 0 ≤ a
/-! ### emod -/
theorem emod_nonneg : (a : Int) {b : Int}, b 0 0 a % b
| ofNat _, _, _ => ofNat_zero_le _
| ofNat _, _, _ => natCast_nonneg _
| -[_+1], _, H => Int.sub_nonneg_of_le <| ofNat_le.2 <| Nat.mod_lt _ (natAbs_pos.2 H)
theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
@@ -233,10 +230,6 @@ theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
@[simp] theorem mul_add_emod_self_left (a b c : Int) : (a * b + c) % a = c % a := by
rw [Int.add_comm, add_mul_emod_self_left]
@[deprecated add_mul_emod_self_right (since := "2025-04-11")]
theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
add_mul_emod_self_right ..
@[simp] theorem emod_add_emod (m n k : Int) : (m % n + k) % n = (m + k) % n := by
have := (add_mul_emod_self_left (m % n + k) n (m / n)).symm
rwa [Int.add_right_comm, emod_add_mul_ediv] at this

View File

@@ -73,7 +73,7 @@ protected theorem dvd_iff_dvd_of_dvd_add {a b c : Int} (H : a b + c) : a
theorem le_of_dvd {a b : Int} (bpos : 0 < b) (H : a b) : a b :=
match a, b, eq_succ_of_zero_lt bpos, H with
| ofNat _, _, n, rfl, H => ofNat_le.2 <| Nat.le_of_dvd n.succ_pos <| ofNat_dvd.1 H
| -[_+1], _, _, rfl, _ => Int.le_trans (Int.le_of_lt <| negSucc_lt_zero _) (ofNat_zero_le _)
| -[_+1], _, _, rfl, _ => Int.le_trans (Int.le_of_lt <| negSucc_lt_zero _) (natCast_nonneg _)
theorem natAbs_dvd {a b : Int} : (a.natAbs : Int) b a b :=
match natAbs_eq a with
@@ -145,6 +145,12 @@ theorem dvd_of_mul_dvd_mul_left {a m n : Int} (ha : a ≠ 0) (h : a * m a *
theorem dvd_of_mul_dvd_mul_right {a m n : Int} (ha : a 0) (h : m * a n * a) : m n :=
dvd_of_mul_dvd_mul_left ha (by simpa [Int.mul_comm] using h)
theorem dvd_mul_of_dvd_right {a b c : Int} (h : a c) : a b * c :=
Int.dvd_trans h (Int.dvd_mul_left b c)
theorem dvd_mul_of_dvd_left {a b c : Int} (h : a b) : a b * c :=
Int.dvd_trans h (Int.dvd_mul_right b c)
@[norm_cast] theorem natCast_dvd_natCast {m n : Nat} : (m : Int) n m n where
mp := by
rintro a, h
@@ -214,8 +220,8 @@ theorem tdiv_eq_ediv {a b : Int} :
| ofNat a, -[b+1] => simp [tdiv_eq_ediv_of_nonneg]
| -[a+1], 0 => simp
| -[a+1], ofNat (succ b) =>
simp only [tdiv, Nat.succ_eq_add_one, ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int,
negSucc_not_nonneg, sign_of_add_one]
simp only [tdiv, Nat.succ_eq_add_one, ofNat_eq_natCast, Int.natCast_add, cast_ofNat_Int,
negSucc_not_nonneg, sign_natCast_add_one]
simp only [negSucc_emod_ofNat_succ_eq_zero_iff]
norm_cast
simp only [Nat.succ_eq_add_one, false_or]
@@ -225,7 +231,7 @@ theorem tdiv_eq_ediv {a b : Int} :
· rw [neg_ofNat_eq_negSucc_add_one_iff]
exact Nat.succ_div_of_mod_ne_zero h
| -[a+1], -[b+1] =>
simp only [tdiv, ofNat_eq_coe, negSucc_not_nonneg, false_or, sign_negSucc]
simp only [tdiv, ofNat_eq_natCast, negSucc_not_nonneg, false_or, sign_negSucc]
norm_cast
simp only [negSucc_ediv_negSucc]
rw [Int.natCast_add, Int.natCast_one]
@@ -256,7 +262,7 @@ theorem fdiv_eq_ediv {a b : Int} :
| 0, -[b+1] => simp
| ofNat (a + 1), -[b+1] =>
simp only [fdiv, ofNat_ediv_negSucc, negSucc_not_nonneg, negSucc_dvd, false_or]
simp only [ofNat_eq_coe, ofNat_dvd]
simp only [ofNat_eq_natCast, ofNat_dvd]
norm_cast
rw [Nat.succ_div, negSucc_eq]
split <;> rename_i h
@@ -264,7 +270,7 @@ theorem fdiv_eq_ediv {a b : Int} :
· simp [Int.neg_add]
norm_cast
| -[a+1], -[b+1] =>
simp only [fdiv, ofNat_eq_coe, negSucc_ediv_negSucc, negSucc_not_nonneg, dvd_negSucc, negSucc_dvd,
simp only [fdiv, ofNat_eq_natCast, negSucc_ediv_negSucc, negSucc_not_nonneg, dvd_negSucc, negSucc_dvd,
false_or]
norm_cast
rw [Int.natCast_add, Int.natCast_one, Nat.succ_div]
@@ -510,32 +516,29 @@ theorem ediv_neg_of_neg_of_pos {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0
match a, b, eq_negSucc_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
| _, _, _, rfl, _, rfl => negSucc_lt_zero _
@[deprecated ediv_neg_of_neg_of_pos (since := "2025-03-04")]
abbrev ediv_neg' := @ediv_neg_of_neg_of_pos
theorem negSucc_ediv (m : Nat) {b : Int} (H : 0 < b) : -[m+1] / b = -(ediv m b + 1) :=
match b, eq_succ_of_zero_lt H with
| _, _, rfl => rfl
theorem ediv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a / b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_zero_le _
| _, _, _, rfl, _, rfl => natCast_nonneg _
theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a / b := by
match a, b with
| ofNat a, b =>
match Int.le_antisymm Ha (ofNat_zero_le a) with
match Int.le_antisymm Ha (natCast_nonneg a) with
| h1 =>
rw [h1, zero_ediv]
exact Int.le_refl 0
| a, ofNat b =>
match Int.le_antisymm Hb (ofNat_zero_le b) with
match Int.le_antisymm Hb (natCast_nonneg b) with
| h1 =>
rw [h1, Int.ediv_zero]
exact Int.le_refl 0
| negSucc a, negSucc b =>
rw [Int.div_def, ediv]
exact le_add_one (ediv_nonneg (ofNat_zero_le a) (Int.le_trans (ofNat_zero_le b) (le.intro 1 rfl)))
exact le_add_one (ediv_nonneg (natCast_nonneg a) (Int.le_trans (natCast_nonneg b) (le.intro 1 rfl)))
theorem ediv_pos_of_neg_of_neg {a b : Int} (ha : a < 0) (hb : b < 0) : 0 < a / b := by
rw [Int.div_def]
@@ -545,9 +548,6 @@ theorem ediv_pos_of_neg_of_neg {a b : Int} (ha : a < 0) (hb : b < 0) : 0 < a / b
theorem ediv_nonpos_of_nonneg_of_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a / b 0 :=
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
@[deprecated ediv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev ediv_nonpos := @ediv_nonpos_of_nonneg_of_nonpos
theorem ediv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a / b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
@@ -565,7 +565,7 @@ theorem ediv_eq_one_of_neg_of_le {a b : Int} (H1 : a < 0) (H2 : b ≤ a) : a / b
match a, b, H1, H2 with
| negSucc a', ofNat n', H1, H2 => simp [Int.negSucc_eq] at H2; omega
| negSucc a', negSucc b', H1, H2 =>
rw [Int.div_def, ediv, ofNat_eq_coe]
rw [Int.div_def, ediv, ofNat_eq_natCast]
norm_cast
rw [Nat.succ_eq_add_one, Nat.add_eq_right, Nat.div_eq_zero_iff_lt (by omega)]
simp [Int.negSucc_eq] at H2
@@ -585,7 +585,7 @@ theorem neg_one_ediv (b : Int) : -1 / b = -b.sign :=
match b with
| ofNat 0 => by simp
| ofNat (b + 1) =>
ediv_eq_neg_one_of_neg_of_le (by decide) (by simp [ofNat_eq_coe]; omega)
ediv_eq_neg_one_of_neg_of_le (by decide) (by simp [ofNat_eq_natCast]; omega)
| negSucc b =>
ediv_eq_one_of_neg_of_le (by decide) (by omega)
@@ -652,7 +652,7 @@ theorem sign_ediv (a b : Int) : sign (a / b) = if 0 ≤ a ∧ a < b.natAbs then
| (a + 1 : Nat) =>
norm_cast
simp only [Nat.le_add_left, Nat.add_lt_add_iff_right, true_and, Int.natCast_add,
cast_ofNat_Int, sign_of_add_one, Int.mul_one]
cast_ofNat_Int, sign_natCast_add_one, Int.mul_one]
split
· rw [Nat.div_eq_of_lt (by omega)]
simp
@@ -678,25 +678,15 @@ theorem ofNat_mod_ofNat (m n : Nat) : (m % n : Int) = ↑(m % n) := rfl
@[simp] theorem add_neg_mul_emod_self_right (a b c : Int) : (a + -(b * c)) % c = a % c := by
rw [Int.neg_mul_eq_neg_mul, add_mul_emod_self_right]
@[deprecated add_neg_mul_emod_self_right (since := "2025-04-11")]
theorem add_neg_mul_emod_self {a b c : Int} : (a + -(b * c)) % c = a % c :=
add_neg_mul_emod_self_right ..
@[simp] theorem add_neg_mul_emod_self_left (a b c : Int) : (a + -(b * c)) % b = a % b := by
rw [Int.neg_mul_eq_mul_neg, add_mul_emod_self_left]
@[simp] theorem add_emod_right (a b : Int) : (a + b) % b = a % b := by
have := add_mul_emod_self_left a b 1; rwa [Int.mul_one] at this
@[deprecated add_emod_right (since := "2025-04-11")]
theorem add_emod_self {a b : Int} : (a + b) % b = a % b := add_emod_right ..
@[simp] theorem add_emod_left (a b : Int) : (a + b) % a = b % a := by
rw [Int.add_comm, add_emod_right]
@[deprecated add_emod_left (since := "2025-04-11")]
theorem add_emod_self_left {a b : Int} : (a + b) % a = b % a := add_emod_left ..
@[simp] theorem sub_mul_emod_self_right (a b c : Int) : (a - b * c) % c = a % c := by
simp [Int.sub_eq_add_neg]
@@ -937,9 +927,6 @@ where
| -[_+1], 0 => Nat.zero_le _
| -[_+1], succ _ => Nat.succ_le_succ (Nat.div_le_self _ _)
@[deprecated natAbs_ediv_le_natAbs (since := "2025-03-05")]
abbrev natAbs_div_le_natAbs := natAbs_ediv_le_natAbs
theorem ediv_le_self {a : Int} (b : Int) (Ha : 0 a) : a / b a := by
have := Int.le_trans le_natAbs (ofNat_le.2 <| natAbs_ediv_le_natAbs a b)
rwa [natAbs_of_nonneg Ha] at this
@@ -972,14 +959,6 @@ theorem emod_eq_iff {a b c : Int} (hb : b ≠ 0) : a % b = c ↔ 0 ≤ c ∧ c <
rw [ dvd_iff_emod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_right a b
@[deprecated mul_ediv_cancel (since := "2025-03-05")]
theorem neg_mul_ediv_cancel (a b : Int) (h : b 0) : -(a * b) / b = -a := by
rw [neg_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ h]
@[deprecated mul_ediv_cancel (since := "2025-03-05")]
theorem neg_mul_ediv_cancel_left (a b : Int) (h : a 0) : -(a * b) / a = -b := by
rw [neg_ediv_of_dvd (Int.dvd_mul_right a b), mul_ediv_cancel_left _ h]
@[simp] theorem ediv_one : a : Int, a / 1 = a
| (_:Nat) => congrArg Nat.cast (Nat.div_one _)
| -[_+1] => congrArg negSucc (Nat.div_one _)
@@ -993,10 +972,6 @@ theorem ediv_minus_one (a : Int) : a / (-1) = -a := by
theorem emod_minus_one (a : Int) : a % (-1) = 0 := by
simp
@[deprecated sub_emod_right (since := "2025-04-11")]
theorem emod_sub_cancel (x y : Int) : (x - y) % y = x % y :=
sub_emod_right ..
@[simp] theorem add_neg_emod_self (a b : Int) : (a + -b) % b = a % b := by
rw [Int.add_neg_eq_sub, sub_emod_right]
@@ -1007,10 +982,6 @@ theorem emod_sub_cancel (x y : Int) : (x - y) % y = x % y :=
theorem dvd_self_sub_of_emod_eq {a b : Int} : {c : Int} a % b = c b a - c
| _, rfl => dvd_self_sub_emod
@[deprecated dvd_self_sub_of_emod_eq (since := "2025-04-12")]
theorem dvd_sub_of_emod_eq {a b : Int} : {c : Int} a % b = c b a - c :=
dvd_self_sub_of_emod_eq
theorem dvd_sub_self_of_emod_eq {a b : Int} : {c : Int} a % b = c b c - a
| _, rfl => dvd_emod_sub_self
@@ -1098,7 +1069,7 @@ theorem emod_natAbs_of_neg {x : Int} (h : x < 0) {n : Nat} (w : n ≠ 0) :
match x, h with
| -(x + 1 : Nat), _ =>
rw [Int.natAbs_neg]
rw [Int.natAbs_cast]
rw [Int.natAbs_natCast]
rw [Int.neg_emod]
simp only [Int.dvd_neg]
simp only [Int.natCast_dvd_natCast]
@@ -1264,7 +1235,7 @@ private theorem ediv_ediv_of_pos {x y z : Int} (hy : 0 < y) (hz : 0 < z) :
· rw [Int.mul_comm y, Int.mul_assoc, Int.add_mul, Int.mul_comm _ z]
exact Int.lt_mul_of_ediv_lt hy (Int.lt_mul_ediv_self_add hz)
theorem ediv_ediv {x y z : Int} (hy : 0 y) : x / y / z = x / (y * z) := by
theorem ediv_ediv_of_nonneg {x y z : Int} (hy : 0 y) : x / y / z = x / (y * z) := by
rcases y with (_ | a) | a
· simp
· rcases z with (_ | b) | b
@@ -1273,6 +1244,21 @@ theorem ediv_ediv {x y z : Int} (hy : 0 ≤ y) : x / y / z = x / (y * z) := by
· simp [Int.negSucc_eq, Int.mul_neg, ediv_ediv_of_pos]
· simp at hy
theorem ediv_ediv {x y z : Int} : x / y / z = x / (y * z) - if y < 0 ¬ z x / y then z.sign else 0 := by
rcases y with y | y
· rw [ediv_ediv_of_nonneg (by simp), if_neg (by simp; omega)]
simp
· rw [Int.negSucc_eq, Int.ediv_neg, Int.neg_mul, Int.ediv_neg, Int.neg_ediv, ediv_ediv_of_nonneg (by omega)]
simp
theorem ediv_mul {x y z : Int} : x / (y * z) = x / y / z + if y < 0 ¬ z x / y then z.sign else 0 := by
have := ediv_ediv (x := x) (y := y) (z := z)
omega
theorem ediv_mul_of_nonneg {x y z : Int} (hy : 0 y) : x / (y * z) = x / y / z := by
have := ediv_ediv_of_nonneg (x := x) (y := y) (z := z) hy
omega
/-! ### tdiv -/
-- `tdiv` analogues of `ediv` lemmas from `Bootstrap.lean`
@@ -1306,7 +1292,7 @@ because these statements are all incorrect, and require awkward conditional off-
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.tdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_zero_le _
| _, _, _, rfl, _, rfl => natCast_nonneg _
theorem tdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a.tdiv b := by
rw [tdiv_eq_ediv]
@@ -1329,9 +1315,6 @@ theorem tdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0
protected theorem tdiv_nonpos_of_nonneg_of_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a.tdiv b 0 :=
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
@[deprecated Int.tdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev tdiv_nonpos := @Int.tdiv_nonpos_of_nonneg_of_nonpos
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.tdiv b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
@@ -1427,7 +1410,7 @@ theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
theorem lt_tmod_of_pos (a : Int) {b : Int} (H : 0 < b) : -b < tmod a b :=
match a, b, eq_succ_of_zero_lt H with
| ofNat _, _, n, rfl => by rw [ofNat_eq_coe, Int.natCast_succ, ofNat_tmod]; omega
| ofNat _, _, n, rfl => by rw [ofNat_eq_natCast, Int.natCast_succ, ofNat_tmod]; omega
| -[a+1], _, n, rfl => by
rw [negSucc_eq, neg_tmod, Int.natCast_add_one, Int.natCast_add_one, ofNat_tmod]
have : (a + 1) % (n + 1) < n + 1 := Nat.mod_lt _ (Nat.zero_lt_succ n)
@@ -1874,7 +1857,7 @@ theorem le_emod_self_add_one_iff {a b : Int} (h : 0 < b) : b ≤ a % b + 1 ↔ b
match b, h with
| .ofNat 1, h => simp
| .ofNat (b + 2), h =>
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int] at *
simp only [ofNat_eq_natCast, Int.natCast_add, cast_ofNat_Int] at *
constructor
· rw [dvd_iff_emod_eq_zero]
intro w
@@ -1890,16 +1873,12 @@ theorem le_emod_self_add_one_iff {a b : Int} (h : 0 < b) : b ≤ a % b + 1 ↔ b
sign_eq_one_of_pos (by omega), Int.mul_add]
omega
@[deprecated le_emod_self_add_one_iff (since := "2025-04-12")]
theorem le_mod_self_add_one_iff {a b : Int} (h : 0 < b) : b a % b + 1 b a + 1 :=
le_emod_self_add_one_iff h
theorem add_one_tdiv_of_pos {a b : Int} (h : 0 < b) :
(a + 1).tdiv b = a.tdiv b + if (0 < a + 1 b a + 1) (a < 0 b a) then 1 else 0 := by
match b, h with
| .ofNat 1, h => simp; omega
| .ofNat (b + 2), h =>
simp only [ofNat_eq_coe]
simp only [ofNat_eq_natCast]
rw [tdiv_eq_ediv, add_ediv (by omega), tdiv_eq_ediv]
simp only [Int.natCast_add, cast_ofNat_Int]
have : 1 / (b + 2 : Int) = 0 := by rw [one_ediv]; omega
@@ -2014,7 +1993,7 @@ theorem add_fdiv_of_dvd_left {a b c : Int} (H : c a) : (a + b).fdiv c = a.fd
theorem fdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.fdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_fdiv .. ofNat_zero_le _
| _, _, _, rfl, _, rfl => ofNat_fdiv .. natCast_nonneg _
theorem fdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a.fdiv b := by
rw [fdiv_eq_ediv]
@@ -2029,9 +2008,6 @@ theorem fdiv_nonpos_of_nonneg_of_nonpos : ∀ {a b : Int}, 0 ≤ a → b ≤ 0
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => by
simp [fdiv, negSucc_le_zero]
@[deprecated fdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev fdiv_nonpos := @fdiv_nonpos_of_nonneg_of_nonpos
theorem fdiv_neg_of_neg_of_pos : {a b : Int}, a < 0 0 < b a.fdiv b < 0
| -[_+1], succ _, _, _ => negSucc_lt_zero _
@@ -2066,8 +2042,8 @@ protected theorem fdiv_eq_of_eq_mul_right {a b c : Int}
(H1 : b 0) (H2 : a = b * c) : a.fdiv b = c := by rw [H2, Int.mul_fdiv_cancel_left _ H1]
protected theorem eq_fdiv_of_mul_eq_right {a b c : Int}
(H1 : a 0) (H2 : a * b = c) : b = c.tdiv a :=
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
(H1 : a 0) (H2 : a * b = c) : b = c.fdiv a :=
(Int.fdiv_eq_of_eq_mul_right H1 H2.symm).symm
protected theorem fdiv_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a.fdiv b = c :=
@@ -2104,20 +2080,20 @@ theorem neg_fdiv {a b : Int} : (-a).fdiv b = -(a.fdiv b) - if b = 0 b a
| ofNat (a + 1), 0 => simp
| ofNat (a + 1), ofNat (b + 1) =>
unfold fdiv
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
simp only [ofNat_eq_natCast, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
rw [ negSucc_eq, negSucc_eq]
| ofNat (a + 1), -[b+1] =>
unfold fdiv
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
simp only [ofNat_eq_natCast, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
rw [ negSucc_eq, neg_negSucc]
| -[a+1], 0 => simp
| -[a+1], ofNat (b + 1) =>
unfold fdiv
simp only [ofNat_eq_coe, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
simp only [ofNat_eq_natCast, Int.natCast_add, cast_ofNat_Int, Nat.succ_eq_add_one]
rw [neg_negSucc, negSucc_eq]
| -[a+1], -[b+1] =>
unfold fdiv
simp only [ofNat_eq_coe, natCast_ediv, Nat.succ_eq_add_one, Int.natCast_add, cast_ofNat_Int]
simp only [ofNat_eq_natCast, natCast_ediv, Nat.succ_eq_add_one, Int.natCast_add, cast_ofNat_Int]
rw [neg_negSucc, neg_negSucc]
simp
@@ -2150,9 +2126,6 @@ theorem fmod_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a.fmod b :
theorem fmod_nonneg_of_pos (a : Int) {b : Int} (hb : 0 < b) : 0 a.fmod b :=
fmod_eq_emod_of_nonneg _ (Int.le_of_lt hb) emod_nonneg _ (Int.ne_of_lt hb).symm
@[deprecated fmod_nonneg_of_pos (since := "2025-03-04")]
abbrev fmod_nonneg' := @fmod_nonneg_of_pos
theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
fmod_eq_emod_of_nonneg _ (Int.le_of_lt H) emod_lt_of_pos a H
@@ -2162,10 +2135,6 @@ theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
rw [fmod_eq_emod, add_mul_emod_self_right, fmod_eq_emod]
simp
@[deprecated add_mul_fmod_self_right (since := "2025-04-11")]
theorem add_mul_fmod_self {a b c : Int} : (a + b * c).fmod c = a.fmod c :=
add_mul_fmod_self_right ..
@[simp] theorem add_mul_fmod_self_left (a b c : Int) : (a + b * c).fmod b = a.fmod b := by
rw [Int.mul_comm, Int.add_mul_fmod_self_right]
@@ -2413,7 +2382,7 @@ theorem natAbs_fdiv_le_natAbs (a b : Int) : natAbs (a.fdiv b) ≤ natAbs a := by
| 0, .negSucc b, h => simp at h
| .ofNat (a + 1), .negSucc 0, h => simp at h
| .ofNat (a + 1), .negSucc (b + 1), h =>
rw [negSucc_eq, ofNat_eq_coe]
rw [negSucc_eq, ofNat_eq_natCast]
norm_cast
rw [Int.ediv_neg, Int.sub_eq_add_neg, Int.neg_add, natAbs_neg]
norm_cast
@@ -2464,20 +2433,12 @@ theorem dvd_sub_self_of_fmod_eq {a b c : Int} (h : a.fmod b = c) :
@[simp] theorem fmod_one (a : Int) : a.fmod 1 = 0 := by
simp [fmod_def, Int.one_mul, Int.sub_self]
@[deprecated sub_fmod_right (since := "2025-04-12")]
theorem fmod_sub_cancel (x y : Int) : (x - y).fmod y = x.fmod y :=
sub_fmod_right _ _
@[simp] theorem add_neg_fmod_self (a b : Int) : (a + -b).fmod b = a.fmod b := by
rw [Int.add_neg_eq_sub, sub_fmod_right]
@[simp] theorem neg_add_fmod_self (a b : Int) : (-a + b).fmod a = b.fmod a := by
rw [Int.add_comm, add_neg_fmod_self]
@[deprecated dvd_self_sub_of_fmod_eq (since := "2025-04-12")]
theorem dvd_sub_of_fmod_eq {a b c : Int} (h : a.fmod b = c) : b a - c :=
dvd_self_sub_of_fmod_eq h
theorem fdiv_sign {a b : Int} : a.fdiv (sign b) = a * sign b := by
rw [fdiv_eq_ediv]
rcases sign_trichotomy b with h | h | h <;> simp [h]
@@ -2511,10 +2472,6 @@ theorem lt_mul_fdiv_self_add {x k : Int} (h : 0 < k) : x < k * (x.fdiv k) + k :=
theorem emod_bmod (x : Int) (n : Nat) : Int.bmod (x%n) n = Int.bmod x n := by
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 :=
emod_bmod ..
theorem bdiv_add_bmod (x : Int) (m : Nat) : m * bdiv x m + bmod x m = x := by
unfold bdiv bmod
split
@@ -2541,6 +2498,10 @@ theorem bmod_eq_self_sub_mul_bdiv (x : Int) (m : Nat) : bmod x m = x - m * bdiv
theorem bmod_eq_self_sub_bdiv_mul (x : Int) (m : Nat) : bmod x m = x - bdiv x m * m := by
rw [ Int.add_sub_cancel (bmod x m), bmod_add_bdiv']
theorem bmod_eq_emod_of_lt {x : Int} {m : Nat} (hx : x % m < (m + 1) / 2) : bmod x m = x % m := by
simp [bmod, hx]
@[deprecated Int.bmod_eq_emod_of_lt (since := "2025-10-29")]
theorem bmod_pos (x : Int) (m : Nat) (p : x % m < (m + 1) / 2) : bmod x m = x % m := by
simp [bmod_def, p]
@@ -2550,15 +2511,12 @@ theorem bmod_neg (x : Int) (m : Nat) (p : x % m ≥ (m + 1) / 2) : bmod x m = (x
theorem bmod_eq_emod (x : Int) (m : Nat) : bmod x m = x % m - if x % m (m + 1) / 2 then m else 0 := by
split
· rwa [bmod_neg]
· rw [bmod_pos] <;> simp_all
· rw [bmod_eq_emod_of_lt] <;> simp_all
@[simp]
theorem bmod_one (x : Int) : Int.bmod x 1 = 0 := by
simp [Int.bmod]
@[deprecated bmod_one (since := "2025-04-10")]
abbrev bmod_one_is_zero := @bmod_one
@[simp] theorem add_bmod_right (a : Int) (b : Nat) : (a + b).bmod b = a.bmod b := by
simp [bmod_def]
@@ -2601,76 +2559,36 @@ abbrev bmod_one_is_zero := @bmod_one
@[simp] theorem add_neg_mul_bmod_self_left (a : Int) (b : Nat) (c : Int) : (a + -(b * c)).bmod b = a.bmod b := by
simp [bmod_def]
@[deprecated add_bmod_right (since := "2025-04-10")]
theorem bmod_add_cancel {x : Int} {n : Nat} : Int.bmod (x + n) n = Int.bmod x n :=
add_bmod_right ..
@[deprecated add_mul_bmod_self_left (since := "2025-04-10")]
theorem bmod_add_mul_cancel (x : Int) (n : Nat) (k : Int) : Int.bmod (x + n * k) n = Int.bmod x n :=
add_mul_bmod_self_left ..
@[deprecated sub_bmod_right (since := "2025-04-10")]
theorem bmod_sub_cancel (x : Int) (n : Nat) : Int.bmod (x - n) n = Int.bmod x n :=
sub_bmod_right ..
@[deprecated sub_mul_bmod_self_left (since := "2025-04-10")]
theorem Int.bmod_sub_mul_cancel (x : Int) (n : Nat) (k : Int) : (x - n * k).bmod n = x.bmod n :=
sub_mul_bmod_self_left ..
@[simp]
theorem emod_add_bmod (x : Int) (n : Nat) : Int.bmod (x % n + y) n = Int.bmod (x + y) n := by
simp [Int.emod_def, Int.sub_eq_add_neg]
rw [Int.mul_neg, Int.add_right_comm, Int.add_mul_bmod_self_left]
@[deprecated emod_add_bmod (since := "2025-04-11")]
theorem emod_add_bmod_congr (x : Int) (n : Nat) : Int.bmod (x % n + y) n = Int.bmod (x + y) n :=
emod_add_bmod ..
@[simp]
theorem emod_sub_bmod (x : Int) (n : Nat) : Int.bmod (x % n - y) n = Int.bmod (x - y) n := by
simp only [emod_def, Int.sub_eq_add_neg]
rw [Int.mul_neg, Int.add_right_comm, Int.add_mul_bmod_self_left]
@[deprecated emod_sub_bmod (since := "2025-04-11")]
theorem emod_sub_bmod_congr (x : Int) (n : Nat) : Int.bmod (x % n - y) n = Int.bmod (x - y) n :=
emod_sub_bmod ..
@[simp]
theorem sub_emod_bmod (x : Int) (n : Nat) : Int.bmod (x - y % n) n = Int.bmod (x - y) n := by
simp only [emod_def]
rw [Int.sub_eq_add_neg, Int.neg_sub, Int.sub_eq_add_neg, Int.add_assoc, Int.add_right_comm,
Int.add_mul_bmod_self_left, Int.sub_eq_add_neg]
@[deprecated sub_emod_bmod (since := "2025-04-11")]
theorem sub_emod_bmod_congr (x : Int) (n : Nat) : Int.bmod (x - y % n) n = Int.bmod (x - y) n :=
sub_emod_bmod ..
@[simp]
theorem emod_mul_bmod (x : Int) (n : Nat) : Int.bmod (x % n * y) n = Int.bmod (x * y) n := by
simp [Int.emod_def, Int.sub_eq_add_neg]
rw [Int.mul_neg, Int.add_mul, Int.mul_assoc, Int.add_mul_bmod_self_left]
@[deprecated emod_mul_bmod (since := "2025-04-11")]
theorem emod_mul_bmod_congr (x : Int) (n : Nat) : Int.bmod (x % n * y) n = Int.bmod (x * y) n :=
emod_mul_bmod ..
@[simp]
theorem bmod_add_bmod : Int.bmod (Int.bmod x n + y) n = Int.bmod (x + y) n := by
have := (@add_mul_bmod_self_left (Int.bmod x n + y) n (bdiv x n)).symm
rwa [Int.add_right_comm, bmod_add_bdiv] at this
@[deprecated bmod_add_bmod (since := "2025-04-11")]
theorem bmod_add_bmod_congr : Int.bmod (Int.bmod x n + y) n = Int.bmod (x + y) n :=
bmod_add_bmod ..
@[simp]
theorem bmod_sub_bmod : Int.bmod (Int.bmod x n - y) n = Int.bmod (x - y) n :=
@bmod_add_bmod x n (-y)
@[deprecated bmod_sub_bmod (since := "2025-04-11")]
theorem bmod_sub_bmod_congr : Int.bmod (Int.bmod x n - y) n = Int.bmod (x - y) n :=
bmod_sub_bmod ..
theorem add_bmod_eq_add_bmod_right (i : Int)
(H : bmod x n = bmod y n) : bmod (x + i) n = bmod (y + i) n := by
rw [ bmod_add_bmod, @bmod_add_bmod y, H]
@@ -2830,9 +2748,6 @@ theorem bmod_eq_iff {a : Int} {b : Nat} {c : Int} (hb : 0 < b) :
have := bmod_lt (x := a) (m := b) hb
omega
theorem bmod_eq_emod_of_lt {x : Int} {m : Nat} (hx : x % m < (m + 1) / 2) : bmod x m = x % m := by
simp [bmod, hx]
theorem bmod_eq_neg {n : Nat} {m : Int} (hm : 0 m) (hn : n = 2 * m) : m.bmod n = -m := by
by_cases h : m = 0
· subst h; simp
@@ -2863,9 +2778,6 @@ theorem bmod_natAbs_add_one (x : Int) (w : x ≠ -1) : x.bmod (x.natAbs + 1) = -
· rw [sign_eq_one_iff_pos.2 hx]
exact by omega, by omega, -1, by omega
@[deprecated bmod_natAbs_add_one (since := "2025-04-04")]
abbrev bmod_natAbs_plus_one := @bmod_natAbs_add_one
theorem bmod_self_add_one {x : Nat} : (x : Int).bmod (x + 1) = if x = 0 then 0 else -1 := by
have := bmod_natAbs_add_one x (by omega)
simp only [natAbs_natCast] at this
@@ -2876,7 +2788,7 @@ theorem one_bmod_two : Int.bmod 1 2 = -1 := by simp
theorem one_bmod {b : Nat} (h : 3 b) : Int.bmod 1 b = 1 := by
have hb : 1 % (b : Int) = 1 := by rw [one_emod]; omega
rw [bmod_pos _ _ (by omega), hb]
rw [bmod_eq_emod_of_lt (by omega), hb]
theorem bmod_two_eq (x : Int) : x.bmod 2 = -1 x.bmod 2 = 0 := by
have := le_bmod (x := x) (m := 2) (by omega)
@@ -2986,11 +2898,6 @@ theorem bmod_eq_of_le {n : Int} {m : Nat} (hn' : -(m / 2) ≤ n) (hn : n < (m +
n.bmod m = n :=
(Nat.eq_zero_or_pos m).elim (by rintro rfl; simp) (fun hm => by simp_all [bmod_eq_iff])
@[deprecated bmod_eq_of_le (since := "2025-04-11")]
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) n) (hn : n < (m + 1) / 2) :
n.bmod m = n :=
bmod_eq_of_le hn' hn
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n m) :
(a.bmod m).bmod n = a.bmod n := by
rw [ Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
@@ -3001,11 +2908,6 @@ theorem bmod_eq_of_le_mul_two {x : Int} {y : Nat} (hle : -y ≤ x * 2) (hlt : x
x.bmod y = x := by
apply bmod_eq_of_le (by omega) (by omega)
@[deprecated bmod_eq_of_le_mul_two (since := "2025-04-11")]
theorem bmod_eq_self_of_le_mul_two {x : Int} {y : Nat} (hle : -y x * 2) (hlt : x * 2 < y) :
x.bmod y = x :=
bmod_eq_of_le_mul_two hle hlt
/- ### ediv -/
theorem ediv_lt_self_of_pos_of_ne_one {x y : Int} (hx : 0 < x) (hy : y 1) :
@@ -3024,7 +2926,7 @@ theorem ediv_nonneg_of_nonneg_of_nonneg {x y : Int} (hx : 0 ≤ x) (hy : 0 ≤ y
obtain xn, rfl := Int.eq_ofNat_of_zero_le (a := x) (by omega)
obtain yn, rfl := Int.eq_ofNat_of_zero_le (a := y) (by omega)
rw [ Int.natCast_ediv]
exact Int.ofNat_zero_le (xn / yn)
exact natCast_nonneg (xn / yn)
/-- When both x and y are negative we need stricter bounds on x and y
to establish the upper bound of x/y, i.e., x / y < x.natAbs.
@@ -3061,7 +2963,7 @@ theorem neg_self_le_ediv_of_nonneg_of_nonpos (x y : Int) (hx : 0 ≤ x) (hy : y
· obtain xn, rfl := Int.eq_ofNat_of_zero_le (a := x) (by omega)
obtain yn, rfl := Int.eq_negSucc_of_lt_zero (a := y) (by omega)
rw [show xn = ofNat xn by norm_cast, Int.ofNat_ediv_negSucc (a := xn)]
simp only [ofNat_eq_coe, natCast_ediv, Int.natCast_add, cast_ofNat_Int, Int.neg_le_neg_iff]
simp only [ofNat_eq_natCast, natCast_ediv, Int.natCast_add, cast_ofNat_Int, Int.neg_le_neg_iff]
norm_cast
apply Nat.le_trans (m := xn) (by exact Nat.div_le_self xn (yn + 1)) (by omega)

View File

@@ -0,0 +1,35 @@
/-
Copyright (c) 2025 Lean FRO, LLC All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Init.Data.Int.DivMod.Lemmas
public import Init.Data.Int.Pow
/-!
# Lemmas about divisibility of powers
-/
namespace Int
theorem dvd_pow {a b : Int} {n : Nat} (hab : b a) : b ^ n a ^ n := by
rcases hab with c, rfl
rw [Int.mul_pow]
exact Int.dvd_mul_right (b ^ n) (c ^ n)
theorem ediv_pow {a b : Int} {n : Nat} (hab : b a) :
(a / b) ^ n = a ^ n / b ^ n := by
obtain c, rfl := hab
by_cases b = 0
· by_cases n = 0 <;> simp [*, Int.zero_pow]
· simp [Int.mul_pow, Int.pow_ne_zero, *]
theorem tdiv_pow {a b : Int} {n : Nat} (hab : b a) :
(a.tdiv b) ^ n = (a ^ n).tdiv (b ^ n) := by
rw [Int.tdiv_eq_ediv_of_dvd hab, ediv_pow hab, Int.tdiv_eq_ediv_of_dvd (dvd_pow hab)]
theorem fdiv_pow {a b : Int} {n : Nat} (hab : b a) :
(a.fdiv b) ^ n = (a ^ n).fdiv (b ^ n) := by
rw [Int.fdiv_eq_ediv_of_dvd hab, ediv_pow hab, Int.fdiv_eq_ediv_of_dvd (dvd_pow hab)]

View File

@@ -17,7 +17,7 @@ open Nat
/-! ## Definitions of basic functions -/
theorem subNatNat_of_sub_eq_zero {m n : Nat} (h : n - m = 0) : subNatNat m n = (m - n) := by
rw [subNatNat, h, ofNat_eq_coe]
rw [subNatNat, h, ofNat_eq_natCast]
theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat m n = -[k+1] := by
rw [subNatNat, h]
@@ -74,9 +74,6 @@ theorem negSucc_inj : negSucc m = negSucc n ↔ m = n := ⟨negSucc.inj, fun H =
theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_coe (n : Nat) : -[n+1] = -(n + 1) := rfl
@[simp] theorem negSucc_ne_zero (n : Nat) : -[n+1] 0 := nofun
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 -[n+1] := nofun
@@ -132,7 +129,7 @@ theorem subNatNat_elim (m n : Nat) (motive : Nat → Nat → Int → Prop)
theorem subNatNat_add_left : subNatNat (m + n) m = n := by
unfold subNatNat
rw [Nat.sub_eq_zero_of_le (Nat.le_add_right ..), Nat.add_sub_cancel_left, ofNat_eq_coe]
rw [Nat.sub_eq_zero_of_le (Nat.le_add_right ..), Nat.add_sub_cancel_left, ofNat_eq_natCast]
theorem subNatNat_add_right : subNatNat m (m + n + 1) = negSucc n := by
simp [subNatNat, Nat.add_assoc, Nat.add_sub_cancel_left]
@@ -353,10 +350,6 @@ protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
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")]
theorem negSucc_coe' (n : Nat) : -[n+1] = -n - 1 := by
rw [Int.sub_eq_add_neg, Int.neg_add]; rfl
protected theorem subNatNat_eq_coe {m n : Nat} : subNatNat m n = m - n := by
apply subNatNat_elim m n fun m n i => i = m - n
· intro i n
@@ -559,7 +552,7 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 b = 0 := by
exact match a, b, h with
| .ofNat 0, _, _ => by simp
| _, .ofNat 0, _ => by simp
| .ofNat (a+1), .negSucc b, h => by cases h
| .ofNat (_+1), .negSucc _, h => by cases h
protected theorem mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 :=
Or.rec a0 b0 Int.mul_eq_zero.mp
@@ -607,6 +600,4 @@ protected theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
protected theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
@[simp, norm_cast] theorem natAbs_cast (n : Nat) : natAbs n = n := rfl
end Int

View File

@@ -27,28 +27,28 @@ namespace Int
natCast_nonneg _
@[simp] theorem neg_natCast_le_natCast (n m : Nat) : -(n : Int) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
Int.le_trans (by simp) (natCast_nonneg m)
@[simp] theorem neg_natCast_le_ofNat (n m : Nat) : -(n : Int) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
Int.le_trans (by simp) (natCast_nonneg m)
@[simp] theorem neg_ofNat_le_ofNat (n m : Nat) : -(no_index (OfNat.ofNat n)) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
Int.le_trans (by simp) (natCast_nonneg m)
@[simp] theorem neg_ofNat_le_natCast (n m : Nat) : -(no_index (OfNat.ofNat n)) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
Int.le_trans (by simp) (natCast_nonneg m)
theorem neg_lt_self_iff {n : Int} : -n < n 0 < n := by
omega
@[deprecated ofNat_add_ofNat (since := "2025-10-26")]
protected theorem ofNat_add_out (m n : Nat) : m + n = ((m + n) : Int) := rfl
@[deprecated ofNat_mul_ofNat (since := "2025-10-26")]
protected theorem ofNat_mul_out (m n : Nat) : m * n = ((m * n) : Int) := rfl
protected theorem ofNat_add_one_out (n : Nat) : n + (1 : Int) = (Nat.succ n) := rfl
@[simp] theorem ofNat_eq_natCast (n : Nat) : Int.ofNat n = n := rfl
@[norm_cast] theorem natCast_inj {m n : Nat} : (m : Int) = (n : Int) m = n := ofNat_inj
@[norm_cast]
@@ -62,8 +62,6 @@ theorem natCast_succ_pos (n : Nat) : 0 < (n.succ : Int) := natCast_pos.2 n.succ_
@[simp high] theorem natCast_nonpos_iff {n : Nat} : (n : Int) 0 n = 0 := by omega
@[simp] theorem sign_natCast_add_one (n : Nat) : sign (n + 1) = 1 := rfl
@[simp, norm_cast] theorem cast_id {n : Int} : Int.cast n = n := rfl
@[simp] theorem ble'_eq_true (a b : Int) : (Int.ble' a b = true) = (a b) := by
@@ -84,7 +82,7 @@ theorem natCast_succ_pos (n : Nat) : 0 < (n.succ : Int) := natCast_pos.2 n.succ_
symm
simp only [Int.toNat]
split <;> rename_i x a
· simp only [Int.ofNat_eq_coe]
· simp only [Int.ofNat_eq_natCast]
split <;> rename_i y b h
· simp at h
omega
@@ -118,14 +116,8 @@ theorem pos_iff_toNat_pos {n : Int} : 0 < n ↔ 0 < n.toNat := by
theorem natCast_toNat_eq_self {a : Int} : a.toNat = a 0 a := by omega
@[deprecated natCast_toNat_eq_self (since := "2025-04-16")]
theorem ofNat_toNat_eq_self {a : Int} : a.toNat = a 0 a := natCast_toNat_eq_self
theorem eq_natCast_toNat {a : Int} : a = a.toNat 0 a := by omega
@[deprecated eq_natCast_toNat (since := "2025-04-16")]
theorem eq_ofNat_toNat {a : Int} : a = a.toNat 0 a := eq_natCast_toNat
theorem toNat_le_toNat {n m : Int} (h : n m) : n.toNat m.toNat := by omega
theorem toNat_lt_toNat {n m : Int} (hn : 0 < m) : n.toNat < m.toNat n < m := by omega

View File

@@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Data.Int.LemmasAux
public import Init.Data.Int.Cooper
@@ -12,9 +11,7 @@ import all Init.Data.Int.Gcd
public import Init.Data.AC
import all Init.Data.AC
import Init.LawfulBEqTactics
public section
namespace Int.Linear
/-! Helper definitions and theorems for constructing linear arithmetic proofs. -/
@@ -22,8 +19,7 @@ namespace Int.Linear
abbrev Var := Nat
abbrev Context := Lean.RArray Int
@[expose]
def Var.denote (ctx : Context) (v : Var) : Int :=
abbrev Var.denote (ctx : Context) (v : Var) : Int :=
ctx.get v
inductive Expr where
@@ -36,8 +32,7 @@ inductive Expr where
| mulR (a : Expr) (k : Int)
deriving Inhabited, @[expose] BEq
@[expose]
def Expr.denote (ctx : Context) : Expr Int
abbrev Expr.denote (ctx : Context) : Expr Int
| .add a b => denote ctx a + denote ctx b
| .sub a b => denote ctx a - denote ctx b
| .neg a => - denote ctx a
@@ -46,6 +41,9 @@ def Expr.denote (ctx : Context) : Expr → Int
| .mulL k e => k * denote ctx e
| .mulR e k => denote ctx e * k
set_option allowUnsafeReducibility true
attribute [semireducible] Var.denote Expr.denote
inductive Poly where
| num (k : Int)
| add (k : Int) (v : Var) (p : Poly)
@@ -68,35 +66,36 @@ protected noncomputable def Poly.beq' (p₁ : Poly) : Poly → Bool :=
intro _ _; subst k₁ v₁
simp [ ih p₂, Bool.and'_eq_and]; rfl
@[expose]
def Poly.denote (ctx : Context) (p : Poly) : Int :=
abbrev Poly.denote (ctx : Context) (p : Poly) : Int :=
match p with
| .num k => k
| .add k v p => k * v.denote ctx + denote ctx p
noncomputable abbrev Poly.denote'.go (ctx : Context) (p : Poly) : Int Int :=
Poly.rec
(fun k r => Bool.rec
(r + k)
r
(Int.beq' k 0))
(fun k v _ ih r => Bool.rec
(ih (r + k * v.denote ctx))
(ih (r + v.denote ctx))
(Int.beq' k 1))
p
/--
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] noncomputable def Poly.denote' (ctx : Context) (p : Poly) : Int :=
noncomputable abbrev Poly.denote' (ctx : Context) (p : Poly) : Int :=
Poly.rec (fun k => k)
(fun k v p _ => Bool.rec
(go p (k * v.denote ctx))
(go p (v.denote ctx))
(denote'.go ctx p (k * v.denote ctx))
(denote'.go ctx p (v.denote ctx))
(Int.beq' k 1))
p
where
go (p : Poly) : Int Int :=
Poly.rec
(fun k r => Bool.rec
(r + k)
r
(Int.beq' k 0))
(fun k v _ ih r => Bool.rec
(ih (r + k * v.denote ctx))
(ih (r + v.denote ctx))
(Int.beq' k 1))
p
attribute [semireducible] Poly.denote Poly.denote' Poly.denote'.go
@[simp] theorem Poly.denote'_go_eq_denote (ctx : Context) (p : Poly) (r : Int) : denote'.go ctx p r = p.denote ctx + r := by
induction p generalizing r
@@ -1091,7 +1090,7 @@ theorem eq_unsat_coeff (ctx : Context) (p : Poly) (k : Int) : eq_unsat_coeff_cer
induction p
next => rfl
next a y p ih =>
simp [coeff_k, coeff, cond_eq_if]; split
simp [coeff_k, coeff, cond_eq_ite]; split
next h => simp [h]
next h => rw [ Nat.beq_eq, Bool.not_eq_true] at h; simp [h, ih]; rfl

View File

@@ -62,8 +62,6 @@ protected theorem le_total (a b : Int) : a ≤ b b ≤ a :=
let k, (hk : m + k = n) := Nat.le.dest h
le.intro k (by rw [ hk]; rfl)
@[simp] theorem ofNat_zero_le (n : Nat) : 0 (n : Int) := ofNat_le.2 n.zero_le
theorem eq_ofNat_of_zero_le {a : Int} (h : 0 a) : n : Nat, a = n := by
have t := le.dest_sub h; rwa [Int.sub_zero] at t
@@ -88,8 +86,12 @@ theorem lt.dest {a b : Int} (h : a < b) : ∃ n : Nat, a + Nat.succ n = b :=
@[deprecated natCast_pos (since := "2025-05-13"), simp high]
theorem ofNat_pos {n : Nat} : 0 < (n : Int) 0 < n := ofNat_lt
@[simp]
theorem natCast_nonneg (n : Nat) : 0 (n : Int) := _
@[deprecated natCast_nonneg (since := "2025-10-26")]
theorem ofNat_zero_le (n : Nat) : 0 (n : Int) := ofNat_le.2 n.zero_le
@[deprecated natCast_nonneg (since := "2025-05-13")]
theorem ofNat_nonneg (n : Nat) : 0 (n : Int) := _
@@ -234,13 +236,10 @@ theorem eq_natAbs_of_nonneg {a : Int} (h : 0 ≤ a) : a = natAbs a := by
let n, e := eq_ofNat_of_zero_le h
rw [e]; rfl
@[deprecated eq_natAbs_of_nonneg (since := "2025-03-11")]
abbrev eq_natAbs_of_zero_le := @eq_natAbs_of_nonneg
theorem le_natAbs {a : Int} : a natAbs a :=
match Int.le_total 0 a with
| .inl h => by rw [eq_natAbs_of_nonneg h]; apply Int.le_refl
| .inr h => Int.le_trans h (ofNat_zero_le _)
| .inr h => Int.le_trans h (natCast_nonneg _)
@[simp] theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
Int.not_le.1 fun h => let _, h := eq_ofNat_of_zero_le h; nomatch h
@@ -468,10 +467,10 @@ protected theorem max_lt {a b c : Int} : max a b < c ↔ a < c ∧ b < c := by
simpa using Int.max_le (a := a + 1) (b := b + 1) (c := c)
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (ofNat_zero_le n)]
rw [Int.max_eq_left (natCast_nonneg n)]
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
rw [Int.max_eq_right (ofNat_zero_le n)]
rw [Int.max_eq_right (natCast_nonneg n)]
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
rw [Int.max_eq_right (negSucc_le_zero _)]
@@ -554,8 +553,8 @@ protected theorem mul_le_mul_of_nonpos_left {a b c : Int}
@[simp, norm_cast] theorem natAbs_natCast (n : Nat) : natAbs n = n := rfl
@[deprecated natAbs_natCast (since := "2025-04-16")]
theorem natAbs_ofNat (n : Nat) : natAbs n = n := natAbs_natCast n
@[deprecated natAbs_natCast (since := "2025-10-26")]
theorem natAbs_cast (n : Nat) : natAbs n = n := rfl
/-
TODO: rename `natAbs_ofNat'` to `natAbs_ofNat` once the current deprecated alias
@@ -634,7 +633,7 @@ theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d n) (h₁ : n.n
/-! ### toNat -/
theorem toNat_eq_max : a : Int, (toNat a : Int) = max a 0
| (n : Nat) => (Int.max_eq_left (ofNat_zero_le n)).symm
| (n : Nat) => (Int.max_eq_left (natCast_nonneg n)).symm
| -[n+1] => (Int.max_eq_right (Int.le_of_lt (negSucc_lt_zero n))).symm
@[simp] theorem toNat_zero : (0 : Int).toNat = 0 := rfl
@@ -646,17 +645,11 @@ 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 := rfl
@[simp] theorem toNat_negSucc (n : Nat) : (Int.negSucc n).toNat = 0 := by
simp [toNat]
@[simp] theorem toNat_natCast_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
@[deprecated toNat_natCast_add_one (since := "2025-04-16")]
theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := toNat_natCast_add_one
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
match a with
| (n : Nat) => simp
@@ -717,9 +710,6 @@ theorem mem_toNat? : ∀ {a : Int} {n : Nat}, toNat? a = some n ↔ a = n
| (m : Nat), n => by simp [toNat?, Int.ofNat_inj]
| -[m+1], n => by constructor <;> nofun
@[deprecated mem_toNat? (since := "2025-03-11")]
abbrev mem_toNat' := @mem_toNat?
/-! ## Order properties of the integers -/
protected theorem le_of_not_le {a b : Int} : ¬ a b b a := (Int.le_total a b).resolve_left
@@ -728,7 +718,7 @@ protected theorem le_of_not_le {a b : Int} : ¬ a ≤ b → b ≤ a := (Int.le_t
simp only [Int.not_lt, iff_false]; constructor
theorem eq_negSucc_of_lt_zero : {a : Int}, a < 0 n : Nat, a = -[n+1]
| ofNat _, h => absurd h (Int.not_lt.2 (ofNat_zero_le _))
| ofNat _, h => absurd h (Int.not_lt.2 (natCast_nonneg _))
| -[n+1], _ => n, rfl
protected theorem lt_of_add_lt_add_left {a b c : Int} (h : a + b < a + c) : b < c := by
@@ -796,10 +786,10 @@ theorem add_one_le_iff {a b : Int} : a + 1 ≤ b ↔ a < b := .rfl
theorem lt_add_one_iff {a b : Int} : a < b + 1 a b := Int.add_le_add_iff_right _
@[simp] theorem succ_ofNat_pos (n : Nat) : 0 < (n : Int) + 1 :=
lt_add_one_iff.mpr (ofNat_zero_le _)
lt_add_one_iff.mpr (natCast_nonneg _)
theorem not_ofNat_neg (n : Nat) : ¬((n : Int) < 0) :=
Int.not_lt.mpr (ofNat_zero_le ..)
Int.not_lt.mpr (natCast_nonneg ..)
theorem le_add_one {a b : Int} (h : a b) : a b + 1 :=
Int.le_of_lt (Int.lt_add_one_iff.2 h)
@@ -1250,7 +1240,11 @@ protected theorem neg_of_mul_pos_right {a b : Int}
@[simp] theorem sign_one : sign 1 = 1 := rfl
theorem sign_neg_one : sign (-1) = -1 := rfl
@[simp] theorem sign_of_add_one (x : Nat) : Int.sign (x + 1) = 1 := rfl
@[simp] theorem sign_natCast_add_one (n : Nat) : sign (n + 1) = 1 := rfl
@[deprecated sign_natCast_add_one (since := "2025-10-26")]
theorem sign_of_add_one (x : Nat) : Int.sign (x + 1) = 1 := rfl
@[simp] theorem sign_negSucc (x : Nat) : Int.sign (Int.negSucc x) = -1 := rfl
theorem natAbs_sign (z : Int) : z.sign.natAbs = if z = 0 then 0 else 1 :=
@@ -1259,17 +1253,9 @@ theorem natAbs_sign (z : Int) : z.sign.natAbs = if z = 0 then 0 else 1 :=
theorem natAbs_sign_of_ne_zero {z : Int} (hz : z 0) : z.sign.natAbs = 1 := by
rw [Int.natAbs_sign, if_neg hz]
@[deprecated natAbs_sign_of_ne_zero (since := "2025-04-16")]
theorem natAbs_sign_of_nonzero {z : Int} (hz : z 0) : z.sign.natAbs = 1 :=
natAbs_sign_of_ne_zero hz
theorem sign_natCast_of_ne_zero {n : Nat} (hn : n 0) : Int.sign n = 1 :=
match n, Nat.exists_eq_succ_of_ne_zero hn with
| _, n, rfl => Int.sign_of_add_one n
@[deprecated sign_natCast_of_ne_zero (since := "2025-04-16")]
theorem sign_ofNat_of_nonzero {n : Nat} (hn : n 0) : Int.sign n = 1 :=
sign_natCast_of_ne_zero hn
| _, n, rfl => Int.sign_natCast_add_one n
@[simp] theorem sign_neg (z : Int) : Int.sign (-z) = -Int.sign z := by
match z with | 0 | succ _ | -[_+1] => rfl
@@ -1325,8 +1311,6 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
exact Int.le_add_one (natCast_nonneg _)
| .negSucc _ => simp +decide [sign]
@[deprecated sign_nonneg_iff (since := "2025-03-11")] abbrev sign_nonneg := @sign_nonneg_iff
@[simp] theorem sign_pos_iff : 0 < sign x 0 < x := by
match x with
| 0
@@ -1409,9 +1393,6 @@ theorem natAbs_add_of_nonpos {a b : Int} (ha : a ≤ 0) (hb : b ≤ 0) :
natAbs_add_of_nonneg (Int.neg_nonneg_of_nonpos ha) (Int.neg_nonneg_of_nonpos hb),
natAbs_neg (-a), natAbs_neg (-b)]
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_eq' (m : Nat) : -[m+1] = -m - 1 := by simp only [negSucc_eq, Int.neg_add]; rfl
theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
(w₁ : 0 a) (w₂ : a < b) : a.natAbs < b.natAbs :=
match a, b, eq_ofNat_of_zero_le w₁, eq_ofNat_of_zero_le (Int.le_trans w₁ (Int.le_of_lt w₂)) with
@@ -1420,9 +1401,6 @@ theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
theorem natAbs_eq_iff_mul_eq_zero : natAbs a = n (a - n) * (a + n) = 0 := by
rw [natAbs_eq_iff, Int.mul_eq_zero, Int.sub_neg, Int.sub_eq_zero, Int.sub_eq_zero]
@[deprecated natAbs_eq_iff_mul_eq_zero (since := "2025-03-11")]
abbrev eq_natAbs_iff_mul_eq_zero := @natAbs_eq_iff_mul_eq_zero
instance instIsLinearOrder : IsLinearOrder Int := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int.le_antisymm

View File

@@ -14,9 +14,20 @@ namespace Int
/-! # pow -/
@[simp] protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
@[simp, norm_cast]
theorem natCast_pow (m n : Nat) : (m ^ n : Nat) = (m : Int) ^ n := rfl
theorem negSucc_pow (m n : Nat) : (-[m+1] : Int) ^ n = if n % 2 = 0 then Int.ofNat (m.succ ^ n) else Int.negOfNat (m.succ ^ n) := rfl
@[simp] protected theorem pow_zero (m : Int) : m ^ 0 = 1 := by cases m <;> simp [ natCast_pow, negSucc_pow]
protected theorem pow_succ (m : Int) (n : Nat) : m ^ n.succ = m ^ n * m := by
rcases m with _ | a
· rfl
· simp only [negSucc_pow, Nat.succ_mod_succ_eq_zero_iff, Nat.reduceAdd, Nat.mod_two_ne_zero,
Nat.pow_succ, ofNat_eq_natCast, @negOfNat_eq (_ * _), ite_not, apply_ite (· * -[a+1]),
ofNat_mul_negSucc, negOfNat_mul_negSucc]
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]
@@ -32,33 +43,46 @@ protected theorem zero_pow {n : Nat} (h : n ≠ 0) : (0 : Int) ^ n = 0 := by
protected theorem one_pow {n : Nat} : (1 : Int) ^ n = 1 := by
induction n with simp_all [Int.pow_succ]
protected theorem mul_pow {a b : Int} {n : Nat} : (a * b) ^ n = a ^ n * b ^ n := by
induction n with
| zero => simp
| succ n ih =>
rw [Int.pow_succ, Int.pow_succ, Int.pow_succ, ih, Int.mul_assoc, Int.mul_assoc,
Int.mul_left_comm (b^n)]
protected theorem pow_one (a : Int) : a ^ 1 = a := by
rw [Int.pow_succ, Int.pow_zero, Int.one_mul]
protected theorem pow_mul {a : Int} {n m : Nat} : a ^ (n * m) = (a ^ n) ^ m := by
induction m with
| zero => simp
| succ m ih =>
rw [Int.pow_succ, Nat.mul_add_one, Int.pow_add, ih]
protected theorem pow_pos {n : Int} {m : Nat} : 0 < n 0 < n ^ m := by
induction m with
| zero => simp
| succ m ih => exact fun h => Int.mul_pos (ih h) h
| succ m ih =>
simp only [Int.pow_succ]
exact fun h => Int.mul_pos (ih h) h
protected theorem pow_nonneg {n : Int} {m : Nat} : 0 n 0 n ^ m := by
induction m with
| zero => simp
| succ m ih => exact fun h => Int.mul_nonneg (ih h) h
| succ m ih =>
simp only [Int.pow_succ]
exact fun h => Int.mul_nonneg (ih h) h
protected theorem pow_ne_zero {n : Int} {m : Nat} : n 0 n ^ m 0 := by
induction m with
| zero => simp
| succ m ih => exact fun h => Int.mul_ne_zero (ih h) h
| succ m ih =>
simp only [Int.pow_succ]
exact fun h => Int.mul_ne_zero (ih h) h
instance {n : Int} {m : Nat} [NeZero n] : NeZero (n ^ m) := Int.pow_ne_zero (NeZero.ne _)
-- This can't be removed until the next update-stage0
@[deprecated Nat.pow_pos (since := "2025-02-17")]
abbrev _root_.Nat.pos_pow_of_pos := @Nat.pow_pos
@[simp, norm_cast]
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
match n with
| 0 => rfl
| n + 1 =>
simp only [Nat.pow_succ, Int.pow_succ, Int.natCast_mul, Int.natCast_pow _ n]
instance {n : Int} : NeZero (n^0) := by simp
@[simp]
protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) :
@@ -81,7 +105,7 @@ theorem pow_lt_pow_of_lt {a : Int} {b c : Nat} (ha : 1 < a) (hbc : b < c):
omega
@[simp] theorem natAbs_pow (n : Int) : (k : Nat) (n ^ k).natAbs = n.natAbs ^ k
| 0 => rfl
| 0 => by simp
| k + 1 => by rw [Int.pow_succ, natAbs_mul, natAbs_pow, Nat.pow_succ]
theorem toNat_pow_of_nonneg {x : Int} (h : 0 x) (k : Nat) : (x ^ k).toNat = x.toNat ^ k := by
@@ -90,4 +114,21 @@ theorem toNat_pow_of_nonneg {x : Int} (h : 0 ≤ x) (k : Nat) : (x ^ k).toNat =
| succ k ih =>
rw [Int.pow_succ, Int.toNat_mul (Int.pow_nonneg h) h, ih, Nat.pow_succ]
protected theorem sq_nonnneg (m : Int) : 0 m ^ 2 := by
rw [Int.pow_succ, Int.pow_one]
cases m
· apply Int.mul_nonneg <;> simp
· apply Int.mul_nonneg_of_nonpos_of_nonpos <;> exact negSucc_le_zero _
protected theorem pow_nonneg_of_even {m : Int} {n : Nat} (h : n % 2 = 0) : 0 m ^ n := by
rw [ Nat.mod_add_div n 2, h, Nat.zero_add, Int.pow_mul]
apply Int.pow_nonneg
exact Int.sq_nonnneg m
protected theorem neg_pow {m : Int} {n : Nat} : (-m)^n = (-1)^(n % 2) * m^n := by
rw [Int.neg_eq_neg_one_mul, Int.mul_pow]
rw (occs := [1]) [ Nat.mod_add_div n 2]
rw [Int.pow_add, Int.pow_mul]
simp [Int.one_pow]
end Int

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.Iterators.Basic
public import Init.Data.Iterators.PostconditionMonad
public import Init.Data.Iterators.Consumers
public import Init.Data.Iterators.Producers
public import Init.Data.Iterators.Combinators
public import Init.Data.Iterators.Lemmas
public import Init.Data.Iterators.ToIterator

View File

@@ -241,6 +241,16 @@ def IterStep.successor : IterStep α β → Option α
| .skip it => some it
| .done => none
@[simp]
theorem IterStep.successor_yield {it : α} {out : β} :
(IterStep.yield it out).successor = some it := rfl
@[simp]
theorem IterStep.successor_skip {it : α} : (IterStep.skip (β := β) it).successor = some it := rfl
@[simp]
theorem IterStep.successor_done : (IterStep.done (α := α) (β := β)).successor = none := rfl
/--
If present, applies `f` to the iterator of an `IterStep` and replaces the iterator
with the result of the application of `f`.
@@ -543,6 +553,10 @@ def Iter.IsPlausibleSuccessorOf {α : Type w} {β : Type w} [Iterator α Id β]
(it' it : Iter (α := α) β) : Prop :=
it'.toIterM.IsPlausibleSuccessorOf it.toIterM
theorem Iter.isPlausibleSuccessorOf_eq_invImage {α : Type w} {β : Type w} [Iterator α Id β] :
IsPlausibleSuccessorOf (α := α) (β := β) =
InvImage (IterM.IsPlausibleSuccessorOf (α := α) (β := β) (m := Id)) Iter.toIterM := rfl
theorem Iter.isPlausibleSuccessorOf_iff_exists {α : Type w} {β : Type w} [Iterator α Id β]
{it' it : Iter (α := α) β} :
it'.IsPlausibleSuccessorOf it step, step.successor = some it' it.IsPlausibleStep step := by
@@ -555,6 +569,16 @@ theorem Iter.isPlausibleSuccessorOf_iff_exists {α : Type w} {β : Type w} [Iter
exact step.mapIterator Iter.toIterM,
by cases step <;> simp_all [IterStep.successor, Iter.IsPlausibleStep]
theorem Iter.IsPlausibleStep.isPlausibleSuccessor_of_yield {α : Type w} {β : Type w}
[Iterator α Id β] {it' it : Iter (α := α) β} {out : β}
(h : it.IsPlausibleStep (.yield it' out)) : it'.IsPlausibleSuccessorOf it := by
simpa [isPlausibleSuccessorOf_iff_exists] using .yield it' out, by simp [h]
theorem Iter.IsPlausibleStep.isPlausibleSuccessor_of_skip {α : Type w} {β : Type w}
[Iterator α Id β] {it' it : Iter (α := α) β} (h : it.IsPlausibleStep (.skip it')) :
it'.IsPlausibleSuccessorOf it := by
simpa [isPlausibleSuccessorOf_iff_exists] using .skip it', by simp [h]
/--
Asserts that a certain iterator `it` could plausibly yield the value `out` after an arbitrary
number of steps.
@@ -656,6 +680,10 @@ Given this typeclass, termination proofs for well-founded recursion over an iter
class Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
wf : WellFounded (IterM.IsPlausibleSuccessorOf (α := α) (m := m))
theorem Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
WellFounded (Iter.IsPlausibleSuccessorOf (α := α)) := by
simpa [Iter.isPlausibleSuccessorOf_eq_invImage] using InvImage.wf _ Finite.wf
/--
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`.
@@ -705,6 +733,12 @@ theorem IterM.TerminationMeasures.Finite.rel_of_skip
Rel it' it := by
exact .single _, rfl, h
theorem IterM.TerminationMeasures.Finite.rel_trans
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
{it it' it'' : TerminationMeasures.Finite α m} :
it.Rel it' it'.Rel it'' it.Rel it'' :=
.trans
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
first
| exact IterM.TerminationMeasures.Finite.rel_of_yield _
@@ -783,6 +817,24 @@ def IterM.TerminationMeasures.Productive.Rel
TerminationMeasures.Productive α m TerminationMeasures.Productive α m Prop :=
Relation.TransGen <| InvImage IterM.IsPlausibleSkipSuccessorOf IterM.TerminationMeasures.Productive.it
theorem IterM.TerminationMeasures.Finite.Rel.of_productive
{α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β] {a b : Finite α m} :
Productive.Rel a.it b.it Finite.Rel a b := by
generalize ha' : Productive.mk a.it = a'
generalize hb' : Productive.mk b.it = b'
have ha : a = a'.it := by simp [ ha']
have hb : b = b'.it := by simp [ hb']
rw [ha, hb]
clear ha hb ha' hb' a b
rw [Productive.Rel, Finite.Rel]
intro h
induction h
· rename_i ih
exact .single _, rfl, ih
· rename_i hab ih
refine .trans ih ?_
exact .single _, rfl, hab
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

View File

@@ -9,4 +9,5 @@ prelude
public import Init.Data.Iterators.Combinators.Monadic
public import Init.Data.Iterators.Combinators.FilterMap
public import Init.Data.Iterators.Combinators.FlatMap
public import Init.Data.Iterators.Combinators.Take
public import Init.Data.Iterators.Combinators.ULift

View File

@@ -8,4 +8,5 @@ module
prelude
public import Init.Data.Iterators.Combinators.Monadic.FilterMap
public import Init.Data.Iterators.Combinators.Monadic.FlatMap
public import Init.Data.Iterators.Combinators.Monadic.Take
public import Init.Data.Iterators.Combinators.Monadic.ULift

View File

@@ -106,16 +106,6 @@ instance Attach.instIteratorLoopPartial {α β : Type w} {m : Type w → Type w'
IteratorLoopPartial (Attach α m P) m n :=
.defaultImplementation
instance {α β : Type w} {m : Type w Type w'} [Monad m]
{P : β Prop} [Iterator α m β] [IteratorSize α m] :
IteratorSize (Attach α m P) m where
size it := IteratorSize.size it.internalState.inner
instance {α β : Type w} {m : Type w Type w'} [Monad m]
{P : β Prop} [Iterator α m β] [IteratorSizePartial α m] :
IteratorSizePartial (Attach α m P) m where
size it := IteratorSizePartial.size it.internalState.inner
end Types
/--

View File

@@ -94,7 +94,7 @@ it.filterMapWithPostcondition ---a'-----c'-------⊥
**Termination properties:**
* `Finite` instance: only if `it` is finite
* `Productive` instance: only if `it` is finite`
* `Productive` instance: only if `it` is finite
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` never returns `none`, then
@@ -604,30 +604,4 @@ def IterM.filter {α β : Type w} {m : Type w → Type w'} [Iterator α m β] [M
(f : β Bool) (it : IterM (α := α) m β) :=
(it.filterMap (fun b => if f b then some b else none) : IterM m β)
instance {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} [Finite α m] :
IteratorSize (FilterMap α m n lift f) n :=
.defaultImplementation
instance {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} :
IteratorSizePartial (FilterMap α m n lift f) n :=
.defaultImplementation
instance {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n γ} [IteratorSize α m] :
IteratorSize (Map α m n lift f) n where
size it := lift (IteratorSize.size it.internalState.inner)
instance {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n γ} [IteratorSizePartial α m] :
IteratorSizePartial (Map α m n lift f) n where
size it := lift (IteratorSizePartial.size it.internalState.inner)
end Std.Iterators

View File

@@ -0,0 +1,223 @@
/-
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
public import Init.Data.Nat.Lemmas
public import Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Loop
public import Init.Data.Iterators.Internal.Termination
@[expose] public section
/-!
This module provides the iterator combinator `IterM.take`.
-/
namespace Std.Iterators
variable {α : Type w} {m : Type w Type w'} {β : Type w}
/--
The internal state of the `IterM.take` iterator combinator.
-/
@[unbox]
structure Take (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
Internal implementation detail of the iterator library.
Caution: For `take n`, `countdown` is `n + 1`.
If `countdown` is zero, the combinator only terminates when `inner` terminates.
-/
countdown : Nat
/-- Internal implementation detail of the iterator library -/
inner : IterM (α := α) m β
/--
Internal implementation detail of the iterator library.
This proof term ensures that a `take` always produces a finite iterator from a productive one.
-/
finite : countdown > 0 Finite α m
/--
Given an iterator `it` and a natural number `n`, `it.take n` is an iterator that outputs
up to the first `n` of `it`'s values in order and then terminates.
**Marble diagram:**
```text
it ---a----b---c--d-e--
it.take 3 ---a----b---c⊥
it ---a--
it.take 3 ---a--
```
**Termination properties:**
* `Finite` instance: only if `it` is productive
* `Productive` instance: only if `it` is productive
**Performance:**
This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def IterM.take [Iterator α m β] (n : Nat) (it : IterM (α := α) m β) :=
toIterM (Take.mk (n + 1) it (Or.inl <| Nat.zero_lt_succ _)) m β
/--
This combinator is only useful for advanced use cases.
Given a finite iterator `it`, returns an iterator that behaves exactly like `it` but is of the same
type as `it.take n`.
**Marble diagram:**
```text
it ---a----b---c--d-e--
it.toTake ---a----b---c--d-e--
```
**Termination properties:**
* `Finite` instance: always
* `Productive` instance: always
**Performance:**
This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def IterM.toTake [Iterator α m β] [Finite α m] (it : IterM (α := α) m β) :=
toIterM (Take.mk 0 it (Or.inr inferInstance)) m β
theorem IterM.take.surjective_of_zero_lt {α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (it : IterM (α := Take α m) m β) (h : 0 < it.internalState.countdown) :
(it₀ : IterM (α := α) m β) (k : Nat), it = it₀.take k := by
refine it.internalState.inner, it.internalState.countdown - 1, ?_
simp only [take, Nat.sub_add_cancel (m := 1) (n := it.internalState.countdown) (by omega)]
rfl
inductive Take.PlausibleStep [Iterator α m β] (it : IterM (α := Take α m) m β) :
(step : IterStep (IterM (α := Take α m) m β) β) Prop where
| yield : {it' out}, it.internalState.inner.IsPlausibleStep (.yield it' out)
(h : it.internalState.countdown 1) PlausibleStep it (.yield it.internalState.countdown - 1, it', it.internalState.finite.imp_left (by omega) out)
| skip : {it'}, it.internalState.inner.IsPlausibleStep (.skip it')
it.internalState.countdown 1 PlausibleStep it (.skip it.internalState.countdown, it', it.internalState.finite)
| done : it.internalState.inner.IsPlausibleStep .done PlausibleStep it .done
| depleted : it.internalState.countdown = 1
PlausibleStep it .done
@[always_inline, inline]
instance Take.instIterator [Monad m] [Iterator α m β] : Iterator (Take α m) m β where
IsPlausibleStep := Take.PlausibleStep
step it :=
if h : it.internalState.countdown = 1 then
pure <| .deflate <| .done (.depleted h)
else do
match ( it.internalState.inner.step).inflate with
| .yield it' out h' =>
pure <| .deflate <| .yield it.internalState.countdown - 1, it', (it.internalState.finite.imp_left (by omega)) out (.yield h' h)
| .skip it' h' => pure <| .deflate <| .skip it.internalState.countdown, it', it.internalState.finite (.skip h' h)
| .done h' => pure <| .deflate <| .done (.done h')
def Take.Rel (m : Type w Type w') [Monad m] [Iterator α m β] [Productive α m] :
IterM (α := Take α m) m β IterM (α := Take α m) m β Prop :=
open scoped Classical in
if _ : Finite α m then
InvImage (Prod.Lex Nat.lt_wfRel.rel IterM.TerminationMeasures.Finite.Rel)
(fun it => (it.internalState.countdown, it.internalState.inner.finitelyManySteps))
else
InvImage (Prod.Lex Nat.lt_wfRel.rel IterM.TerminationMeasures.Productive.Rel)
(fun it => (it.internalState.countdown, it.internalState.inner.finitelyManySkips))
theorem Take.rel_of_countdown [Monad m] [Iterator α m β] [Productive α m]
{it it' : IterM (α := Take α m) m β}
(h : it'.internalState.countdown < it.internalState.countdown) : Take.Rel m it' it := by
simp only [Rel]
split <;> exact Prod.Lex.left _ _ h
theorem Take.rel_of_inner [Monad m] [Iterator α m β] [Productive α m] {remaining : Nat}
{it it' : IterM (α := α) m β}
(h : it'.finitelyManySkips.Rel it.finitelyManySkips) :
Take.Rel m (it'.take remaining) (it.take remaining) := by
simp only [Rel]
split
· exact Prod.Lex.right _ (.of_productive h)
· exact Prod.Lex.right _ h
theorem Take.rel_of_zero_of_inner [Monad m] [Iterator α m β]
{it it' : IterM (α := Take α m) m β}
(h : it.internalState.countdown = 0) (h' : it'.internalState.countdown = 0)
(h'' : haveI := it.internalState.finite.resolve_left (by omega); it'.internalState.inner.finitelyManySteps.Rel it.internalState.inner.finitelyManySteps) :
haveI := it.internalState.finite.resolve_left (by omega)
Take.Rel m it' it := by
haveI := it.internalState.finite.resolve_left (by omega)
simp only [Rel, this, reduceDIte, InvImage, h, h']
exact Prod.Lex.right _ h''
private def Take.instFinitenessRelation [Monad m] [Iterator α m β]
[Productive α m] :
FinitenessRelation (Take α m) m where
rel := Take.Rel m
wf := by
rw [Rel]
split
all_goals
apply InvImage.wf
refine fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b
· exact WellFoundedRelation.wf
· exact WellFoundedRelation.wf
subrelation {it it'} h := by
obtain step, h, h' := h
cases h'
case yield it' out k h' h'' =>
cases h
cases it.internalState.finite
· apply rel_of_countdown
simp only
omega
· by_cases h : it.internalState.countdown = 0
· simp only [h, Nat.zero_le, Nat.sub_eq_zero_of_le]
apply rel_of_zero_of_inner h rfl
exact .single _, rfl, h'
· apply rel_of_countdown
simp only
omega
case skip it' out k h' h'' =>
cases h
by_cases h : it.internalState.countdown = 0
· simp only [h]
apply Take.rel_of_zero_of_inner h rfl
exact .single _, rfl, h'
· obtain it, k, rfl := IterM.take.surjective_of_zero_lt it (by omega)
apply Take.rel_of_inner
exact IterM.TerminationMeasures.Productive.rel_of_skip h'
case done _ =>
cases h
case depleted _ =>
cases h
instance Take.instFinite [Monad m] [Iterator α m β] [Productive α m] :
Finite (Take α m) m :=
by exact Finite.of_finitenessRelation instFinitenessRelation
instance Take.instIteratorCollect {n : Type w Type w'} [Monad m] [Monad n] [Iterator α m β] :
IteratorCollect (Take α m) m n :=
.defaultImplementation
instance Take.instIteratorCollectPartial {n : Type w Type w'} [Monad m] [Monad n] [Iterator α m β] :
IteratorCollectPartial (Take α m) m n :=
.defaultImplementation
instance Take.instIteratorLoop {n : Type x Type x'} [Monad m] [Monad n] [Iterator α m β] :
IteratorLoop (Take α m) m n :=
.defaultImplementation
instance Take.instIteratorLoopPartial [Monad m] [Monad n] [Iterator α m β] :
IteratorLoopPartial (Take α m) m n :=
.defaultImplementation
end Std.Iterators

View File

@@ -74,7 +74,7 @@ variable {α : Type u} {m : Type u → Type u'} {n : Type max u v → Type v'}
/--
Transforms a step of the base iterator into a step of the `uLift` iterator.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def Types.ULiftIterator.Monadic.modifyStep (step : IterStep (IterM (α := α) m β) β) :
IterStep (IterM (α := ULiftIterator.{v} α m n β lift) n (ULift.{v} β)) (ULift.{v} β) :=
match step with
@@ -140,15 +140,6 @@ instance Types.ULiftIterator.instIteratorCollectPartial {o} [Monad n] [Monad o]
IteratorCollectPartial (ULiftIterator α m n β lift) n o :=
.defaultImplementation
instance Types.ULiftIterator.instIteratorSize [Monad n] [Iterator α m β] [IteratorSize α m]
[Finite (ULiftIterator α m n β lift) n] :
IteratorSize (ULiftIterator α m n β lift) n :=
.defaultImplementation
instance Types.ULiftIterator.instIteratorSizePartial [Monad n] [Iterator α m β] [IteratorSize α m] :
IteratorSizePartial (ULiftIterator α m n β lift) n :=
.defaultImplementation
/--
Transforms an `m`-monadic iterator with values in `β` into an `n`-monadic iterator with
values in `ULift β`. Requires a `MonadLift m (ULiftT n)` instance.

View File

@@ -0,0 +1,70 @@
/-
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
public import Init.Data.Iterators.Combinators.Monadic.Take
@[expose] public section
namespace Std.Iterators
/--
Given an iterator `it` and a natural number `n`, `it.take n` is an iterator that outputs
up to the first `n` of `it`'s values in order and then terminates.
**Marble diagram:**
```text
it ---a----b---c--d-e--
it.take 3 ---a----b---c⊥
it ---a--
it.take 3 ---a--
```
**Termination properties:**
* `Finite` instance: only if `it` is productive
* `Productive` instance: only if `it` is productive
**Performance:**
This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def Iter.take {α : Type w} {β : Type w} [Iterator α Id β] (n : Nat) (it : Iter (α := α) β) :
Iter (α := Take α Id) β :=
it.toIterM.take n |>.toIter
/--
This combinator is only useful for advanced use cases.
Given a finite iterator `it`, returns an iterator that behaves exactly like `it` but is of the same
type as `it.take n`.
**Marble diagram:**
```text
it ---a----b---c--d-e--
it.toTake ---a----b---c--d-e--
```
**Termination properties:**
* `Finite` instance: always
* `Productive` instance: always
**Performance:**
This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def Iter.toTake {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] (it : Iter (α := α) β) :
Iter (α := Take α Id) β :=
it.toIterM.toTake.toIter
end Std.Iterators

View File

@@ -202,28 +202,105 @@ def Iter.all {α β : Type w}
(p : β Bool) (it : Iter (α := α) β) : Bool :=
(it.allM (fun x => pure (f := Id) (p x))).run
@[always_inline, inline, expose, inherit_doc IterM.size]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSize α Id]
(it : Iter (α := α) β) : Nat :=
(IteratorSize.size it.toIterM).run.down
@[inline]
def Iter.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (Option γ)) :
m (Option γ) :=
ForIn.forIn it none (fun x _ => do
match f x with
| none => return .yield none
| some fx => return .done (some fx))
@[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
@[inline]
def Iter.Partial.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β)
(f : β m (Option γ)) :
m (Option γ) :=
ForIn.forIn it none (fun x _ => do
match f x with
| none => return .yield none
| some fx => return .done (some fx))
@[inline]
def Iter.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] (it : Iter (α := α) β) (f : β Option γ) :
Option γ :=
Id.run (it.findSomeM? (pure <| f ·))
@[inline]
def Iter.Partial.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoopPartial α Id Id] (it : Iter.Partial (α := α) β) (f : β Option γ) :
Option γ :=
Id.run (it.findSomeM? (pure <| f ·))
@[inline]
def Iter.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
@[inline]
def Iter.Partial.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
@[inline]
def Iter.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] (it : Iter (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))
@[inline]
def Iter.Partial.find? {α β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))
/--
`LawfulIteratorSize α m` ensures that the `size` function of an iterator behaves as if it
iterated over the whole iterator, counting its elements and causing all the monadic side effects
of the iterations. This is a fairly strong condition for monadic iterators and it will be false
for many efficient implementations of `size` that compute the size without actually iterating.
Steps through the whole iterator, counting the number of outputs emitted.
This class is experimental and users of the iterator API should not explicitly depend on it.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
class LawfulIteratorSize (α : Type w) {β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorSize α Id] where
size_eq_size_toArray {it : Iter (α := α) β} : it.size =
haveI : IteratorCollect α Id Id := .defaultImplementation
it.toArray.size
@[always_inline, inline, expose]
def Iter.count {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.toIterM.count.run.down
/--
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.count
/--
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose]
def Iter.Partial.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.it.toIterM.allowNontermination.count.run.down
/--
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.Partial.count (since := "2025-10-29")]
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.count
end Std.Iterators

View File

@@ -88,27 +88,6 @@ class IteratorLoopPartial (α : Type w) (m : Type w → Type w') {β : 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
/-- Internal implementation detail of the iterator library. -/
@@ -315,7 +294,7 @@ instance {m : Type w → Type w'} {n : Type w → Type w''}
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]
{α : Type w} {β : Type w} [Iterator α 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)
@@ -579,86 +558,112 @@ def IterM.Partial.all {α β : Type w} {m : Type w → Type w'} [Monad m]
(p : β Bool) (it : IterM.Partial (α := α) m β) : m (ULift Bool) := do
it.allM (fun x => pure (.up (p x)))
section Size
@[inline]
def IterM.findSomeM? {α β γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [Finite α m] (it : IterM (α := α) m β) (f : β m (Option γ)) :
m (Option γ) :=
ForIn.forIn it none (fun x _ => do
match f x with
| none => return .yield none
| some fx => return .done (some fx))
@[inline]
def IterM.Partial.findSomeM? {α β γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoopPartial α m m] (it : IterM.Partial (α := α) m β) (f : β m (Option γ)) :
m (Option γ) :=
ForIn.forIn it none (fun x _ => do
match f x with
| none => return .yield none
| some fx => return .done (some fx))
@[inline]
def IterM.findSome? {α β γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [Finite α m] (it : IterM (α := α) m β) (f : β Option γ) :
m (Option γ) :=
it.findSomeM? (pure <| f ·)
@[inline]
def IterM.Partial.findSome? {α β γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoopPartial α m m] (it : IterM.Partial (α := α) m β) (f : β Option γ) :
m (Option γ) :=
it.findSomeM? (pure <| f ·)
@[inline]
def IterM.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [Finite α m] (it : IterM (α := α) m β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
@[inline]
def IterM.Partial.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoopPartial α m m] (it : IterM.Partial (α := α) m β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
@[inline]
def IterM.find? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [Finite α m] (it : IterM (α := α) m β) (f : β Bool) :
m (Option β) :=
it.findM? (pure <| .up <| f ·)
@[inline]
def IterM.Partial.find? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoopPartial α m m] (it : IterM.Partial (α := α) m β) (f : β Bool) :
m (Option β) :=
it.findM? (pure <| .up <| f ·)
section Count
/--
This is the implementation of the default instance `IteratorSize.defaultImplementation`.
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[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) :=
def IterM.count {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β] [Finite α m]
[IteratorLoop α m m]
[Monad 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.
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
Default performance is linear in the number of steps taken by the iterator.
This function's runtime 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
@[always_inline, inline, deprecated IterM.count (since := "2025-10-29")]
def IterM.size {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β] [Finite α m]
[IteratorLoop α m m]
[Monad m] (it : IterM (α := α) m β) : m (ULift Nat) :=
it.count
/--
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`.
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
Default performance is linear in the number of steps taken by the iterator.
This function's runtime 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
def IterM.Partial.count {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoopPartial α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
it.fold (init := .up 0) fun acc _ => .up (acc.down + 1)
end Size
/--
Steps through the whole iterator, counting the number of outputs emitted.
**Performance**:
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, deprecated IterM.Partial.count (since := "2025-10-29")]
def IterM.Partial.size {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[IteratorLoopPartial α m m] [Monad m] (it : IterM.Partial (α := α) m β) : m (ULift Nat) :=
it.count
end Count
end Std.Iterators

View File

@@ -55,7 +55,7 @@ theorem LawfulMonadLiftFunction.lift_map [LawfulMonad m] [LawfulMonad n]
theorem LawfulMonadLiftFunction.lift_seq [LawfulMonad m] [LawfulMonad n]
[LawfulMonadLiftFunction lift] (mf : m (α β)) (ma : m α) :
lift (mf <*> ma) = lift mf <*> (lift ma : n α) := by
simp only [seq_eq_bind, lift_map, lift_bind]
simp only [seq_eq_bind_map, lift_map, lift_bind]
theorem LawfulMonadLiftFunction.lift_seqLeft [LawfulMonad m] [LawfulMonad n]
[LawfulMonadLiftFunction lift] (x : m α) (y : m β) :

View File

@@ -8,3 +8,4 @@ module
prelude
public import Init.Data.Iterators.Lemmas.Consumers
public import Init.Data.Iterators.Lemmas.Combinators
public import Init.Data.Iterators.Lemmas.Producers

View File

@@ -10,4 +10,5 @@ public import Init.Data.Iterators.Lemmas.Combinators.Attach
public import Init.Data.Iterators.Lemmas.Combinators.Monadic
public import Init.Data.Iterators.Lemmas.Combinators.FilterMap
public import Init.Data.Iterators.Lemmas.Combinators.FlatMap
public import Init.Data.Iterators.Lemmas.Combinators.Take
public import Init.Data.Iterators.Lemmas.Combinators.ULift

View File

@@ -11,6 +11,7 @@ import all Init.Data.Iterators.Combinators.Attach
import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Loop
public import Init.Data.Array.Attach
public section
@@ -82,4 +83,14 @@ theorem Iter.toArray_attachWith [Iterator α Id β]
simpa only [Array.toList_inj]
simp [Iter.toList_toArray]
@[simp]
theorem Iter.count_attachWith [Iterator α Id β]
{it : Iter (α := α) β} {hP}
[Finite α Id] [IteratorLoop α Id Id]
[LawfulIteratorLoop α Id Id] :
(it.attachWith P hP).count = it.count := by
letI : IteratorCollect α Id Id := .defaultImplementation
rw [ Iter.length_toList_eq_count, toList_attachWith]
simp
end Std.Iterators

View File

@@ -467,6 +467,17 @@ theorem Iter.fold_map {α β γ : Type w} {δ : Type x}
end Fold
section Count
@[simp]
theorem Iter.count_map {α β β' : Type w} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β β'} :
(it.map f).count = it.count := by
simp [map_eq_toIter_map_toIterM, count_eq_count_toIterM]
end Count
theorem Iter.anyM_filterMapM {α β β' : Type w} {m : Type w Type w'}
[Iterator α Id β] [Finite α Id] [Monad m] [LawfulMonad m]
{it : Iter (α := α) β} {f : β m (Option β')} {p : β' m (ULift Bool)} :

View File

@@ -9,4 +9,5 @@ prelude
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.FlatMap
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Take
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.ULift

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.Iterators.Combinators.Monadic.Attach
import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public section
@@ -59,4 +60,14 @@ theorem IterM.map_unattach_toArray_attachWith [Iterator α m β] [Monad m] [Mona
rw [ toArray_toList, toArray_toList, map_unattach_toList_attachWith (it := it) (hP := hP)]
simp [-map_unattach_toList_attachWith, -IterM.toArray_toList]
@[simp]
theorem IterM.count_attachWith [Iterator α m β] [Monad m] [Monad n]
{it : IterM (α := α) m β} {hP}
[Finite α m] [IteratorLoop α m m] [LawfulMonad m] [LawfulIteratorLoop α m m] :
(it.attachWith P hP).count = it.count := by
letI : IteratorCollect α m m := .defaultImplementation
rw [ up_length_toList_eq_count, up_length_toList_eq_count,
map_unattach_toList_attachWith (it := it) (P := P) (hP := hP)]
simp only [Functor.map_map, List.length_unattach]
end Std.Iterators

View File

@@ -895,6 +895,23 @@ theorem IterM.fold_map {α β γ δ : Type w} {m : Type w → Type w'}
end Fold
section Count
@[simp]
theorem IterM.count_map {α β β' : Type w} {m : Type w Type w'} [Iterator α m β] [Monad m]
[IteratorLoop α m m] [Finite α m] [LawfulMonad m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β β'} :
(it.map f).count = it.count := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [count_eq_match_step, count_eq_match_step, step_map, bind_assoc]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy _]
· simp [ihs _]
· simp
end Count
section AnyAll
theorem IterM.anyM_filterMapM {α β β' : Type w} {m : Type w Type w'} {n : Type w Type w''}

View File

@@ -0,0 +1,77 @@
/-
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
public import Init.Data.Iterators.Combinators.Monadic.Take
public import Init.Data.Iterators.Lemmas.Consumers.Monadic
@[expose] public section
namespace Std.Iterators
theorem Take.isPlausibleStep_take_yield [Monad m] [Iterator α m β] {n : Nat}
{it : IterM (α := α) m β} (h : it.IsPlausibleStep (.yield it' out)) :
(it.take (n + 1)).IsPlausibleStep (.yield (it'.take n) out) :=
(.yield h (by simp [IterM.take]))
theorem Take.isPlausibleStep_take_skip [Monad m] [Iterator α m β] {n : Nat}
{it : IterM (α := α) m β} (h : it.IsPlausibleStep (.skip it')) :
(it.take (n + 1)).IsPlausibleStep (.skip (it'.take (n + 1))) :=
(.skip h (by simp [IterM.take]))
theorem IterM.step_take {α m β} [Monad m] [Iterator α m β] {n : Nat}
{it : IterM (α := α) m β} :
(it.take n).step = (match n with
| 0 => pure <| .deflate <| .done (.depleted rfl)
| k + 1 => do
match ( it.step).inflate with
| .yield it' out h => pure <| .deflate <| .yield (it'.take k) out (Take.isPlausibleStep_take_yield h)
| .skip it' h => pure <| .deflate <| .skip (it'.take (k + 1)) (Take.isPlausibleStep_take_skip h)
| .done h => pure <| .deflate <| .done (.done h)) := by
simp only [take, step, Iterator.step, internalState_toIterM]
cases n
case zero => rfl
case succ k =>
apply bind_congr
intro step
cases step.inflate using PlausibleIterStep.casesOn <;> rfl
theorem IterM.toList_take_zero {α m β} [Monad m] [LawfulMonad m] [Iterator α m β]
[Finite (Take α m) m]
[IteratorCollect (Take α m) m m] [LawfulIteratorCollect (Take α m) m m]
{it : IterM (α := α) m β} :
(it.take 0).toList = pure [] := by
rw [toList_eq_match_step]
simp [step_take]
theorem IterM.step_toTake {α m β} [Monad m] [Iterator α m β] [Finite α m]
{it : IterM (α := α) m β} :
it.toTake.step = (do
match ( it.step).inflate with
| .yield it' out h => pure <| .deflate <| .yield it'.toTake out (.yield h Nat.zero_ne_one)
| .skip it' h => pure <| .deflate <| .skip it'.toTake (.skip h Nat.zero_ne_one)
| .done h => pure <| .deflate <| .done (.done h)) := by
simp only [toTake, step, Iterator.step, internalState_toIterM]
apply bind_congr
intro step
cases step.inflate using PlausibleIterStep.casesOn <;> rfl
@[simp]
theorem IterM.toList_toTake {α m β} [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toTake.toList = it.toList := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [toList_eq_match_step, toList_eq_match_step]
simp only [step_toTake, bind_assoc]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp [ihy _]
· simp [ihs _]
· simp
end Std.Iterators

View File

@@ -7,8 +7,8 @@ module
prelude
public import Init.Data.Iterators.Combinators.Monadic.ULift
import all Init.Data.Iterators.Combinators.Monadic.ULift
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public section
@@ -20,9 +20,13 @@ variable {α : Type u} {m : Type u → Type u'} {n : Type max u v → Type v'}
theorem IterM.step_uLift [Iterator α m β] [Monad n] {it : IterM (α := α) m β}
[MonadLiftT m (ULiftT n)] :
(it.uLift n).step = (do
let step := ( (monadLift it.step : ULiftT n _).run).down
return .deflate Types.ULiftIterator.Monadic.modifyStep step.inflate.val, step.inflate.val, step.inflate.property, rfl) :=
rfl
match ( (monadLift it.step : ULiftT n _).run).down.inflate with
| .yield it' out h => return .deflate (.yield (it'.uLift n) (.up out) _, h, rfl)
| .skip it' h => return .deflate (.skip (it'.uLift n) _, h, rfl)
| .done h => return .deflate (.done _, h, rfl)) := by
simp only [IterM.step, Iterator.step, IterM.uLift]
apply bind_congr; intro step
split <;> simp [Types.ULiftIterator.Monadic.modifyStep, *]
@[simp]
theorem IterM.toList_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
@@ -33,14 +37,11 @@ theorem IterM.toList_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (
(fun l => l.down.map ULift.up) <$> (monadLift it.toList : ULiftT n _).run := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [IterM.toList_eq_match_step, IterM.toList_eq_match_step, step_uLift]
simp only [bind_pure_comp, bind_map_left, liftM_bind, ULiftT.run_bind, map_bind]
apply bind_congr
intro step
simp [Types.ULiftIterator.Monadic.modifyStep]
simp only [bind_assoc, map_eq_pure_bind, monadLift_bind, ULiftT.run_bind]
apply bind_congr; intro step
cases step.down.inflate using PlausibleIterStep.casesOn
· simp only [uLift] at ihy
simp [ihy _]
· exact ihs _
· simp [ihy _]
· simp [ihs _]
· simp
@[simp]
@@ -63,4 +64,20 @@ theorem IterM.toArray_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (
rw [ toArray_toList, toArray_toList, toList_uLift, monadLift_map]
simp
@[simp]
theorem IterM.count_uLift [Iterator α m β] [Monad m] [Monad n] {it : IterM (α := α) m β}
[MonadLiftT m (ULiftT n)] [Finite α m] [IteratorLoop α m m]
[LawfulMonad m] [LawfulMonad n] [LawfulIteratorLoop α m m]
[LawfulMonadLiftT m (ULiftT n)] :
(it.uLift n).count =
(.up ·.down.down) <$> (monadLift (n := ULiftT n) it.count).run := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [count_eq_match_step, count_eq_match_step, monadLift_bind, map_eq_pure_bind, step_uLift]
simp only [bind_assoc, ULiftT.run_bind]
apply bind_congr; intro step
cases step.down.inflate using PlausibleIterStep.casesOn
· simp [ihy _]
· simp [ihs _]
· simp
end Std.Iterators

View File

@@ -6,8 +6,8 @@ Authors: Paul Reichert
module
prelude
public import Std.Data.Iterators.Combinators.Take
public import Std.Data.Iterators.Lemmas.Combinators.Monadic.Take
public import Init.Data.Iterators.Combinators.Take
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Take
public import Init.Data.Iterators.Lemmas.Consumers
@[expose] public section
@@ -19,14 +19,19 @@ theorem Iter.take_eq_toIter_take_toIterM {α β} [Iterator α Id β] {n : Nat}
it.take n = (it.toIterM.take n).toIter :=
rfl
theorem Iter.toTake_eq_toIter_toTake_toIterM {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toTake = it.toIterM.toTake.toIter :=
rfl
theorem Iter.step_take {α β} [Iterator α Id β] {n : Nat}
{it : Iter (α := α) β} :
(it.take n).step = (match n with
| 0 => .done (.depleted rfl)
| k + 1 =>
match it.step with
| .yield it' out h => .yield (it'.take k) out (.yield h rfl)
| .skip it' h => .skip (it'.take (k + 1)) (.skip h rfl)
| .yield it' out h => .yield (it'.take k) out (Take.isPlausibleStep_take_yield h)
| .skip it' h => .skip (it'.take (k + 1)) (Take.isPlausibleStep_take_skip h)
| .done h => .done (.done h)) := by
simp only [Iter.step, Iter.step, Iter.take_eq_toIter_take_toIterM, IterM.step_take, toIterM_toIter]
cases n
@@ -88,11 +93,29 @@ theorem Iter.toArray_take_of_finite {α β} [Iterator α Id β] {n : Nat}
@[simp]
theorem Iter.toList_take_zero {α β} [Iterator α Id β]
[Finite (Take α Id β) Id]
[IteratorCollect (Take α Id β) Id Id] [LawfulIteratorCollect (Take α Id β) Id Id]
[Finite (Take α Id) Id]
[IteratorCollect (Take α Id) Id Id] [LawfulIteratorCollect (Take α Id) Id Id]
{it : Iter (α := α) β} :
(it.take 0).toList = [] := by
rw [toList_eq_match_step]
simp [step_take]
theorem Iter.step_toTake {α β} [Iterator α Id β] [Finite α Id]
{it : Iter (α := α) β} :
it.toTake.step = (
match it.step with
| .yield it' out h => .yield it'.toTake out (.yield h Nat.zero_ne_one)
| .skip it' h => .skip it'.toTake (.skip h Nat.zero_ne_one)
| .done h => .done (.done h)) := by
simp only [toTake_eq_toIter_toTake_toIterM, Iter.step, toIterM_toIter, IterM.step_toTake,
Id.run_bind]
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
@[simp]
theorem Iter.toList_toTake {α β} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
it.toTake.toList = it.toList := by
simp [toTake_eq_toIter_toTake_toIterM, toList_eq_toList_toIterM]
end Std.Iterators

View File

@@ -10,6 +10,7 @@ public import Init.Data.Iterators.Combinators.ULift
import all Init.Data.Iterators.Combinators.ULift
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.ULift
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Loop
public section
@@ -22,14 +23,16 @@ theorem Iter.uLift_eq_toIter_uLift_toIterM {it : Iter (α := α) β} :
rfl
theorem Iter.step_uLift [Iterator α Id β] {it : Iter (α := α) β} :
it.uLift.step =
Types.ULiftIterator.modifyStep it.step.val,
it.step.val.mapIterator Iter.toIterM, it.step.property,
by simp [Types.ULiftIterator.modifyStep] := by
it.uLift.step = match it.step with
| .yield it' out h => .yield it'.uLift (.up out) _, h, rfl
| .skip it' h => .skip it'.uLift _, h, rfl
| .done h => .done _, h, rfl := by
rw [Subtype.ext_iff]
simp only [uLift_eq_toIter_uLift_toIterM, step, IterM.Step.toPure, toIterM_toIter,
IterM.step_uLift, bind_pure_comp, Id.run_map, toIter_toIterM]
simp [Types.ULiftIterator.modifyStep, monadLift]
IterM.step_uLift, toIter_toIterM]
simp only [monadLift, ULiftT.run_pure, PlausibleIterStep.yield, PlausibleIterStep.skip,
PlausibleIterStep.done, pure_bind]
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
@[simp]
theorem Iter.toList_uLift [Iterator α Id β] {it : Iter (α := α) β}
@@ -55,4 +58,12 @@ theorem Iter.toArray_uLift [Iterator α Id β] {it : Iter (α := α) β}
rw [ toArray_toList, toArray_toList, toList_uLift]
simp [-toArray_toList]
@[simp]
theorem Iter.count_uLift [Iterator α Id β] {it : Iter (α := α) β}
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id] :
it.uLift.count = it.count := by
simp only [monadLift, uLift_eq_toIter_uLift_toIterM, count_eq_count_toIterM, toIterM_toIter]
rw [IterM.count_uLift]
simp [monadLift]
end Std.Iterators

View File

@@ -68,8 +68,7 @@ theorem Iter.forIn_eq {α β : Type w} [Iterator α Id β] [Finite α Id]
rw [ h]
theorem Iter.forIn'_eq_forIn'_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type w Type w''} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[Finite α Id] {m : Type w Type w''} [Monad m] [LawfulMonad m] [IteratorLoop α Id m]
{γ : Type w} {it : Iter (α := α) β} {init : γ}
{f : (out : β) _ γ m (ForInStep γ)} :
letI : ForIn' m (Iter (α := α) β) β _ := Iter.instForIn'
@@ -81,7 +80,7 @@ theorem Iter.forIn'_eq_forIn'_toIterM {α β : Type w} [Iterator α Id β]
theorem Iter.forIn_eq_forIn_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type w Type w''} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorLoop α Id m]
{γ : Type w} {it : Iter (α := α) β} {init : γ}
{f : β γ m (ForInStep γ)} :
ForIn.forIn it init f =
@@ -331,7 +330,7 @@ theorem Iter.foldM_eq_forIn {α β : Type w} {γ : Type x} [Iterator α Id β] [
theorem Iter.foldM_eq_foldM_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type w Type w''} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorLoop α Id m]
{γ : Type w} {it : Iter (α := α) β} {init : γ} {f : γ β m γ} :
it.foldM (init := init) f = it.toIterM.foldM (init := init) f := by
simp [foldM_eq_forIn, IterM.foldM_eq_forIn, forIn_eq_forIn_toIterM]
@@ -396,7 +395,7 @@ theorem Iter.fold_eq_foldM {α β : Type w} {γ : Type x} [Iterator α Id β]
simp [foldM_eq_forIn, fold_eq_forIn]
theorem Iter.fold_eq_fold_toIterM {α β : Type w} {γ : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[Finite α Id] [IteratorLoop α Id Id]
{f : γ β γ} {init : γ} {it : Iter (α := α) β} :
it.fold (init := init) f = (it.toIterM.fold (init := init) f).run := by
rw [fold_eq_foldM, foldM_eq_foldM_toIterM, IterM.fold_eq_foldM]
@@ -422,8 +421,9 @@ theorem Iter.fold_eq_match_step {α β : Type w} {γ : Type x} [Iterator α Id
cases step using PlausibleIterStep.casesOn <;> simp
-- The argument `f : γ₁ → γ₂` is intentionally explicit, as it is sometimes not found by unification.
theorem Iter.fold_hom [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
theorem Iter.fold_hom {γ₁ : Type x₁} {γ₂ : Type x₂} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id.{x₁}] [LawfulIteratorLoop α Id Id.{x₁}]
[IteratorLoop α Id Id.{x₂}] [LawfulIteratorLoop α Id Id.{x₂}]
{it : Iter (α := α) β}
(f : γ₁ γ₂) {g₁ : γ₁ β γ₁} {g₂ : γ₂ β γ₂} {init : γ₁}
(H : x y, g₂ (f x) y = f (g₁ x y)) :
@@ -469,30 +469,72 @@ theorem Iter.foldl_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [F
it.toArray.foldl (init := init) f = it.fold (init := init) f := by
rw [fold_eq_foldM, Array.foldl_eq_foldlM, Iter.foldlM_toArray]
@[simp]
theorem Iter.size_toArray_eq_size {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[IteratorSize α Id] [LawfulIteratorSize α]
theorem Iter.count_eq_count_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] {it : Iter (α := α) β} :
it.count = it.toIterM.count.run.down :=
(rfl)
theorem Iter.count_eq_fold {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] [LawfulIteratorLoop α Id Id.{w}]
[IteratorLoop α Id Id.{0}] [LawfulIteratorLoop α Id Id.{0}]
{it : Iter (α := α) β} :
it.toArray.size = it.size := by
simp only [toArray_eq_toArray_toIterM, LawfulIteratorCollect.toArray_eq]
simp [ toArray_eq_toArray_toIterM, LawfulIteratorSize.size_eq_size_toArray]
it.count = it.fold (γ := Nat) (init := 0) (fun acc _ => acc + 1) := by
rw [count_eq_count_toIterM, IterM.count_eq_fold, fold_eq_fold_toIterM]
rw [ fold_hom (f := ULift.down)]
simp
theorem Iter.count_eq_forIn {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id.{w}] [LawfulIteratorLoop α Id Id.{w}]
[IteratorLoop α Id Id.{0}] [LawfulIteratorLoop α Id Id.{0}]
{it : Iter (α := α) β} :
it.count = (ForIn.forIn (m := Id) it 0 (fun _ acc => return .yield (acc + 1))).run := by
rw [count_eq_fold, forIn_pure_yield_eq_fold, Id.run_pure]
theorem Iter.count_eq_match_step {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.count = (match it.step.val with
| .yield it' _ => it'.count + 1
| .skip it' => it'.count
| .done => 0) := by
simp only [count_eq_count_toIterM]
rw [IterM.count_eq_match_step]
simp only [bind_pure_comp, id_map', Id.run_bind, Iter.step]
cases it.toIterM.step.run.inflate using PlausibleIterStep.casesOn <;> simp
@[simp]
theorem Iter.length_toList_eq_size {α β : Type w} [Iterator α Id β] [Finite α Id]
theorem Iter.size_toArray_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[IteratorSize α Id] [LawfulIteratorSize α]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toList.length = it.size := by
rw [ toList_toArray, Array.length_toList, size_toArray_eq_size]
it.toArray.size = it.count := by
simp only [toArray_eq_toArray_toIterM, count_eq_count_toIterM, Id.run_map,
IterM.up_size_toArray_eq_count]
@[deprecated Iter.size_toArray_eq_count (since := "2025-10-29")]
def Iter.size_toArray_eq_size := @size_toArray_eq_count
@[simp]
theorem Iter.length_toListRev_eq_size {α β : Type w} [Iterator α Id β] [Finite α Id]
theorem Iter.length_toList_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[IteratorSize α Id] [LawfulIteratorSize α]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toListRev.length = it.size := by
rw [toListRev_eq, List.length_reverse, length_toList_eq_size]
it.toList.length = it.count := by
rw [ toList_toArray, Array.length_toList, size_toArray_eq_count]
@[deprecated Iter.length_toList_eq_count (since := "2025-10-29")]
def Iter.length_toList_eq_size := @length_toList_eq_count
@[simp]
theorem Iter.length_toListRev_eq_count {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} :
it.toListRev.length = it.count := by
rw [toListRev_eq, List.length_reverse, length_toList_eq_count]
@[deprecated Iter.length_toListRev_eq_count (since := "2025-10-29")]
def Iter.length_toListRev_eq_size := @length_toListRev_eq_count
theorem Iter.anyM_eq_forIn {α β : Type w} {m : Type Type w'} [Iterator α Id β]
[Finite α Id] [Monad m] [LawfulMonad m] [IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -726,4 +768,170 @@ theorem Iter.all_eq_not_any_not {α β : Type w} [Iterator α Id β]
· simp [ihs _]
· simp
theorem Iter.findSomeM?_eq_match_step {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (Option γ)} :
it.findSomeM? f = (do
match it.step.val with
| .yield it' out =>
match f out with
| none => it'.findSomeM? f
| some fx => return (some fx)
| .skip it' => it'.findSomeM? f
| .done => return none) := by
rw [findSomeM?, forIn_eq_match_step]
cases it.step using PlausibleIterStep.casesOn
· simp only [bind_assoc]
apply bind_congr; intro fx
split <;> simp [findSomeM?]
· simp [findSomeM?]
· simp
theorem Iter.findSomeM?_toList {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorCollect α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {f : β m (Option γ)} :
it.toList.findSomeM? f = it.findSomeM? f := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [it.findSomeM?_eq_match_step, it.toList_eq_match_step]
cases it.step using PlausibleIterStep.casesOn
· simp only [List.findSomeM?_cons]
apply bind_congr; intro fx
split <;> simp [ihy _]
· simp [ihs _]
· simp
theorem Iter.findSome?_eq_findSomeM? {α β : Type w} {γ : Type x}
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
{it : Iter (α := α) β} {f : β Option γ} :
it.findSome? f = Id.run (it.findSomeM? (pure <| f ·)) :=
(rfl)
theorem Iter.findSome?_eq_findSome?_toIterM {α β γ : Type w}
[Iterator α Id β] [IteratorLoop α Id Id.{w}] [Finite α Id]
{it : Iter (α := α) β} {f : β Option γ} :
it.findSome? f = (it.toIterM.findSome? f).run :=
(rfl)
theorem Iter.findSome?_eq_match_step {α β : Type w} {γ : Type x}
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
[LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} {f : β Option γ} :
it.findSome? f = (match it.step.val with
| .yield it' out =>
match f out with
| none => it'.findSome? f
| some fx => some fx
| .skip it' => it'.findSome? f
| .done => none) := by
rw [findSome?_eq_findSomeM?, findSomeM?_eq_match_step]
split
· simp only [pure_bind, findSome?_eq_findSomeM?]
split <;> simp
· simp [findSome?_eq_findSomeM?]
· simp
theorem Iter.findSome?_toList {α β : Type w} {γ : Type x}
[Iterator α Id β] [IteratorLoop α Id Id] [IteratorCollect α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {f : β Option γ} :
it.toList.findSome? f = it.findSome? f := by
simp [findSome?_eq_findSomeM?, List.findSome?_eq_findSomeM?, findSomeM?_toList]
theorem Iter.findSomeM?_pure {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorLoop α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β Option γ} :
it.findSomeM? (pure <| f ·) = pure (f := m) (it.findSome? f) := by
letI : IteratorCollect α Id Id := .defaultImplementation
simp [ findSomeM?_toList, findSome?_toList, List.findSomeM?_pure]
theorem Iter.findM?_eq_findSomeM? {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
{it : Iter (α := α) β} {f : β m (ULift Bool)} :
it.findM? f = it.findSomeM? (fun x => return if ( f x).down then some x else none) :=
(rfl)
theorem Iter.findM?_eq_match_step {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m]
{it : Iter (α := α) β} {f : β m (ULift Bool)} :
it.findM? f = (do
match it.step.val with
| .yield it' out =>
if ( f out).down then return (some out) else it'.findM? f
| .skip it' => it'.findM? f
| .done => return none) := by
rw [findM?_eq_findSomeM?, findSomeM?_eq_match_step]
split
· simp only [bind_assoc]
apply bind_congr; intro fx
split <;> simp [findM?_eq_findSomeM?]
· simp [findM?_eq_findSomeM?]
· simp
theorem Iter.findM?_toList {α β : Type} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorCollect α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {f : β m Bool} :
it.toList.findM? f = it.findM? (.up <$> f ·) := by
simp [findM?_eq_findSomeM?, List.findM?_eq_findSomeM?, findSomeM?_toList]
theorem Iter.findM?_eq_findM?_toList {α β : Type} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorCollect α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {f : β m (ULift Bool)} :
it.findM? f = it.toList.findM? (ULift.down <$> f ·) := by
simp [findM?_toList]
theorem Iter.find?_eq_findM? {α β : Type w} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] {it : Iter (α := α) β} {f : β Bool} :
it.find? f = Id.run (it.findM? (pure <| .up <| f ·)) :=
(rfl)
theorem Iter.find?_eq_find?_toIterM {α β : Type w} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] {it : Iter (α := α) β} {f : β Bool} :
it.find? f = (it.toIterM.find? f).run :=
(rfl)
theorem Iter.find?_eq_findSome? {α β : Type w} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] {it : Iter (α := α) β} {f : β Bool} :
it.find? f = it.findSome? (fun x => if f x then some x else none) := by
simp [find?_eq_findM?, findSome?_eq_findSomeM?, findM?_eq_findSomeM?]
theorem Iter.find?_eq_match_step {α β : Type w}
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β Bool} :
it.find? f = (match it.step.val with
| .yield it' out =>
if f out then some out else it'.find? f
| .skip it' => it'.find? f
| .done => none) := by
rw [find?_eq_findM?, findM?_eq_match_step]
split
· simp only [pure_bind]
split <;> simp [find?_eq_findM?]
· simp [find?_eq_findM?]
· simp
theorem Iter.find?_toList {α β : Type w}
[Iterator α Id β] [IteratorLoop α Id Id] [IteratorCollect α Id Id]
[Finite α Id] [LawfulIteratorLoop α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} {f : β Bool} :
it.toList.find? f = it.find? f := by
simp [find?_eq_findSome?, List.find?_eq_findSome?_guard, findSome?_toList, Option.guard_def]
theorem Iter.findM?_pure {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [IteratorLoop α Id Id]
[LawfulMonad m] [Finite α Id] [LawfulIteratorLoop α Id m] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β} {f : β ULift Bool} :
it.findM? (pure (f := m) <| f ·) = pure (f := m) (it.find? (ULift.down <| f ·)) := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [findM?_eq_match_step, find?_eq_match_step]
cases it.step using PlausibleIterStep.casesOn
· simp only [pure_bind]
split
· simp
· simp [ihy _]
· simp [ihs _]
· simp
end Std.Iterators

View File

@@ -335,6 +335,73 @@ theorem IterM.drain_eq_map_toArray {α β : Type w} {m : Type w → Type w'} [It
it.drain = (fun _ => .unit) <$> it.toList := by
simp [IterM.drain_eq_map_toList]
theorem IterM.count_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m]
{it : IterM (α := α) m β} :
it.count = it.fold (init := .up 0) (fun acc _ => .up <| acc.down + 1) :=
(rfl)
theorem IterM.count_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m]
{it : IterM (α := α) m β} :
it.count = ForIn.forIn it (.up 0) (fun _ acc => return .yield (.up (acc.down + 1))) :=
(rfl)
theorem IterM.count_eq_match_step {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
it.count = (do
match ( it.step).inflate.val with
| .yield it' _ => return .up (( it'.count).down + 1)
| .skip it' => return .up ( it'.count).down
| .done => return .up 0) := by
simp only [count_eq_fold]
have (acc : Nat) (it' : IterM (α := α) m β) :
it'.fold (init := ULift.up acc) (fun acc _ => .up (acc.down + 1)) =
(ULift.up <| ·.down + acc) <$>
it'.fold (init := ULift.up 0) (fun acc _ => .up (acc.down + 1)) := by
rw [ fold_hom]
· simp only [Nat.zero_add]; rfl
· simp only [ULift.up.injEq]; omega
rw [fold_eq_match_step]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp only [Nat.zero_add, bind_pure_comp]
rw [this 1]
· simp
· simp
@[simp]
theorem IterM.up_size_toArray_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.size) <$> it.toArray = it.count := by
rw [toArray_eq_fold, count_eq_fold, fold_hom]
· simp only [List.size_toArray, List.length_nil]; rfl
· simp
@[simp]
theorem IterM.up_length_toList_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toList = it.count := by
rw [toList_eq_fold, count_eq_fold, fold_hom]
· simp only [List.length_nil]; rfl
· simp
@[simp]
theorem IterM.up_length_toListRev_eq_count {α β : Type w} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
[IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} :
(.up <| ·.length) <$> it.toListRev = it.count := by
simp only [toListRev_eq, Functor.map_map, List.length_reverse, up_length_toList_eq_count]
theorem IterM.anyM_eq_forIn {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {p : β m (ULift Bool)} :
@@ -514,4 +581,126 @@ theorem IterM.all_eq_not_any_not {α β : Type w} {m : Type w → Type w'} [Iter
· simp [ihs _]
· simp
theorem IterM.findSomeM?_eq_match_step {α β γ : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Finite α m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β m (Option γ)} :
it.findSomeM? f = (do
match ( it.step).inflate.val with
| .yield it' out =>
match f out with
| none => it'.findSomeM? f
| some fx => return (some fx)
| .skip it' => it'.findSomeM? f
| .done => return none) := by
rw [findSomeM?, forIn_eq_match_step]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp only [bind_assoc]
apply bind_congr; intro fx
split <;> simp [findSomeM?]
· simp [findSomeM?]
· simp
theorem IterM.findSome?_eq_findSomeM? {α β γ : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
{it : IterM (α := α) m β} {f : β Option γ} :
it.findSome? f = it.findSomeM? (pure <| f ·) :=
(rfl)
theorem IterM.findSome?_eq_match_step {α β γ : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Finite α m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β Option γ} :
it.findSome? f = (do
match ( it.step).inflate.val with
| .yield it' out =>
match f out with
| none => it'.findSome? f
| some fx => return (some fx)
| .skip it' => it'.findSome? f
| .done => return none) := by
rw [findSome?_eq_findSomeM?, findSomeM?_eq_match_step]
apply bind_congr; intro step
split <;> simp [findSome?_eq_findSomeM?]
theorem IterM.findSomeM?_pure {α β γ : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m]
[LawfulMonad m] [Finite α m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β Option γ} :
it.findSomeM? (pure <| f ·) = it.findSome? f := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [findSomeM?_eq_match_step, findSome?_eq_match_step]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp only [pure_bind]
split <;> simp [ihy _]
· simp [ihs _]
· simp
theorem IterM.findM?_eq_findSomeM? {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [Finite α m]
{it : IterM (α := α) m β} {f : β m (ULift Bool)} :
it.findM? f = it.findSomeM? (fun x => return if ( f x).down then some x else none) :=
(rfl)
theorem IterM.findM?_eq_match_step {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Finite α m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β m (ULift Bool)} :
it.findM? f = (do
match ( it.step).inflate.val with
| .yield it' out =>
if ( f out).down then return (some out) else it'.findM? f
| .skip it' => it'.findM? f
| .done => return none) := by
rw [findM?_eq_findSomeM?, findSomeM?_eq_match_step]
apply bind_congr; intro step
split
· simp only [bind_assoc]
apply bind_congr; intro fx
split <;> simp [findM?_eq_findSomeM?]
· simp [findM?_eq_findSomeM?]
· simp
theorem IterM.find?_eq_findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [Finite α m] {it : IterM (α := α) m β} {f : β Bool} :
it.find? f = it.findM? (pure <| .up <| f ·) :=
(rfl)
theorem IterM.find?_eq_findSome? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β]
[IteratorLoop α m m] [LawfulMonad m] [Finite α m] {it : IterM (α := α) m β} {f : β Bool} :
it.find? f = it.findSome? (fun x => if f x then some x else none) := by
simp [find?_eq_findM?, findSome?_eq_findSomeM?, findM?_eq_findSomeM?]
theorem IterM.find?_eq_match_step {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m] [LawfulMonad m] [Finite α m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β Bool} :
it.find? f = (do
match ( it.step).inflate.val with
| .yield it' out =>
if f out then return (some out) else it'.find? f
| .skip it' => it'.find? f
| .done => return none) := by
rw [find?_eq_findM?, findM?_eq_match_step]
apply bind_congr; intro step
split
· simp only [pure_bind]
split <;> simp [find?_eq_findM?]
· simp [find?_eq_findM?]
· simp
theorem IterM.findM?_pure {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [IteratorLoop α m m]
[LawfulMonad m] [Finite α m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β} {f : β ULift Bool} :
it.findM? (pure (f := m) <| f ·) = it.find? (ULift.down <| f ·) := by
induction it using IterM.inductSteps with | step it ihy ihs
rw [findM?_eq_match_step, find?_eq_match_step]
apply bind_congr; intro step
cases step.inflate using PlausibleIterStep.casesOn
· simp only [pure_bind]
split
· simp
· simp [ihy _]
· simp [ihs _]
· simp
end Std.Iterators

View File

@@ -0,0 +1,10 @@
/-
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
public import Init.Data.Iterators.Lemmas.Producers.Monadic
public import Init.Data.Iterators.Lemmas.Producers.List

View File

@@ -7,8 +7,8 @@ module
prelude
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Std.Data.Iterators.Producers.List
public import Std.Data.Iterators.Lemmas.Producers.Monadic.List
public import Init.Data.Iterators.Producers.List
public import Init.Data.Iterators.Lemmas.Producers.Monadic.List
@[expose] public section

View File

@@ -1,10 +1,9 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
Authors: Paul Reichert
-/
module
prelude
import Lean.PremiseSelection.Basic
import Lean.PremiseSelection.MePo
public import Init.Data.Iterators.Lemmas.Producers.Monadic.List

View File

@@ -0,0 +1,71 @@
/-
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
public import Init.Data.Iterators.Lemmas.Consumers.Monadic
public import Init.Data.Iterators.Producers.Monadic.List
@[expose] public section
/-!
# Lemmas about list iterators
This module provides lemmas about the interactions of `List.iterM` with `IterM.step` and various
collectors.
-/
namespace Std.Iterators
open Std.Internal
variable {m : Type w Type w'} {n : Type w Type w''} [Monad m] {β : Type w}
@[simp]
theorem _root_.List.step_iterM_nil :
(([] : List β).iterM m).step = pure (.deflate .done, rfl) := by
simp only [IterM.step, Iterator.step]; rfl
@[simp]
theorem _root_.List.step_iterM_cons {x : β} {xs : List β} :
((x :: xs).iterM m).step = pure (.deflate .yield (xs.iterM m) x, rfl) := by
simp only [List.iterM, IterM.step, Iterator.step]; rfl
theorem _root_.List.step_iterM {l : List β} :
(l.iterM m).step = match l with
| [] => pure (.deflate .done, rfl)
| x :: xs => pure (.deflate .yield (xs.iterM m) x, rfl) := by
cases l <;> simp [List.step_iterM_cons, List.step_iterM_nil]
theorem ListIterator.toArrayMapped_iterM [Monad n] [LawfulMonad n]
{β : Type w} {γ : Type w} {lift : δ : Type w m δ n δ}
[LawfulMonadLiftFunction lift] {f : β n γ} {l : List β} :
IteratorCollect.toArrayMapped lift f (l.iterM m) (m := m) = List.toArray <$> l.mapM f := by
rw [LawfulIteratorCollect.toArrayMapped_eq]
induction l with
| nil =>
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp [List.step_iterM_nil, LawfulMonadLiftFunction.lift_pure]
| cons x xs ih =>
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp [List.step_iterM_cons, List.mapM_cons, pure_bind, ih, LawfulMonadLiftFunction.lift_pure]
@[simp]
theorem _root_.List.toArray_iterM [LawfulMonad m] {l : List β} :
(l.iterM m).toArray = pure l.toArray := by
simp only [IterM.toArray, ListIterator.toArrayMapped_iterM]
rw [List.mapM_pure, map_pure, List.map_id']
@[simp]
theorem _root_.List.toList_iterM [LawfulMonad m] {l : List β} :
(l.iterM m).toList = pure l := by
rw [ IterM.toList_toArray, List.toArray_iterM, map_pure, List.toList_toArray]
@[simp]
theorem _root_.List.toListRev_iterM [LawfulMonad m] {l : List β} :
(l.iterM m).toListRev = pure l.reverse := by
simp [IterM.toListRev_eq, List.toList_iterM]
end Std.Iterators

View File

@@ -0,0 +1,10 @@
/-
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
public import Init.Data.Iterators.Producers.Monadic
public import Init.Data.Iterators.Producers.List

View File

@@ -6,7 +6,7 @@ Authors: Paul Reichert
module
prelude
public import Std.Data.Iterators.Producers.Monadic.List
public import Init.Data.Iterators.Producers.Monadic.List
@[expose] public section

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