Compare commits

...

62 Commits

Author SHA1 Message Date
Kim Morrison
bcf63093e6 fix: update test to use public isGrindAnnotatedModule API
The test was accessing the private grindAnnotatedExt extension directly.
Updated to use the new public isGrindAnnotatedModule function instead.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 04:41:27 +01:00
Kim Morrison
1d8fa1faf5 perf: avoid re-exporting Std.Time from grind_annotated
This PR makes the `Std.Time.Format` import in `Lean.Elab.Tactic.Grind.Annotated`
private rather than public, preventing the entire `Std.Time` infrastructure
(including timezone databases) from being re-exported through `import Lean`.

The `grindAnnotatedExt` extension is kept private, with a new public accessor
function `isGrindAnnotatedModule` exposed for use by `LibrarySuggestions.Basic`.

This should address the +2.5% instruction increase on `import Lean` observed
after merging #11332.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 04:15:39 +01:00
Kim Morrison
387833be70 chore: update Claude prompting (#11370)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-26 02:53:36 +00:00
Kim Morrison
b68ac99d26 feat: try? uses parallelism (#11365)
This PR enables parallelism in `try?`. Currently, we replace the
`attempt_all` stages (there are two, one for builtin tactics including
`grind` and `simp_all`, and a second one for all user extensions) with
parallel versions. We do not (yet?) change the behaviour of `first`
based stages.
2025-11-26 01:42:06 +00:00
Mac Malone
e1f8c147e7 refactor: port shell option processing to Lean (#11345)
This PR moves the processing of options passed to the CLI from
`shell.cpp` to `Shell.lean`.

As with previous ports, this attempts to mirror as much of the original
behavior as possible, Benefits to be gained from the ported code can
come in later PRs. There should be no significant behavioral changes
from this port. Nonetheless, error reporting has changed some, hopefully
for the better. For instance, errors for improper argument
configurations has been made more consistent (e.g., Lean will now error
if numeric arguments fall outside the expected range for an option).
2025-11-25 23:39:31 +00:00
Henrik Böving
cef200fda6 perf: speed up termination of ElimDeadBranches compiler pass (#11362)
This PR accelerates termination of the ElimDeadBranches compiler pass.

The implementation addresses situations such as `choice [none, some
top]` which can be summarized to
`top` because `Option` has only two constructors and all constructor
arguments are `top`.
2025-11-25 22:52:43 +00:00
Leonardo de Moura
8ace95f99f feat: Field norm num (#11350)
This PR implements a helper simproc for `grind`. It is part of the
infrastructure used to cleanup denominators in `grind linarith`.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2025-11-25 19:47:31 +00:00
Robert J. Simmons
2e6769dcb3 chore: keep error explanations in sync (#11360)
This PR modifies some error explanations to remove warnings when
building the manual.
2025-11-25 19:03:07 +00:00
Robert J. Simmons
75d79819c3 feat: catch and provide context for misuse of NNG-style induction pattern (#11347)
This PR adds a focused error explanation aimed at the case where someone
tries to use Natural-Numbers-Game-style `induction` proofs directly in
Lean, where such proofs are not syntactically valid.

## Discussion

The natural numbers game uses a syntax that overlaps with Lean's
`induction` syntax despite having more structural similarity to
`induction'`. This means that fully correct proofs in the natural
numbers game, like this...

```lean4
import Mathlib
theorem zero_mul (m : ℕ) : 0 * m = 0 := by
  induction m with n n_ih
  rw [mul_zero]
  rfl
  rw [mul_succ]
  rw [add_zero]
  rw [n_ih]
  rfl
```

...have completely baffling error messages from a newcomers'
perspective:

```
notNaturalNumbersGame.lean:3:20: error: unknown tactic
notNaturalNumbersGame.lean:3:2: error: Alternative `zero` has not been provided
notNaturalNumbersGame.lean:3:2: error: Alternative `succ` has not been provided
```

(the Mathlib import here only provides the `ℕ` syntax here; equivalently
`ℕ` could be renamed to `Nat` and the import could be removed, [like
this](https://live.lean-lang.org/#codez=C4Cwpg9gTmC2AEAvMUIH1YFcA28AUCAXPAHICGwAlPMQAzwBU8CAvPPYWwEYCeAUPHgBLAHYATTAGNgQiCObwA7kNDx5ItEJAD4URfADaWbGmSoAujqgAzbFf1GcaAM5TJlwXsNkxY0yggPXQcNLSCbbCA))

There are many problems with this proof from the perspective of "stock"
Lean, but the error messages in the `induction` case are particularly
unfriendly and provide no guidance from a NNG learner's perspective.

This PR provides more information about what is wrong:

```
notNaturalNumbersGame.lean:3:20: error: unknown tactic
notNaturalNumbersGame.lean:3:14: error(lean.inductionWithNoAlts): Invalid syntax for induction tactic: The `with` keyword must followed by a tactic or by an alternative (e.g. `| zero =>`), but here it is followed by the identifier `n`.
```

The error explanation it links to explicitly flags the transition of
NNG-style proofs to Lean as the likely culprit, and gives an example of
an effective translation.
2025-11-25 18:44:40 +00:00
Markus Himmel
85d7f3321c feat: String.Slice.toInt? (#11358)
This PR adds `String.Slice.toInt?` and variants.

Closes #11275.
2025-11-25 15:48:41 +00:00
Markus Himmel
d99c515b16 refactor: String functions foldr, all, any, contains to go trough String.Slice (#11357)
This PR updates the `foldr`, `all`, `any` and `contains` functions on
`String` to be defined in terms of their `String.Slice` counterparts.

This is the last one in a long series of PRs. After this, all `String`
operations are polymorphic in the pattern, and no `String` operation
falls back to `String.Pos.Raw` internally (except those in the
`String.Pos.Raw` and `String.Substring.Raw` namespaces of course, which
still play a role in metaprogramming and will stay for the foreseeable
future).
2025-11-25 15:42:43 +00:00
Bhavik Mehta
aeddc0d22e feat: add lemmas for a / c < b / c on Int (#11327)
This PR adds two lemmas to prove `a / c < b / c`.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-25 15:04:39 +00:00
Garmelon
debafca7e1 chore: add radar-based bench suite for stdlib (#11264)
This PR adds a new [radar]-based [temci]-less bench suite that replaces
the `stdlib` benchmarks from the old suite and also measures per-module
instruction counts. All other benchmarks from the old suite are
unaffected.

The readme at `tests/bench-radar/README.md` explains in more detail how
the bench suite is structured and how it works. The readmes in the
benchmark subdirectories explain what each benchmark does and which
metrics it collects.

All metrics except `stdlib//max dynamic symbols` were ported to the new
suite, though most have been renamed.

[radar]: https://github.com/leanprover/radar
[temci]: https://github.com/parttimenerd/temci
2025-11-25 12:59:30 +00:00
Eric Wieser
9338aabed9 fix: move the monad argument for ForIn, ForIn', and ForM (#10204)
This PR changes the interface of the `ForIn`, `ForIn'`, and `ForM`
typeclasses to not take a `Monad m` parameter. This is a breaking change
for most downstream `instance`s, which will will now need to assume
`[Monad m]`.

The rationale is that if the provider of an instance requires `m` to be
a Monad, they should assume this up front. This makes it possible for
the instanve to assume `LawfulMonad m` or some other stronger
requirement, and also to provided a concrete instance for a particular
`m` without assuming a non-canonical `Monad` structure on it.

Zulip: [#lean4 > Monad assumptions in fields of other typeclasses @
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Monad.20assumptions.20in.20fields.20of.20other.20typeclasses/near/537102158)
2025-11-25 12:20:37 +00:00
Henrik Böving
b6e6094f85 chore: beta reduce in specialization keys (#11353)
This PR applies beta reduction to specialization keys, allowing us to
reuse specializations in more situations.
2025-11-25 12:14:36 +00:00
Markus Himmel
29ac158fcf feat: String.Pos.le_find (#11354)
This PR adds simple lemmas that show that searching from a position in a
string returns something that is at least that position.
2025-11-25 11:05:58 +00:00
Lean stage0 autoupdater
9b204f7a07 chore: update stage0 2025-11-25 09:53:33 +00:00
Kim Morrison
b0e6db3224 chore: activate grind_annotated in Init.Data.List.Lemmas (#11348)
This PR activates the `grind_annotated` command in
`Init.Data.List.Lemmas` by removing the TODO comment and uncommenting
the command.

This PR depends on #11346 (implement `grind_annotated` command) and
should be merged after that PR (and after CI has done an
`update-stage0`.
2025-11-25 04:23:48 +00:00
Kim Morrison
8a4fb762f3 feat: grind use/instantiate only can activate all scoped theorems in a namespace (#11335)
This PR enables the syntax `use [ns Foo]` and `instantiate only [ns
Foo]` inside a `grind` tactic block, and has the effect of activating
all grind patterns scoped to that namespace. We can use this to
implement specialized tactics using `grind`, but only controlled subsets
of theorems.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-25 02:41:08 +00:00
Kim Morrison
b46fd3e92d feat: with_weak_namespace command (#11338)
This PR upstreams the `with_weak_namespace` command from Mathlib:
`with_weak_namespace <id> <cmd>` changes the current namespace to `<id>`
for the duration of executing command `<cmd>`, without causing scoped
things to go out of scope. This is in preparation for upstreaming the
`scoped[Foo.Bar]` syntax from Mathlib, which will be useful now that we
are adding `grind` annotations in scopes.
2025-11-25 02:37:40 +00:00
Lean stage0 autoupdater
5bf6229626 chore: update stage0 2025-11-25 02:50:17 +00:00
Kim Morrison
2afca2df43 feat: implement grind_annotated command (#11332)
This PR adds a `grind_annotated "YYYY-MM-DD"` command that marks files
as manually annotated for grind.

When LibrarySuggestions is called with `caller := "grind"` (as happens
with `grind +suggestions`), theorems from grind-annotated files are
filtered out from premise selection. The date argument validates using
Std.Time and is informational only for now, but could be used later to
detect files that need re-review.

There's no need for the library suggestions tools to suggest `grind`
theorems from files that have already been carefully annotated by hand.
2025-11-25 02:12:35 +00:00
Kim Morrison
ae7c6b59bc feat: parallelism utilities for MetaM/TacticM (#11333)
This PR adds infrastructure for parallel execution across Lean's tactic
monads.

- Add IO.waitAny' to Init/System/IO.lean for waiting on task completion
- Add `Lean.Elab.Task` with `asTask` utilities for `CoreM`, `MetaM`,
`TermElabM`, `TacticM`
- Add `Lean.Elab.Parallel` with parallel execution strategies:
  * `par`/`par'` - collect results in original order
* `parIter`/`parIterGreedy` - iterate over results (original or
completion order) (also variants with a cancellation token)
  * `parFirst` - return first successful result

This does *not* attempt to be a monad-polymorphic framework for
parallelism. It's intentionally hard-coded to the Lean tactic monads
which I need to work with. If there's desire to make this polymorphic,
hopefully that can be done separately.
2025-11-24 23:42:30 +00:00
Wrenna Robson
c574a85845 feat: add getElem_swapIfInBounds* lemmas and deprecate getElem_swap' (#8406)
This PR adds lemmas of the form `getElem_swapIfInBounds*` and deprecates
`getElem_swap'`.
2025-11-24 23:41:12 +00:00
Henrik Böving
57afb23c5c fix: compilation of projections on non trivial structures (#11340)
This PR fixes a miscompilation when encountering projections of non
trivial structure types.

Closes: #11322
2025-11-24 19:25:03 +00:00
Markus Himmel
151c034f4f refactor: rename String.bytes to String.toByteArray (#11343)
This PR renames `String.bytes` to `String.toByteArray`.

This is for two reasons: first, `toByteArray` is a better name, and
second, we have something else that wants to use the name `bytes`,
namely the function that returns in iterator over the string's bytes.
2025-11-24 18:59:49 +00:00
Lean stage0 autoupdater
2308e3a0a5 chore: update stage0 2025-11-24 18:43:44 +00:00
Joachim Breitner
096d3ce83f feat: document that backward options may disappear (#11304)
This PR documents that `backward.*` options are only temporary
migration aids and may disappear without further notice after 6 months
after their introduction. Users are kindly asked to report if they rely
on these options.
2025-11-24 17:49:46 +00:00
Markus Himmel
96c4b9ee4d feat: coercion from String to String.Slice (#11341)
This PR adds a coercion from `String` to `String.Slice`.

In our envisioned future, most functions operating on strings will
accept `String.Slice` parameters by default (like `str` in Rust), and
this enables calling such functions with arguments of type `String`.

Closes #11298.
2025-11-24 16:50:08 +00:00
Markus Himmel
fa67f300f6 chore: rename String.ValidPos to String.Pos (#11240)
This PR renames `String.ValidPos` to `String.Pos`, `String.endValidPos`
to `String.endPos` and `String.startValidPos` to `String.startPos`.

Accordingly, the deprecations of `String.Pos` to `String.Pos.Raw` and
`String.endPos` to `String.rawEndPos` are removed early, after an
abbreviated deprecation cycle of two releases.
2025-11-24 16:40:21 +00:00
Paul Reichert
6da35eeccb refactor: increase runtime of "sigma iterator" benchmark (#11336)
This PR makes the "sigma iterator" benchmark more compute-intensive
because it was too fast and therefore flaky.
2025-11-24 12:21:27 +00:00
Joachim Breitner
54a10f0790 feat: remove the group field of an option description (#11305)
This PR removes the `group` field from option descriptions. It is
unused, does not have a clear meaning and often matches the first
component of the option name.
2025-11-24 11:40:58 +00:00
Sebastian Ullrich
72573928b1 chore: CI: re-enable fsanitize job (#11258)
Given its run time of >2hrs, the job is added as a secondary job for
nightly releases and a primary job for full releases. A new check level
for differentiating between nightlies and full releases is added for
this.

(Trying to) reactivate lsan will happen in a follow-up PR.
2025-11-24 11:12:25 +00:00
Sebastian Ullrich
bfbad53540 fix: avoid storing reference to environment in realization result to prevent promise cycle (#11328)
This PR fixes freeing memory accidentally retained for each document
version in the language server on certain elaboration workloads. The
issue must have existed since 4.18.0.
2025-11-24 10:16:56 +00:00
Leonardo de Moura
f2e191d0af refactor: grind linarith ring normalization (#11334)
This PR adds an explicit normalization layer for ring constraints in the
`grind linarith` module. For example, it will be used to clean up
denominators when the ring is a field.
2025-11-24 03:11:13 +00:00
Leonardo de Moura
0b173923f4 feat: LawfulOfScientific in grind (#11331)
This PR adds support for the `LawfulOfScientific` class in `grind`.
Examples:
```lean
open Lean Grind Std
variable [LE α] [LT α] [LawfulOrderLT α] [Field α] [OfScientific α]
         [LawfulOfScientific α] [IsLinearOrder α] [OrderedRing α]
example : (2 / 3 : α) ≤ (0.67 : α) := by  grind
example : (1.2 : α) ≤ (1.21 : α) := by grind
example : (2 / 3 : α) ≤ (67 / 100 : α) := by grind
example : (1.2345 : α) ≤ (1.2346 : α) := by grind
example : (2.3 : α) ≤ (4.5 : α) := by grind
example : (2.3 : α) ≤ (5/2 : α) := by grind
```
2025-11-24 00:14:12 +00:00
Kim Morrison
bd711e3a7a feat: rename cutsat to lia with deprecation warning (#11330)
This PR renames the `cutsat` tactic to `lia` for better alignment with
standard terminology in the theorem proving community.

`cutsat` still works but now emits a deprecation warning and suggests
using `lia` instead via "Try this:". Both tactics have identical
behavior.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-23 23:26:00 +00:00
Markus Himmel
e6a07ca6b1 refactor: deprecate String.posOf and variants in favor of unified String.find (#11276)
This PR cleans up the API around `String.find` and moves it uniformly to
the new position types `String.ValidPos` and `String.Slice.Pos`

Overview:

- To search for a character, character predicate, string or slice in a
string or slice `s`, use `s.find?` or `s.find`.
- To do the same, but starting at a position `p` of a string or slice,
use `p.find?` or `p.find`.
- To do the same but between two positions `p` and `q`, construct the
slice from `p` to `q` and then use `find?` or `find` on that.
- To search backwards, all of the above applies, except that the
function is called `revFind?`, there is no non-question-mark version
(use `getD` if there is a sane default return value in your specific
application), and that you can only search for characters and character
predicates, not strings or slices.
2025-11-23 18:39:53 +00:00
Leonardo de Moura
216f7e8753 feat: grind proof parameters whose type is not a forall (#11326)
This PR ensures that users can provide `grind` proof parameters whose
types are not `forall`-quantified. Examples:

```lean
opaque f : Nat → Nat
axiom le_f (a : Nat) : a ≤ f a

example (a : Nat) : a ≤ f a := by
  grind [le_f a]

example (a b : α) (h : ∀ x y : α, x = y) : a = b := by
  grind [h a b]
```
2025-11-23 18:36:04 +00:00
Markus Himmel
fba166eea0 chore: expose more String.Slice functions on String (#11308)
This PR redefines `front` and `back` on `String` to go through
`String.Slice` and adds the new `String` functions `front?`, `back?`,
`positions`, `chars`, `revPositions`, `revChars`, `byteIterator`,
`revBytes`, `lines`.
2025-11-23 15:33:16 +00:00
Kim Morrison
4311237321 chore: add CoreM.toIO' (#11325)
This PR adds `CoreM.toIO'`, the analogue of `CoreM.toIO` dropping the
state from the return type, and similarly for `TermElabM.toIO'` and
`MetaM.toIO'`.
2025-11-23 10:59:15 +00:00
Leonardo de Moura
4135674021 feat: add funCC (function-valued congruence closure) to grind (#11323)
This PR introduces a new `grind` option, `funCC` (enabled by default),
which extends congruence closure to *function-valued* equalities. When
`funCC` is enabled, `grind` tracks equalities of **partially applied
functions**, allowing reasoning steps such as:
```lean
a : Nat → Nat 
f : (Nat → Nat) → (Nat → Nat)
h : f a = a
⊢ (f a) m = a m

g : Nat → Nat
f : Nat → Nat → Nat
h : f a = g
⊢ f a b = g b
```

Given an application `f a₁ a₂ … aₙ`, when `funCC := true` and function
equality is enabled for `f`, `grind` generates and tracks equalities for
all partial applications:

* `f a₁`
* `f a₁ a₂`
* …
* `f a₁ a₂ … aₙ`

This allows equalities such as `f a₁ = g` to propagate through further
applications.

**When is function equality enabled for a symbol?**

Function equality is enabled for `f` in the following cases:

1. `f` is **not a constant** (e.g., a lambda, a local function, or a
function parameter).
2. `f` is a **structure field projection**, provided the structure is
**not a `class`**.
3. `f` is a constant marked with  `@[grind funCC]`

Users can also enable function equality for specific constants in a
single call using:
```lean
grind [funCC f, funCC g]
```

**Examples:**

```lean
example (m : Nat) (a : Nat → Nat) (f : (Nat → Nat) → (Nat → Nat)) (h : f a = a) :
    f a m = a m := by
  grind

example (m : Nat) (a : Nat → Nat) (f : (Nat → Nat) → (Nat → Nat)) (h : f a = a) :
    f a m = a m := by
  fail_if_success grind -funCC -- fails if `funCC` is disabled
  grind
```

```lean
example (a b : Nat) (g : Nat → Nat) (f : Nat → Nat → Nat) (h : f a = g) :
    f a b = g b := by
  grind

example (a b : Nat) (g : Nat → Nat) (f : Nat → Nat → Nat) (h : f a = g) :
    f a b = g b := by
  fail_if_success grind -funCC
  grind
```

**Enabling per-symbol with parameters or attributes**

```lean
opaque f : Nat → Nat → Nat
opaque g : Nat → Nat

example (a b c : Nat) : f a = g → b = c → f a b = g c := by
  grind [funCC f, funCC g]

attribute [grind funCC] f g

example (a b c : Nat) : f a = g → b = c → f a b = g c := by
  grind
```

This feature substantially improves `grind`’s support for higher-order
and partially-applied function equalities, while preserving
compatibility with first-order SMT behavior when `funCC` is disabled.

Closes #11309
2025-11-23 05:06:41 +00:00
Paul Reichert
2980155f5c refactor: simplify ToIterator (#11242)
This PR significantly changes the signature of the `ToIterator` type
class. The obtained iterators' state is no longer dependently typed and
is an `outParam` instead of being bundled inside the class. Among other
benefits, `simp` can now rewrite inside of `Slice.toList` and
`Slice.toArray`. The downside is that we lose flexibility. For example,
the former combinator-based implementation of `Subarray`'s iterators is
no longer feasible because the states are dependently typed. Therefore,
this PR provides a hand-written iterator for `Subarray`, which does not
require a dependently typed state and is faster than the previous one.

Converting a family of dependently typed iterators into a simply typed
one using a `Sigma`-state iterator generates forbiddingly bad code, so
that we do provide such a combinator. This PR adds a benchmark for this
problem.
2025-11-22 12:37:18 +00:00
Leonardo de Moura
0818cf6483 feat: improves Fin n support in grind (#11319)
This PR improves the support for `Fin n` in `grind` when `n` is not a
numeral.

- `toInt (0 : Fin n) = 0` in `grind lia`.
- `Fin.mk`-applications are treated as interpreted terms in `grind lia`.
- `Fin.val` applications are suppressed from `grind lia`
counterexamples.
2025-11-22 06:51:25 +00:00
Mac Malone
c1a82c4bd7 chore: lake: update tests/toml (#11314)
This PR fixes a breakage in Lake's TOML test caused by String API
changes. It also removes a JSON parser workaround that has since been
fixed, and it more generally polishes up the code.
2025-11-22 04:41:58 +00:00
Leonardo de Moura
db4206f2a9 fix: instantiate metavariables in hypotheses in grind (#11315)
This PR fixes an issue affecting `grind -revert`. In this mode, assigned
metavariables in hypotheses were not being instantiated. This issue was
affecting two files in Mathlib.
2025-11-22 04:28:53 +00:00
Leonardo de Moura
a0772dc82d fix: grind internalization (#11318)
This PR fixes a local declaration internalization in `grind` that was
exposed when using `grind -revert`. This bug was affecting a `grind`
proof in Mathlib.
2025-11-22 04:24:11 +00:00
Kim Morrison
90389a8d90 feat: improvements to grind annotations for Fin (#11299)
This PR add many `@[grind]` annotations for `Fin`, and updates the
tests.
2025-11-22 02:48:48 +00:00
Kim Morrison
26b435fa4d feat: grind_pattern for Subtype.property (#11317)
This PR adds `grind_pattern Subtype.property => self.val`.
2025-11-22 02:23:09 +00:00
Kim Morrison
fd4ff1f7e2 feat: grind_pattern for Exists.choose_spec (#11316)
This PR adds `grind_pattern Exists.choose_spec => P.choose`.
2025-11-22 02:19:00 +00:00
Henrik Böving
80224c72c9 perf: improve specializer cache keys (#11310)
This PR makes the specializer (correctly) share more cache keys across
invocations, causing us to produce less code bloat.

We observed that in functions with lots of specialization, sometimes
cache keys are defeq but not BEq because one has unused let decls
(introduced by specialization) that the other doesn't. This PR resolves
this conflict by erasing unused let decls from specializer cache keys.
2025-11-21 23:21:40 +00:00
Robert J. Simmons
3a309ba4eb feat: improve error message in the case of type class synthesis failure (#11245)
This PR improves the error message encountered in the case of a type
class instance resolution failure, and adds an error explanation that
discusses the common new-user case of binary operation overloading and
points to the `trace.Meta.synthInstance` option for advanced debugging.

## Example

```lean4
def f (x : String) := x + x
```

Before:
```
failed to synthesize
  HAdd String String ?m.5

Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command.
```

After:
```
failed to synthesize instance of type class
  HAdd String String ?m.5

Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
Error code: lean.failedToSynthesizeTypeclassInstance
[View explanation](https://lean-lang.org/doc/reference/latest/find/?domain=Manual.errorExplanation&name=lean.failedToSynthesizeTypeclassInstance)
```

The error message is changed in three important ways:
* Explains *what* failed to synthesize, using the "type class"
terminology that's more likely to be recognized than the "instance"
terminology
* Points to the `trace.Meta.synthInstance` option which is otherwise
nearly undiscoverable but is quite powerful (see also
leanprover/reference-manual#663 which is adding commentary on this
option)
* Gives an error explanation link (which won't actually work until the
next release after this is merged) which prioritizes the common-case
explanation of using the wrong binary operation
2025-11-21 21:24:27 +00:00
Joachim Breitner
4288aa71e0 chore: do not set unused Option.Decl.group (#11307)
This PR removes all code that sets the `Option.Decl.group` field, which
is unused and has no clearly documented meaning.

The actual removal of the field would be #11305.
2025-11-21 16:44:38 +00:00
Joachim Breitner
0471319b5a chore: tests: use filenames as test names (#11302)
This PR renames the CTests tests to use filenames as test names. So
instead of
```
        2080 - leanruntest_issue5767.lean (Failed)
```
we get
```
        2080 - tests/lean/run/issue5767.lean (Failed)
```
which allows Ctrl-Click’ing on them in the VSCode terminal.
2025-11-21 12:40:58 +00:00
Wojciech Różowski
2e22c854cb feat: add intersection on ExtDTreeMap/ExtTreeMap/ExtTreeSet (#11292)
This PR adds intersection operation on
`ExtDTreeMap`/`ExtTreeMap`/`ExtTreeSet` and proves several lemmas about
it.
2025-11-21 11:25:58 +00:00
Wojciech Różowski
e7ece45e3c refactor: rename congruence lemmas for union on DHashMap/HashMap/HashSet/DTreeMap/TreeMap/TreeSet (#11267)
This PR renames congruence lemmas for union on
`DHashMap`/`HashMap`/`HashSet`/`DTreeMap`/`TreeMap`/`TreeSet` to fit the
convention of being in the `Equiv` namespace.
2025-11-21 11:25:00 +00:00
Markus Himmel
dda6885eae refactor: String.foldl and String.isNat go through String.Slice (#11289)
This PR redefines `String.foldl`, `String.isNat` to use their
`String.Slice` counterparts.
2025-11-21 11:17:50 +00:00
Joachim Breitner
cce4873c25 chore: rename wrongly named backwards. options to backward. (#11303)
This PR renames rename wrongly named `backwards.` options to
`backward.`
2025-11-21 10:57:56 +00:00
Joachim Breitner
dedf7a8f44 feat: allow setting reducibilityCoreExt in async contexts (#11301)
This PR allows setting reducibilityCoreExt in async contexts (e.g. when
using `mkSparseCasesOn` in a realizable definition)
2025-11-21 09:23:14 +00:00
Kim Morrison
01335863e6 chore: add #grint_lint exception for sizeOf_spec lemmas (#11300) 2025-11-21 09:02:19 +00:00
Kim Morrison
1aecd85e0c chore: update stage0 2025-11-21 19:35:21 +11:00
Kim Morrison
4f7c5f4dca feat: #grind_lint skip suffix
delete old grind_lint

.

move exception to separate file

note about stage0
2025-11-21 19:35:21 +11:00
1059 changed files with 9363 additions and 3687 deletions

View File

@@ -1,14 +1,34 @@
To build Lean you should use `make -j -C build/release`.
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
## New features
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`.
All new tests should go in `tests/lean/run/`. These tests don't have expected output; we just check there are no errors. You should use `#guard_msgs` to check for specific messages.
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
## Success Criteria
*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.
*Never* report success on a task unless you have verified both a clean build without errors, and that the relevant tests pass.
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.
## Build System Safety
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.
**NEVER manually delete build directories** (build/, stage0/, stage1/, etc.) even when builds fail.
- ONLY use the project's documented build command: `make -j -C build/release`
- If a build is broken, ask the user before attempting any manual cleanup
## LSP and IDE Diagnostics
After rebuilding, LSP diagnostics may be stale until the user interacts with files. Trust command-line test results over IDE diagnostics.
## Update prompting when the user is frustrated
If the user expresses frustration with you, stop and ask them to help update this `.claude/CLAUDE.md` file with missing guidance.
## Creating pull requests.
All PRs must have a first paragraph starting with "This PR". This paragraph is automatically incorporated into release notes. Read `lean4/doc/dev/commit_convention.md` when making PRs.

View File

@@ -46,6 +46,8 @@ jobs:
CCACHE_MAXSIZE: 400M
# squelch error message about missing nixpkgs channel
NIX_BUILD_SHELL: bash
# TODO
ASAN_OPTIONS: detect_leaks=0
LSAN_OPTIONS: max_leaks=10
# somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
CXX: c++

View File

@@ -108,7 +108,8 @@ jobs:
# 0: PRs without special label
# 1: PRs with `merge-ci` label, merge queue checks, master commits
# 2: PRs with `release-ci` label, releases (incl. nightlies)
# 2: nightlies
# 2: PRs with `release-ci` label, full releases
- name: Set check level
id: set-level
# We do not use github.event.pull_request.labels.*.name here because
@@ -118,14 +119,16 @@ jobs:
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
if [[ -n "${{ steps.set-release.outputs.RELEASE_TAG }}" || -n "${{ steps.set-release-custom.outputs.RELEASE_TAG }}" ]]; then
check_level=3
elif [[ -n "${{ steps.set-nightly.outputs.nightly }}" ]]; then
check_level=2
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
else
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels')"
if echo "$labels" | grep -q "release-ci"; then
check_level=2
check_level=3
elif echo "$labels" | grep -q "merge-ci"; then
check_level=1
fi
@@ -210,17 +213,18 @@ jobs:
"test": true,
"CMAKE_PRESET": "reldebug",
},
// TODO: suddenly started failing in CI
/*{
{
"name": "Linux fsanitize",
"os": "ubuntu-latest",
"enabled": level >= 2,
// do not have nightly release wait for this
"secondary": level <= 2,
"test": true,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_PRESET": "sanitize",
// exclude seriously slow/problematic tests (laketests crash)
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},*/
// exclude seriously slow/problematic tests (laketests crash, async_base_functions timeouts)
"CTEST_OPTIONS": "-E '(interactive|pkg|lake|bench)/|treemap|StackOverflow|async_base_functions'"
},
{
"name": "macOS",
"os": "macos-15-intel",
@@ -252,7 +256,7 @@ jobs:
},
{
"name": "Windows",
"os": large && (fast || 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,

View File

@@ -41,7 +41,7 @@
"SMALL_ALLOCATOR": "OFF",
"USE_MIMALLOC": "OFF",
"BSYMBOLIC": "OFF",
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000"
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000 ASAN_OPTIONS=detect_leaks=0"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/sanitize"

View File

@@ -300,7 +300,7 @@ 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 + '\n'
let insertion := text.findAux (· == '\n') text.rawEndPos insertion + '\n'
pure (path, inputCtx, header, insertion)
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
@@ -593,16 +593,16 @@ def main (args : List String) : IO UInt32 := do
for stx in imports do
let mod := decodeImport stx
if remove.contains mod || seen.contains mod then
out := out ++ text.extract pos stx.raw.getPos?.get!
out := out ++ String.Pos.Raw.extract text 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.rawEndPos stx.raw.getTailPos?.get! + '\n'
seen := seen.insert mod
out := out ++ text.extract pos insertion
out := out ++ String.Pos.Raw.extract text 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.rawEndPos
out := out ++ String.Pos.Raw.extract text insertion text.rawEndPos
IO.FS.writeFile path out
count := count + 1

View File

@@ -1,96 +0,0 @@
#!/usr/bin/env bash
set -euxo pipefail
cmake --preset release 1>&2
# We benchmark against stage2/bin to test new optimizations.
timeout -s KILL 1h time make -C build/release -j$(nproc) stage3 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
# The extra opts used to be passed to the Makefile during benchmarking only but with Lake it is
# easier to configure them statically.
cmake -B build/release/stage3 -S src -DLEAN_EXTRA_LAKEFILE_TOML='weakLeanArgs=["-Dprofiler=true", "-Dprofiler.threshold=9999999", "--stats"]' 1>&2
(
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2
)
if [ -d .git ]; then
DIR="$(git rev-parse @)"
BASE_URL="https://speed.lean-lang.org/lean4-out/$DIR"
{
cat <<'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lakeprof Report</title>
</head>
<h1>Lakeprof Report</h1>
<button type="button" id="btn_fetch">View build trace in Perfetto</button>
<script type="text/javascript">
const ORIGIN = 'https://ui.perfetto.dev';
const btnFetch = document.getElementById('btn_fetch');
async function fetchAndOpen(traceUrl) {
const resp = await fetch(traceUrl);
// Error checking is left as an exercise to the reader.
const blob = await resp.blob();
const arrayBuffer = await blob.arrayBuffer();
openTrace(arrayBuffer, traceUrl);
}
function openTrace(arrayBuffer, traceUrl) {
const win = window.open(ORIGIN);
if (!win) {
btnFetch.style.background = '#f3ca63';
btnFetch.onclick = () => openTrace(arrayBuffer);
btnFetch.innerText = 'Popups blocked, click here to open the trace file';
return;
}
const timer = setInterval(() => win.postMessage('PING', ORIGIN), 50);
const onMessageHandler = (evt) => {
if (evt.data !== 'PONG') return;
// We got a PONG, the UI is ready.
window.clearInterval(timer);
window.removeEventListener('message', onMessageHandler);
const reopenUrl = new URL(location.href);
reopenUrl.hash = `#reopen=${traceUrl}`;
win.postMessage({
perfetto: {
buffer: arrayBuffer,
title: 'Lake Build Trace',
url: reopenUrl.toString(),
}}, ORIGIN);
};
window.addEventListener('message', onMessageHandler);
}
// This is triggered when following the link from the Perfetto UI's sidebar.
if (location.hash.startsWith('#reopen=')) {
const traceUrl = location.hash.substr(8);
fetchAndOpen(traceUrl);
}
EOF
cat <<EOF
btnFetch.onclick = () => fetchAndOpen("$BASE_URL/lakeprof.trace_event");
</script>
EOF
echo "<pre><code>"
(cd src; lakeprof report -prc)
echo "</code></pre>"
echo "</body></html>"
} | tee index.html
curl -T index.html $BASE_URL/index.html
curl -T src/lakeprof.log $BASE_URL/lakeprof.log
curl -T src/lakeprof.trace_event $BASE_URL/lakeprof.trace_event
fi

View File

@@ -205,3 +205,5 @@ export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
/-- Show that an element extracted from `P : ∃ a, p a` using `P.choose` satisfies `p`. -/
theorem Exists.choose_spec {p : α Prop} (P : a, p a) : p P.choose := Classical.choose_spec P
grind_pattern Exists.choose_spec => P.choose

View File

@@ -25,7 +25,7 @@ instances are provided for the same type.
instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α where
forIn x b f := forIn' x b fun a _ => f a
@[simp] theorem forIn'_eq_forIn [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m] (x : ρ) (b : β)
@[simp] theorem forIn'_eq_forIn [d : Membership α ρ] [ForIn' m ρ α d] {β} (x : ρ) (b : β)
(f : (a : α) a x β m (ForInStep β)) (g : (a : α) β m (ForInStep β))
(h : a m b, f a m b = g a b) :
forIn' x b f = forIn x b g := by
@@ -40,7 +40,7 @@ instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α
simp [h]
rfl
@[wf_preprocess] theorem forIn_eq_forIn' [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m]
@[wf_preprocess] theorem forIn_eq_forIn' [d : Membership α ρ] [ForIn' m ρ α d] {β}
(x : ρ) (b : β) (f : (a : α) β m (ForInStep β)) :
forIn x b f = forIn' x b (fun x h => binderNameHint x f <| binderNameHint h () <| f x) := by
rfl
@@ -403,7 +403,7 @@ class ForM (m : Type u → Type v) (γ : Type w₁) (α : outParam (Type w₂))
/--
Runs the monadic action `f` on each element of the collection `coll`.
-/
forM [Monad m] (coll : γ) (f : α m PUnit) : m PUnit
forM (coll : γ) (f : α m PUnit) : m PUnit
export ForM (forM)

View File

@@ -377,7 +377,7 @@ class ForIn (m : Type u₁ → Type u₂) (ρ : Type u) (α : outParam (Type v))
More information about the translation of `for` loops into `ForIn.forIn` is available in [the Lean
reference manual](lean-manual://section/monad-iteration-syntax).
-/
forIn {β} [Monad m] (xs : ρ) (b : β) (f : α β m (ForInStep β)) : m β
forIn {β} (xs : ρ) (b : β) (f : α β m (ForInStep β)) : m β
export ForIn (forIn)
@@ -405,7 +405,7 @@ class ForIn' (m : Type u₁ → Type u₂) (ρ : Type u) (α : outParam (Type v)
More information about the translation of `for` loops into `ForIn'.forIn'` is available in [the
Lean reference manual](lean-manual://section/monad-iteration-syntax).
-/
forIn' {β} [Monad m] (x : ρ) (b : β) (f : (a : α) a x β m (ForInStep β)) : m β
forIn' {β} (x : ρ) (b : β) (f : (a : α) a x β m (ForInStep β)) : m β
export ForIn' (forIn')

View File

@@ -242,7 +242,7 @@ Examples:
* `#["red", "green", "blue", "brown"].swapIfInBounds 0 4 = #["red", "green", "blue", "brown"]`
* `#["red", "green", "blue", "brown"].swapIfInBounds 9 2 = #["red", "green", "blue", "brown"]`
-/
@[extern "lean_array_swap", grind]
@[extern "lean_array_swap", expose]
def swapIfInBounds (xs : Array α) (i j : @& Nat) : Array α :=
if h₁ : i < xs.size then
if h₂ : j < xs.size then swap xs i j
@@ -570,7 +570,7 @@ protected def forIn' {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance : ForIn' m (Array α) α inferInstance where
instance [Monad m] : ForIn' m (Array α) α inferInstance where
forIn' := Array.forIn'
-- No separate `ForIn` instance is required because it can be derived from `ForIn'`.
@@ -1001,7 +1001,7 @@ unless `start < stop`. By default, the entire array is used.
protected def forM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Array α) (start := 0) (stop := as.size) : m PUnit :=
as.foldlM (fun _ => f) start stop
instance : ForM m (Array α) α where
instance [Monad m] : ForM m (Array α) α where
forM xs f := Array.forM f xs
-- We simplify `Array.forM` to `forM`.

View File

@@ -3966,28 +3966,29 @@ theorem getElem_modify_of_ne {xs : Array α} {i : Nat} (h : i ≠ j)
/-! ### swap -/
@[simp] theorem getElem_swap_right {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[j]'(by simpa using hj) = xs[i] := by
simp [swap_def]
@[simp] theorem getElem_swap_left {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[i]'(by simpa using hi) = xs[j] := by
simp +contextual [swap_def, getElem_set]
@[simp] theorem getElem_swap_of_ne {xs : Array α} {i j : Nat} {hi hj} (hp : k < xs.size)
(hi' : k i) (hj' : k j) : (xs.swap i j hi hj)[k]'(xs.size_swap .. |>.symm hp) = xs[k] := by
simp [swap_def, getElem_set, hi'.symm, hj'.symm]
theorem getElem_swap' {xs : Array α} {i j : Nat} {hi hj} {k : Nat} (hk : k < xs.size) :
(xs.swap i j hi hj)[k]'(by simp_all) = if k = i then xs[j] else if k = j then xs[i] else xs[k] := by
split
· simp_all only [getElem_swap_left]
· split <;> simp_all
@[grind =]
theorem getElem_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} (hk : k < (xs.swap i j hi hj).size) :
(xs.swap i j hi hj)[k] = if k = i then xs[j] else if k = j then xs[i] else xs[k]'(by simp_all) := by
apply getElem_swap'
simp only [swap_def, getElem_set, eq_comm (a := k)]
split <;> split <;> simp_all
@[simp] theorem getElem_swap_right {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[j]'(by simpa using hj) = xs[i] := by
simp +contextual [getElem_swap]
@[simp] theorem getElem_swap_left {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[i]'(by simpa using hi) = xs[j] := by
simp [getElem_swap]
@[simp] theorem getElem_swap_of_ne {xs : Array α} {i j : Nat} {hi hj}
{h : k < (xs.swap i j hi hj).size} (hi' : k i) (hj' : k j) :
(xs.swap i j hi hj)[k] = xs[k]'(by simp_all) := by
simp [getElem_swap, hi', hj']
@[deprecated getElem_swap (since := "2025-10-10")]
theorem getElem_swap' {xs : Array α} {i j : Nat} {hi hj} {k : Nat} (hk : k < xs.size) :
(xs.swap i j hi hj)[k]'(by simp_all) = if k = i then xs[j] else if k = j then xs[i] else xs[k] :=
getElem_swap _ _ _
@[simp] theorem swap_swap {xs : Array α} {i j : Nat} (hi hj) :
(xs.swap i j hi hj).swap i j ((xs.size_swap ..).symm hi) ((xs.size_swap ..).symm hj) = xs := by
@@ -4008,8 +4009,66 @@ theorem swap_comm {xs : Array α} {i j : Nat} (hi hj) : xs.swap i j hi hj = xs.s
· split <;> simp_all
· split <;> simp_all
/-! ### swapIfInBounds -/
@[grind =] theorem swapIfInBounds_def {xs : Array α} {i j : Nat} :
xs.swapIfInBounds i j = if h₁ : i < xs.size then
if h₂ : j < xs.size then swap xs i j else xs else xs := rfl
@[simp, grind =] theorem size_swapIfInBounds {xs : Array α} {i j : Nat} :
(xs.swapIfInBounds i j).size = xs.size := by unfold swapIfInBounds; split <;> (try split) <;> simp [size_swap]
(xs.swapIfInBounds i j).size = xs.size := by
unfold swapIfInBounds; split <;> (try split) <;> simp [size_swap]
@[grind =] theorem getElem_swapIfInBounds {xs : Array α} {i j k : Nat}
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] =
if h₁ : k = i j < xs.size then xs[j]'h₁.2 else if h₂ : k = j i < xs.size then xs[i]'h₂.2
else xs[k]'(by simp_all) := by
rw [size_swapIfInBounds] at hk
unfold swapIfInBounds
split <;> rename_i hi
· split <;> rename_i hj
· simp only [hi, hj, and_true]
exact getElem_swap _ _ _
· simp only [hi, hj, and_true, and_false, dite_false]
split <;> simp_all
· simp only [hi, and_false, dite_false]
split <;> simp_all
@[simp]
theorem getElem_swapIfInBounds_of_size_le_left {xs : Array α} {i j k : Nat} (hi : xs.size i)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
have h₁ : k i := Nat.ne_of_lt <| Nat.lt_of_lt_of_le hk <|
Nat.le_trans (Nat.le_of_eq (size_swapIfInBounds)) hi
have h₂ : ¬ (i < xs.size) := Nat.not_lt_of_le hi
simp [getElem_swapIfInBounds, h₁, h₂]
@[simp]
theorem getElem_swapIfInBounds_of_size_le_right {xs : Array α} {i j k : Nat} (hj : xs.size j)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
have h₁ : ¬ (j < xs.size) := Nat.not_lt_of_le hj
have h₂ : k j := Nat.ne_of_lt <| Nat.lt_of_lt_of_le hk <|
Nat.le_trans (Nat.le_of_eq (size_swapIfInBounds)) hj
simp [getElem_swapIfInBounds, h₁, h₂]
@[simp]
theorem getElem_swapIfInBounds_left {xs : Array α} {i j : Nat} (hj : j < xs.size)
(hi : i < (xs.swapIfInBounds i j).size) : (xs.swapIfInBounds i j)[i] = xs[j] := by
simp [getElem_swapIfInBounds, hj]
@[simp]
theorem getElem_swapIfInBounds_right {xs : Array α} {i j : Nat} (hi : i < xs.size)
(hj : j < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[j] = xs[i] := by
simp +contextual [getElem_swapIfInBounds, hi]
@[simp]
theorem getElem_swapIfInBounds_of_ne_of_ne {xs : Array α} {i j k : Nat} (hi : k i) (hj : k j)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
simp [getElem_swapIfInBounds, hi, hj]
/-! ### swapAt -/
@@ -4271,6 +4330,10 @@ theorem size_uset {xs : Array α} {v : α} {i : USize} (h : i.toNat < xs.size) :
theorem getElem!_eq_getD [Inhabited α] {xs : Array α} {i} : xs[i]! = xs.getD i default := by
rfl
theorem getElem_eq_getD {xs : Array α} {i} {h : i < xs.size} (fallback : α) :
xs[i]'h = xs.getD i fallback := by
rw [getD_eq_getD_getElem?, getElem_eq_getElem?_get, Option.get_eq_getD]
/-! # mem -/
@[deprecated mem_toList_iff (since := "2025-05-26")]

View File

@@ -1056,7 +1056,7 @@ theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
@[simp, grind =] theorem toFin_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
(setWidth' p x).toFin = x.toFin.castLE (Nat.pow_le_pow_right (by omega) (by omega)) := by
ext
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.coe_castLE, val_toFin,
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.val_castLE, val_toFin,
Nat.mod_eq_of_lt (by apply BitVec.toNat_lt_twoPow_of_le p)]
theorem toNat_setWidth_of_le {w w' : Nat} {b : BitVec w} (h : w w') : (b.setWidth w').toNat = b.toNat := by

View File

@@ -243,7 +243,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteAr
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance : ForIn m ByteArray UInt8 where
instance [Monad m] : ForIn m ByteArray UInt8 where
forIn := ByteArray.forIn
/--

View File

@@ -246,6 +246,11 @@ instance neg (n : Nat) : Neg (Fin n) :=
theorem neg_def (a : Fin n) : -a = (n - a) % n, Nat.mod_lt _ a.pos := rfl
-- Later we give another version called `Fin.val_neg` that splits on `a = 0`.
protected theorem val_neg' (a : Fin n) : ((-a : Fin n) : Nat) = (n - a) % n :=
rfl
@[deprecated Fin.val_neg' (since := "2025-11-21")]
protected theorem coe_neg (a : Fin n) : ((-a : Fin n) : Nat) = (n - a) % n :=
rfl

View File

@@ -16,17 +16,25 @@ open Std
namespace Fin
@[simp] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@[simp, grind =] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@[deprecated ofNat_zero (since := "2025-05-28")] abbrev ofNat'_zero := @ofNat_zero
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a.val % m.val) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
rfl
theorem val_mod (a m : Fin n) : (a % m).val = a.val % m.val := rfl
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a.val * b.val) % n) (Nat.mod_lt _ a.pos) := rfl
theorem val_mul (a b : Fin n) : (a * b).val = (a.val * b.val) % n := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b.val) + a.val) % n) (Nat.mod_lt _ a.pos) := rfl
@[grind =]
theorem val_sub (a b : Fin n) : (a - b).val = ((n - b.val) + a.val) % n := rfl
@[grind ]
theorem pos' : [Nonempty (Fin n)], 0 < n | i => i.pos
@[simp] theorem is_lt (a : Fin n) : (a : Nat) < n := a.2
@@ -38,7 +46,8 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (a, h : Fin n) = a := rfl
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
@[ext, grind ext]
protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -69,7 +78,7 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[deprecated val_ofNat (since := "2025-05-28")] abbrev val_ofNat' := @val_ofNat
@[simp] theorem ofNat_self {n : Nat} [NeZero n] : Fin.ofNat n n = 0 := by
@[simp, grind =] theorem ofNat_self {n : Nat} [NeZero n] : Fin.ofNat n n = 0 := by
ext
simp
congr
@@ -89,7 +98,7 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[simp] theorem div_val (a b : Fin n) : (a / b).val = a.val / b.val :=
rfl
@[simp] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
@[simp, grind =] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
rfl
@[simp] theorem val_eq_zero (a : Fin 1) : a.val = 0 :=
@@ -259,7 +268,9 @@ instance : LawfulOrderLT (Fin n) where
lt_iff := by
simp [ Fin.not_le, Decidable.imp_iff_not_or, Std.Total.total]
@[simp, grind =] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
@[simp] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
grind_pattern val_rev => i.rev
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@@ -284,6 +295,8 @@ theorem rev_eq {n a : Nat} (i : Fin (n + 1)) (h : n = a + i) :
@[simp] theorem val_last (n : Nat) : (last n).1 = n := rfl
grind_pattern val_last => last n
@[simp] theorem last_zero : (Fin.last 0 : Fin 1) = 0 := by
ext
simp
@@ -393,6 +406,8 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt zero_lt_one
@[simp] theorem val_succ (j : Fin n) : (j.succ : Nat) = j + 1 := rfl
grind_pattern val_succ => j.succ
@[simp] theorem succ_pos (a : Fin n) : (0 : Fin (n + 1)) < a.succ := by
simp [Fin.lt_def]
@@ -453,12 +468,18 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) 1 :=
Fin.ne_of_gt (one_lt_succ_succ a)
@[simp] theorem coe_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[simp, grind =] theorem val_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[deprecated val_castLT (since := "2025-11-21")]
theorem coe_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[simp] theorem castLT_mk (i n m : Nat) (hn : i < n) (hm : i < m) : castLT i, hn hm = i, hm :=
rfl
@[simp, grind =] theorem coe_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp, grind =] theorem val_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[deprecated val_castLE (since := "2025-11-21")]
theorem coe_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n m) :
castLE h i, hn = i, Nat.lt_of_lt_of_le hn h := rfl
@@ -470,13 +491,16 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem castLE_castLE {k m n} (km : k m) (mn : m n) (i : Fin k) :
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
Fin.ext (by simp only [coe_castLE])
Fin.ext (by simp only [val_castLE])
@[simp] theorem castLE_comp_castLE {k m n} (km : k m) (mn : m n) :
Fin.castLE mn Fin.castLE km = Fin.castLE (Nat.le_trans km mn) :=
funext (castLE_castLE km mn)
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp, grind =] theorem val_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[deprecated val_cast (since := "2025-11-21")]
theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem cast_castLE {k m n} (km : k m) (mn : m = n) (i : Fin k) :
Fin.cast mn (i.castLE km) = i.castLE (mn km) :=
@@ -489,7 +513,7 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem cast_zero [NeZero n] [NeZero m] (h : n = m) : Fin.cast h 0 = 0 := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : (last n).cast h = last n' :=
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
Fin.ext (by rw [val_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : Fin.cast h i, hn = i, h hn := rfl
@@ -504,7 +528,10 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m n} : castLE h' = Fin.cast h := rfl
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[simp, grind =] theorem val_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[deprecated val_castAdd (since := "2025-11-21")]
theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n Fin (n + 0)) = Fin.cast rfl := rfl
@@ -540,7 +567,10 @@ the reverse direction. -/
theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
(i.cast h).succ = i.succ.cast (by rw [h]) := rfl
@[simp] theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[simp, grind =] theorem val_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[deprecated val_castSucc (since := "2025-11-21")]
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_succ_of_lt h := rfl
@@ -548,7 +578,7 @@ theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
i.castSucc.cast h = (i.cast (Nat.succ.inj h)).castSucc := rfl
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]
lt_def.2 <| by simp only [val_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
simpa only [lt_def, le_def] using Nat.add_one_le_add_one_iff.symm
@@ -602,7 +632,7 @@ theorem coeSucc_eq_succ {a : Fin n} : a.castSucc + 1 = a.succ := by
@[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
rw [castSucc, lt_def, val_castAdd, val_succ]; exact Nat.lt_succ_self a.val
theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : ( j, castSucc j = i) i last n :=
fun j, hj => hj Fin.ne_of_lt j.castSucc_lt_last,
@@ -610,7 +640,10 @@ theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : (∃ j, castSucc j = i)
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = i.succ.castSucc := rfl
@[simp] theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[simp, grind =] theorem val_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[deprecated val_addNat (since := "2025-11-21")]
theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[simp] theorem addNat_zero (n : Nat) (i : Fin n) : addNat i 0 = i := by
ext
@@ -638,7 +671,10 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
(addNat i m').cast h = addNat i m :=
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[simp, grind =] theorem val_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[deprecated val_natAdd (since := "2025-11-21")]
theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[simp] theorem natAdd_mk (n i : Nat) (hi : i < m) :
natAdd n i, hi = n + i, Nat.add_lt_add_left hi n := rfl
@@ -695,7 +731,7 @@ theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSu
omega
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
rw [val_rev, val_castAdd, val_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
rw [ rev_rev (castAdd ..), rev_castAdd, rev_rev]
@@ -717,7 +753,12 @@ theorem castSucc_natAdd (n : Nat) (i : Fin k) :
/-! ### pred -/
@[simp] theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
@[simp] theorem val_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
grind_pattern val_pred => j.pred h
@[deprecated val_pred (since := "2025-11-21")]
theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
@[simp] theorem succ_pred : (i : Fin (n + 1)) (h : i 0), (i.pred h).succ = i
| 0, _, hi => by simp only [mk_zero, ne_eq, not_true] at hi
@@ -735,7 +776,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) {j : Fin
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
Fin.pred i + 1, Nat.add_lt_add_right h 1 (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
i, h := by
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
simp only [Fin.ext_iff, val_pred, Nat.add_sub_cancel]
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
Fin.pred i + 1, h₁ h₂ = i, Nat.lt_of_succ_lt_succ h₁ := pred_mk_succ i _
@@ -762,10 +803,13 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
rw [Fin.ext_iff, val_pred, val_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
exact Nat.add_lt_add_right h 1
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[simp, grind =] theorem val_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[deprecated val_subNat (since := "2025-11-21")]
theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[simp] theorem subNat_mk {i : Nat} (h₁ : i < n + m) (h₂ : m i) :
subNat m i, h₁ h₂ = i - m, Nat.sub_lt_right_of_lt_add h₂ h₁ := rfl
@@ -830,11 +874,11 @@ step. `Fin.succRec` is a version of this induction principle that takes the `Fin
(zero : n, motive (n + 1) 0) (succ : n i, motive n i motive (Nat.succ n) i.succ) :
motive n i := i.succRec zero succ
@[simp] theorem succRecOn_zero {motive : n, Fin n Sort _} {zero succ} (n) :
@[simp, grind =] theorem succRecOn_zero {motive : n, Fin n Sort _} {zero succ} (n) :
@Fin.succRecOn (n + 1) 0 motive zero succ = zero n := by
cases n <;> rfl
@[simp] theorem succRecOn_succ {motive : n, Fin n Sort _} {zero succ} {n} (i : Fin n) :
@[simp, grind =] theorem succRecOn_succ {motive : n, Fin n Sort _} {zero succ} {n} (i : Fin n) :
@Fin.succRecOn (n + 1) i.succ motive zero succ = succ n i (Fin.succRecOn i zero succ) := by
cases i; rfl
@@ -862,11 +906,11 @@ where
| 0, hi => by rwa [Fin.mk_zero]
| i+1, hi => succ i, Nat.lt_of_succ_lt_succ hi (go i (Nat.lt_of_succ_lt hi))
@[simp] theorem induction_zero {motive : Fin (n + 1) Sort _} (zero : motive 0)
@[simp, grind =] theorem induction_zero {motive : Fin (n + 1) Sort _} (zero : motive 0)
(hs : i : Fin n, motive (castSucc i) motive i.succ) :
(induction zero hs : i : Fin (n + 1), motive i) 0 = zero := rfl
@[simp] theorem induction_succ {motive : Fin (n + 1) Sort _} (zero : motive 0)
@[simp, grind =] theorem induction_succ {motive : Fin (n + 1) Sort _} (zero : motive 0)
(succ : i : Fin n, motive (castSucc i) motive i.succ) (i : Fin n) :
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
@@ -898,13 +942,13 @@ The corresponding induction principle is `Fin.induction`.
(zero : motive 0) (succ : i : Fin n, motive i.succ) :
i : Fin (n + 1), motive i := induction zero fun i _ => succ i
@[simp] theorem cases_zero {n} {motive : Fin (n + 1) Sort _} {zero succ} :
@[simp, grind =] theorem cases_zero {n} {motive : Fin (n + 1) Sort _} {zero succ} :
@Fin.cases n motive zero succ 0 = zero := rfl
@[simp] theorem cases_succ {n} {motive : Fin (n + 1) Sort _} {zero succ} (i : Fin n) :
@[simp, grind =] theorem cases_succ {n} {motive : Fin (n + 1) Sort _} {zero succ} (i : Fin n) :
@Fin.cases n motive zero succ i.succ = succ i := rfl
@[simp] theorem cases_succ' {n} {motive : Fin (n + 1) Sort _} {zero succ}
@[simp, grind =] theorem cases_succ' {n} {motive : Fin (n + 1) Sort _} {zero succ}
{i : Nat} (h : i + 1 < n + 1) :
@Fin.cases n motive zero succ i.succ, h = succ i, Nat.lt_of_succ_lt_succ h := rfl
@@ -954,7 +998,7 @@ For the induction:
| j + 1 => go j (by omega) (by omega) (cast j, by omega x)
go _ _ (by omega) last
@[simp] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ} :
@[simp, grind =] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ} :
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
rw [reverseInduction, reverseInduction.go]; simp
@@ -971,7 +1015,7 @@ private theorem reverseInduction_castSucc_aux {n : Nat} {motive : Fin (n + 1)
dsimp only
rw [ih _ _ (by omega), eq_comm, reverseInduction.go, dif_neg (by change i.1 + 1 _; omega)]
@[simp] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ}
@[simp, grind =] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ}
(i : Fin n) : reverseInduction (motive := motive) zero succ (castSucc i) =
succ i (reverseInduction zero succ i.succ) := by
rw [reverseInduction, reverseInduction_castSucc_aux _ _ _ i.isLt, reverseInduction]
@@ -990,11 +1034,11 @@ The corresponding induction principle is `Fin.reverseInduction`.
(cast : i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
reverseInduction last (fun i _ => cast i) i
@[simp] theorem lastCases_last {n : Nat} {motive : Fin (n + 1) Sort _} {last cast} :
@[simp, grind =] theorem lastCases_last {n : Nat} {motive : Fin (n + 1) Sort _} {last cast} :
(Fin.lastCases last cast (Fin.last n) : motive (Fin.last n)) = last :=
reverseInduction_last ..
@[simp] theorem lastCases_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {last cast}
@[simp, grind =] theorem lastCases_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {last cast}
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
reverseInduction_castSucc ..
@@ -1014,11 +1058,11 @@ as `Fin.natAdd m (j : Fin n)`.
if hi : (i : Nat) < m then (castAdd_castLT n i hi) (left (castLT i hi))
else (natAdd_subNat_cast (Nat.le_of_not_lt hi)) (right _)
@[simp] theorem addCases_left {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin m) :
@[simp, grind =] theorem addCases_left {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin m) :
addCases (motive := motive) left right (Fin.castAdd n i) = left i := by
rw [addCases, dif_pos (castAdd_lt _ _)]; rfl
@[simp]
@[simp, grind =]
theorem addCases_right {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin n) :
addCases (motive := motive) left right (natAdd m i) = right i := by
have : ¬(natAdd m i : Nat) < m := Nat.not_lt.2 (le_coe_natAdd ..)
@@ -1051,6 +1095,7 @@ theorem add_ofNat [NeZero n] (x : Fin n) (y : Nat) :
/-! ### sub -/
@[deprecated val_sub (since := "2025-11-21")]
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = ((n - b) + a) % n := by
cases a; cases b; rfl
@@ -1102,6 +1147,7 @@ theorem coe_sub_iff_lt {a b : Fin n} : (↑(a - b) : Nat) = n + a - b ↔ a < b
/-! ### neg -/
@[grind =]
theorem val_neg {n : Nat} [NeZero n] (x : Fin n) :
(-x).val = if x = 0 then 0 else n - x.val := by
change (n - x) % n = _
@@ -1117,7 +1163,7 @@ protected theorem sub_eq_add_neg {n : Nat} (x y : Fin n) : x - y = x + -y := by
apply elim0 x
· replace h : NeZero n := h
ext
rw [Fin.coe_sub, Fin.val_add, val_neg]
rw [Fin.val_sub, Fin.val_add, val_neg]
split
· simp_all
· simp [Nat.add_comm]
@@ -1138,9 +1184,6 @@ theorem mul_ofNat [NeZero n] (x : Fin n) (y : Nat) :
@[deprecated mul_ofNat (since := "2025-05-28")] abbrev mul_ofNat' := @mul_ofNat
theorem val_mul {n : Nat} : a b : Fin n, (a * b).val = a.val * b.val % n
| _, _, _, _ => rfl
@[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

@@ -129,7 +129,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : FloatA
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance : ForIn m FloatArray Float where
instance [Monad m] : ForIn m FloatArray Float where
forIn := FloatArray.forIn
/-- See comment at `forInUnsafe` -/

View File

@@ -1781,6 +1781,16 @@ theorem ediv_lt_ediv_iff_of_dvd_of_neg_of_neg {a b c d : Int} (hb : b < 0) (hd :
obtain x, rfl, y, rfl := hba, hdc
simp [*, Int.ne_of_lt, d.mul_assoc, b.mul_comm]
theorem ediv_lt_ediv_of_lt {a b c : Int} (h : a < b) (hcb : c b) (hc : 0 < c) :
a / c < b / c :=
Int.lt_ediv_of_mul_lt (Int.le_of_lt hc) hcb
(Int.lt_of_le_of_lt (Int.ediv_mul_le _ (Int.ne_of_gt hc)) h)
theorem ediv_lt_ediv_of_lt_of_neg {a b c : Int} (h : b < a) (hca : c a) (hc : c < 0) :
a / c < b / c :=
(Int.ediv_lt_iff_of_dvd_of_neg hc hca).2
(Int.lt_of_le_of_lt (Int.mul_ediv_self_le (Int.ne_of_lt hc)) h)
/-! ### `tdiv` and ordering -/
-- Theorems about `tdiv` and ordering, whose `ediv` analogues are in `Bootstrap.lean`.

View File

@@ -377,6 +377,15 @@ protected theorem le_iff_lt_add_one {a b : Int} : a ≤ b ↔ a < b + 1 := by
@[grind =] protected theorem max_def (n m : Int) : max n m = if n m then m else n := rfl
end Int
namespace Lean.Meta.Grind.Lia
scoped grind_pattern Int.min_def => min n m
scoped grind_pattern Int.max_def => max n m
end Lean.Meta.Grind.Lia
namespace Int
@[simp] protected theorem neg_min_neg (a b : Int) : min (-a) (-b) = -max a b := by
rw [Int.min_def, Int.max_def]
simp

View File

@@ -63,12 +63,12 @@ instance (α : Type w) (β : Type w) (n : Type x → Type x') [Monad n]
instForInOfForIn'
instance {m : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id m] :
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id m] [Monad m] :
ForM m (Iter (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoopPartial α Id m] :
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoopPartial α Id m] [Monad m] :
ForM m (Iter.Partial (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)

View File

@@ -247,10 +247,10 @@ This `ForIn'`-style loop construct traverses a finite iterator using an `Iterato
-/
@[always_inline, inline]
def IteratorLoop.finiteForIn' {m : Type w Type w'} {n : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [Monad n]
(lift : γ δ, (γ n δ) m γ n δ) :
ForIn' n (IterM (α := α) m β) β fun it out => it.IsPlausibleIndirectOutput out where
forIn' {γ} [Monad n] it init f :=
forIn' {γ} it init f :=
IteratorLoop.forIn (α := α) (m := m) lift γ (fun _ _ _ => True)
wellFounded_of_finite
it init (fun out h acc => (·, .intro) <$> f out h acc)
@@ -288,13 +288,13 @@ instance {m : Type w → Type w'} {n : Type w → Type w''}
instForInOfForIn'
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n]
{α : Type w} {β : Type w} [Iterator α m β] [Finite α m] [IteratorLoop α m n] [Monad n]
[MonadLiftT m n] :
ForM n (IterM (α := α) m β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n]
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoopPartial α m n] [Monad 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)

View File

@@ -19,110 +19,45 @@ open Std.Iterators
namespace Std.Iterators
/--
This typeclass provides an iterator for the given element `x : γ`. Usually, instances are provided
for all elements of a type `γ`.
-/
class ToIterator {γ : Type u} (x : γ) (m : Type w Type w') (β : outParam (Type w)) where
State : Type w
iterMInternal : IterM (α := State) m β
/-- This typeclass provides an iterator for elements of type `γ`. -/
class ToIterator (γ : Type u) (m : Type w Type w') (α β : outParam (Type w)) where
iterMInternal (x : γ) : IterM (α := α) m β
/-- Converts `x` into a monadic iterator. -/
@[always_inline, inline, expose]
def ToIterator.iterM (x : γ) [ToIterator x m β] : IterM (α := ToIterator.State x m) m β :=
def ToIterator.iterM (x : γ) [ToIterator γ m α β] : IterM (α := α) m β :=
ToIterator.iterMInternal (x := x)
/-- Converts `x` into a pure iterator. -/
@[always_inline, inline, expose]
def ToIterator.iter (x : γ) [ToIterator x Id β] : Iter (α := ToIterator.State x Id) β :=
def ToIterator.iter [ToIterator γ Id α β] (x : γ) : Iter (α := α) β :=
ToIterator.iterM x |>.toIter
/-- Creates a monadic `ToIterator` instance. -/
@[always_inline, inline, expose]
def ToIterator.ofM {x : γ} (State : Type w)
(iterM : IterM (α := State) m β) :
ToIterator x m β where
State := State
iterMInternal := iterM
def ToIterator.ofM (α : Type w)
(iterM : γ IterM (α := α) m β) :
ToIterator γ m α β where
iterMInternal x := iterM x
/-- Creates a pure `ToIterator` instance. -/
@[always_inline, inline, expose]
def ToIterator.of {x : γ} (State : Type w)
(iter : Iter (α := State) β) :
ToIterator x Id β where
State := State
iterMInternal := iter.toIterM
def ToIterator.of (α : Type w)
(iter : γ Iter (α := α) β) :
ToIterator γ Id α β where
iterMInternal x := iter x |>.toIterM
theorem ToIterator.iterM_eq {γ : Type u} {x : γ} {State : Type v} {β : Type v} {it} :
letI : ToIterator x Id β := .ofM State it
ToIterator.iterM x = it :=
/-- Replaces `ToIterator.iterM` with its definition. -/
theorem ToIterator.iterM_eq {γ : Type u} {α β : Type v}
{it : γ IterM (α := α) Id β} {x} :
letI : ToIterator γ Id α β := .ofM α it
ToIterator.iterM x = it x :=
rfl
theorem ToIterator.iter_eq {γ : Type u} {x : γ} {State : Type v} {β : Type v} {it} :
letI : ToIterator x Id β := .ofM State it
ToIterator.iter x = it.toIter :=
rfl
/-!
## Instance forwarding
If the type defined as `ToIterator.State` implements an iterator typeclass, then this typeclass
should also be available when the type is syntactically visible as `ToIteratorState`. The following
instances are responsible for this forwarding.
-/
instance {x : γ} {State : Type w} {iter}
[Iterator State m β] :
letI i : ToIterator x m β := .ofM State iter
Iterator (α := i.State) m β :=
inferInstanceAs <| Iterator State m β
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [Finite State m] :
letI i : ToIterator x m β := .ofM State iter
Finite (α := i.State) m :=
inferInstanceAs <| Finite (α := State) m
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorCollect State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorCollect (α := i.State) m n :=
inferInstanceAs <| IteratorCollect (α := State) m n
instance {x : γ} {State : Type w} {iter} [Monad m] [Monad n]
[Iterator (α := State) m β] [IteratorCollect State m n] [LawfulIteratorCollect State m n] :
letI i : ToIterator x m β := .ofM State iter
LawfulIteratorCollect (α := i.State) m n :=
inferInstanceAs <| LawfulIteratorCollect (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorCollectPartial State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorCollectPartial (α := i.State) m n :=
inferInstanceAs <| IteratorCollectPartial (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorLoop State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorLoop (α := i.State) m n :=
inferInstanceAs <| IteratorLoop (α := State) m n
instance {x : γ} {State : Type w} {iter} [Monad m] [Monad n]
[Iterator (α := State) m β] [IteratorLoop State m n] [LawfulIteratorLoop State m n]:
letI i : ToIterator x m β := .ofM State iter
LawfulIteratorLoop (α := i.State) m n :=
inferInstanceAs <| LawfulIteratorLoop (α := State) m n
instance {x : γ} {State : Type w} {iter}
[Iterator (α := State) m β] [IteratorLoopPartial State m n] :
letI i : ToIterator x m β := .ofM State iter
IteratorLoopPartial (α := i.State) m n :=
inferInstanceAs <| IteratorLoopPartial (α := State) m n
@[simp]
theorem ToIterator.state_eq {x : γ} {State : Type w} {iter} :
haveI : ToIterator x Id β := .of State iter
ToIterator.State x Id = State :=
/-- Replaces `ToIterator.iter` with its definition. -/
theorem ToIterator.iter_eq {γ : Type u} {x : γ} {α β : Type v} {it} :
letI : ToIterator γ Id α β := .ofM α it
ToIterator.iter x = (it x).toIter :=
rfl
end Std.Iterators

View File

@@ -471,7 +471,7 @@ theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] {p : α → m Bool} {as :
loop as' b this
loop as init [], rfl
instance : ForIn' m (List α) α inferInstance where
instance [Monad m] : ForIn' m (List α) α inferInstance where
forIn' := List.forIn'
-- No separate `ForIn` instance is required because it can be derived from `ForIn'`.
@@ -485,7 +485,7 @@ instance : ForIn' m (List α) α inferInstance where
@[simp, grind =] theorem forIn_nil [Monad m] {f : α β m (ForInStep β)} {b : β} : forIn [] b f = pure b :=
rfl
instance : ForM m (List α) α where
instance [Monad m] : ForM m (List α) α where
forM := List.forM
-- We simplify `List.forM` to `forM`.

View File

@@ -13,6 +13,9 @@ import all Init.Data.List.BasicAux
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.BinderPredicates
import Init.Grind.Annotated
grind_annotated "2025-01-24"
public section
@@ -251,6 +254,10 @@ theorem getElem_eq_getElem?_get {l : List α} {i : Nat} (h : i < l.length) :
l[i] = l[i]?.get (by simp [h]) := by
simp
theorem getElem_eq_getD {l : List α} {i : Nat} {h : i < l.length} (fallback : α) :
l[i] = l.getD i fallback := by
rw [getElem_eq_getElem?_get, List.getD, Option.get_eq_getD]
theorem getD_getElem? {l : List α} {i : Nat} {d : α} :
l[i]?.getD d = if p : i < l.length then l[i]'p else d := by
if h : i < l.length then

View File

@@ -168,10 +168,10 @@ Examples:
| none , _ => pure
| some a, f => f a
instance : ForM m (Option α) α :=
instance [Monad m] : ForM m (Option α) α :=
Option.forM
instance : ForIn' m (Option α) α inferInstance where
instance [Monad m] : ForIn' m (Option α) α inferInstance where
forIn' x init f := do
match x with
| none => return init

View File

@@ -43,7 +43,7 @@ universe u v
have := range.step_pos
loop init range.start (by simp)
instance : ForIn' m Range Nat inferInstance where
instance [Monad m] : ForIn' m Range Nat inferInstance where
forIn' := Range.forIn'
-- No separate `ForIn` instance is required because it can be derived from `ForIn'`.
@@ -59,7 +59,7 @@ instance : ForIn' m Range Nat inferInstance where
have := range.step_pos
loop range.start
instance : ForM m Range Nat where
instance [Monad m] : ForM m Range Nat where
forM := Range.forM
syntax:max "[" withoutPosition(":" term) "]" : term

View File

@@ -295,6 +295,15 @@ theorem add_def (a b : Rat) :
theorem add_def' (a b : Rat) : a + b = mkRat (a.num * b.den + b.num * a.den) (a.den * b.den) := by
rw [add_def, normalize_eq_mkRat]
theorem num_add (a b : Rat) : (a + b).num =
(a.num * b.den + b.num * a.den) /
((a.num * b.den + b.num * a.den).natAbs.gcd (a.den * b.den)) := by
rw [add_def, num_normalize]
theorem den_add (a b : Rat) : (a + b).den =
a.den * b.den / (a.num * b.den + b.num * a.den).natAbs.gcd (a.den * b.den) := by
rw [add_def, den_normalize]
@[local simp]
protected theorem add_zero (a : Rat) : a + 0 = a := by simp [add_def', mkRat_self]
@[local simp]
@@ -406,6 +415,13 @@ theorem mul_def (a b : Rat) :
theorem mul_def' (a b : Rat) : a * b = mkRat (a.num * b.num) (a.den * b.den) := by
rw [mul_def, normalize_eq_mkRat]
theorem num_mul (a b : Rat) :
(a * b).num = a.num * b.num / ((a.num * b.num).natAbs.gcd (a.den * b.den)) := by
rw [mul_def, num_normalize]
theorem den_mul (a b : Rat) :
(a * b).den = a.den * b.den / (a.num * b.num).natAbs.gcd (a.den * b.den) := by
rw [mul_def, den_normalize]
protected theorem mul_comm (a b : Rat) : a * b = b * a := by
simp [mul_def, normalize_eq_mkRat, Int.mul_comm, Nat.mul_comm]

View File

@@ -26,38 +26,55 @@ open Std Slice PRange Iterators
variable {shape : RangeShape} {α : Type u}
instance {s : Slice (Internal.SubarrayData α)} : ToIterator s Id α :=
.of _
(Rco.Internal.iter (s.internalRepresentation.start...<s.internalRepresentation.stop)
|>.attachWith (· < s.internalRepresentation.array.size) ?h
|>.uLift
|>.map fun | .up i => s.internalRepresentation.array[i.1])
where finally
case h =>
simp only [Rco.Internal.isPlausibleIndirectOutput_iter_iff, Membership.mem, and_imp]
intro out _ h
have := s.internalRepresentation.stop_le_array_size
omega
@[unbox]
structure SubarrayIterator (α : Type u) where
xs : Subarray α
@[inline, expose]
def SubarrayIterator.step :
IterM (α := SubarrayIterator α) Id α IterStep (IterM (α := SubarrayIterator α) m α) α
| xs =>
if h : xs.start < xs.stop then
have := xs.start_le_stop; have := xs.stop_le_array_size
.yield xs.array, xs.start + 1, xs.stop, by omega, xs.stop_le_array_size xs.array[xs.start]
else
.done
instance : Iterator (SubarrayIterator α) Id α where
IsPlausibleStep it step := step = SubarrayIterator.step it
step it := pure <| .deflate SubarrayIterator.step it, rfl
private def SubarrayIterator.instFinitelessRelation : FinitenessRelation (SubarrayIterator α) Id where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.xs.stop - it.internalState.xs.start)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
simp [IterM.IsPlausibleSuccessorOf, IterM.IsPlausibleStep, Iterator.IsPlausibleStep, step] at h
split at h
· cases h
simp only [InvImage, Subarray.stop, Subarray.start, WellFoundedRelation.rel, InvImage,
Nat.lt_wfRel, sizeOf_nat]
exact Nat.sub_succ_lt_self _ _ _
· cases h
instance SubarrayIterator.instFinite : Finite (SubarrayIterator α) Id :=
.of_finitenessRelation instFinitelessRelation
instance [Monad m] : IteratorCollect (SubarrayIterator α) Id m := .defaultImplementation
instance [Monad m] : IteratorCollectPartial (SubarrayIterator α) Id m := .defaultImplementation
instance [Monad m] : IteratorLoop (SubarrayIterator α) Id m := .defaultImplementation
instance [Monad m] : IteratorLoopPartial (SubarrayIterator α) Id m := .defaultImplementation
@[inline, expose]
def Subarray.instToIterator :=
ToIterator.of (γ := Slice (Internal.SubarrayData α)) (β := α) (SubarrayIterator α) (·)
attribute [instance] Subarray.instToIterator
universe v w
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : Iterator (ToIterator.State s Id) Id α := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : Finite (ToIterator.State s Id) Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : IteratorCollect (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : LawfulIteratorCollect (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} : IteratorCollectPartial (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} {m : Type v Type w} [Monad m] :
IteratorLoop (ToIterator.State s Id) Id m := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} {m : Type v Type w} [Monad m] :
LawfulIteratorLoop (ToIterator.State s Id) Id m := inferInstance
@[no_expose] instance {s : Slice (Internal.SubarrayData α)} {m : Type v Type w} [Monad m] :
IteratorLoopPartial (ToIterator.State s Id) Id m := inferInstance
instance : SliceSize (Internal.SubarrayData α) where
size s := s.internalRepresentation.stop - s.internalRepresentation.start
instance {α : Type u} {m : Type v Type w} [Monad m] :
ForIn m (Subarray α) α :=
instance {α : Type u} {m : Type v Type w} [Monad m] : ForIn m (Subarray α) α :=
inferInstance
/-!

View File

@@ -22,36 +22,91 @@ import Init.Data.Range.Polymorphic.NatLemmas
open Std Std.Iterators Std.PRange Std.Slice
namespace SubarrayIterator
theorem step_eq {it : Iter (α := SubarrayIterator α) α} :
it.step = if h : it.1.xs.start < it.1.xs.stop then
haveI := it.1.xs.start_le_stop
haveI := it.1.xs.stop_le_array_size
.yield it.1.xs.array, it.1.xs.start + 1, it.1.xs.stop, by omega, by assumption
(it.1.xs.array[it.1.xs.start]'(by omega)),
(by
simp_all [Iter.IsPlausibleStep, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
SubarrayIterator.step, Iter.toIterM])
else
.done, (by
simpa [Iter.IsPlausibleStep, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
SubarrayIterator.step] using h) := by
simp only [Iter.step, IterM.Step.toPure, Iter.toIter_toIterM, IterStep.mapIterator, IterM.step,
Iterator.step, SubarrayIterator.step, Id.run_pure, Shrink.inflate_deflate]
by_cases h : it.internalState.xs.start < it.internalState.xs.stop
· simp only [h, reduceDIte]
split
· rfl
· rename_i h'
exact h'.elim h
· simp only [h, reduceDIte]
split
· rename_i h'
exact h.elim h'
· rfl
theorem val_step_eq {it : Iter (α := SubarrayIterator α) α} :
it.step.val = if h : it.1.xs.start < it.1.xs.stop then
haveI := it.1.xs.start_le_stop
haveI := it.1.xs.stop_le_array_size
.yield it.1.xs.array, it.1.xs.start + 1, it.1.xs.stop, by omega, by assumption
it.1.xs.array[it.1.xs.start]
else
.done := by
simp only [step_eq]
split <;> simp
theorem toList_eq {α : Type u} {it : Iter (α := SubarrayIterator α) α} :
it.toList =
(it.internalState.xs.array.toList.take it.internalState.xs.stop).drop it.internalState.xs.start := by
induction it using Iter.inductSteps with | step it ihy ihs
rw [Iter.toList_eq_match_step, SubarrayIterator.val_step_eq]
by_cases h : it.internalState.xs.start < it.internalState.xs.stop
· simp [h]
have := it.1.xs.start_le_stop
have := it.1.xs.stop_le_array_size
rw [ihy (out := it.internalState.xs.array[it.internalState.xs.start])]
· simp only [Subarray.start]
rw (occs := [2]) [List.drop_eq_getElem_cons]; rotate_left
· rw [List.length_take]
simp [it.internalState.xs.stop_le_array_size]
exact h
· simp [Subarray.array, Subarray.stop]
· simp only [Iter.IsPlausibleStep, IterM.IsPlausibleStep, Iterator.IsPlausibleStep,
IterStep.mapIterator_yield, SubarrayIterator.step]
rw [dif_pos]; rotate_left; exact h
rfl
· rw [dif_neg]; rotate_left; exact h
simp_all [it.internalState.xs.stop_le_array_size]
theorem count_eq {α : Type u} {it : Iter (α := SubarrayIterator α) α} :
it.count = it.internalState.xs.stop - it.internalState.xs.start := by
simp [ Iter.length_toList_eq_count, toList_eq, it.internalState.xs.stop_le_array_size]
end SubarrayIterator
namespace Subarray
theorem internalIter_eq {α : Type u} {s : Subarray α} :
Internal.iter s = (Rco.Internal.iter (s.start...<s.stop)
|>.attachWith (· < s.array.size)
(fun out h => h
|> Rco.Internal.isPlausibleIndirectOutput_iter_iff.mp
|> Rco.lt_upper_of_mem
|> (Nat.lt_of_lt_of_le · s.stop_le_array_size))
|>.uLift
|>.map fun | .up i => s.array[i.1]) := by
simp [Internal.iter, ToIterator.iter_eq, Subarray.start, Subarray.stop, Subarray.array]
Internal.iter s = s :=
rfl
theorem toList_internalIter {α : Type u} {s : Subarray α} :
(Internal.iter s).toList =
((s.start...s.stop).toList
|>.attach
|>.map fun i => s.array[i.1]'(i.property
|> Rco.mem_toList_iff_mem.mp
|> Rco.lt_upper_of_mem
|> (Nat.lt_of_lt_of_le · s.stop_le_array_size))) := by
rw [internalIter_eq, Iter.toList_map, Iter.toList_uLift, Iter.toList_attachWith]
simp [Rco.toList]
(s.array.toList.take s.stop).drop s.start := by
simp [SubarrayIterator.toList_eq, Internal.iter_eq_toIteratorIter, ToIterator.iter_eq]
public instance : LawfulSliceSize (Internal.SubarrayData α) where
lawful s := by
simp [SliceSize.size, ToIterator.iter_eq, Iter.toIter_toIterM,
Iter.size_toArray_eq_count, Rco.Internal.toArray_eq_toArray_iter,
Rco.size_toArray, Rco.size, Rxo.HasSize.size, Rxc.HasSize.size]
omega
Iter.length_toList_eq_count, SubarrayIterator.toList_eq,
s.internalRepresentation.stop_le_array_size, start, stop, array]
public theorem toArray_eq_sliceToArray {α : Type u} {s : Subarray α} :
s.toArray = Slice.toArray s := by
@@ -88,23 +143,22 @@ public theorem Array.toSubarray_eq_toSubarray_of_min_eq_min {xs : Array α}
· simp only [Nat.min_eq_left, *] at h
split
· simp only [Nat.min_eq_left, *] at h
simp [h]
simp only [h, right_eq_dite_iff, Slice.mk.injEq, Internal.SubarrayData.mk.injEq, and_true,
true_and]
omega
· simp only [ge_iff_le, not_false_eq_true, Nat.min_eq_right (Nat.le_of_not_ge _), *] at h
simp [h]
omega
· split
· split
· simp only [ge_iff_le, not_false_eq_true, Nat.min_eq_right (Nat.le_of_not_ge _),
Nat.min_eq_left, *] at h
simp_all
omega
· simp only [not_false_eq_true, Nat.min_eq_right (Nat.le_of_not_ge _),
Nat.min_eq_left, Nat.not_le, *] at *
simp [*]; omega
· simp
· simp [Nat.min_eq_right (Nat.le_of_not_ge _), *] at h
split
· simp only [Nat.min_eq_left, *] at h
simp_all
omega
simp [*]; omega
· simp
public theorem Array.toSubarray_eq_min {xs : Array α} {lo hi : Nat} :
@@ -136,15 +190,19 @@ theorem Subarray.toList_eq {xs : Subarray α} :
simp only [Subarray.start, Subarray.stop, Subarray.array]
change aslice.toList = _
have : aslice.toList = lslice.toList := by
simp [ListSlice.toList_eq, lslice, aslice]
simp only [Std.Slice.toList, toList_internalIter]
rw [ListSlice.toList_eq]
simp only [aslice, lslice, Std.Slice.toList, toList_internalIter]
apply List.ext_getElem
· have : stop - start array.size - start := by omega
simp [Subarray.start, Subarray.stop, Std.PRange.Nat.size_rco, *]
simp [Subarray.start, Subarray.stop, *, Subarray.array]
· intros
simp [Subarray.array, Subarray.start, Subarray.stop, Std.Rco.getElem_toList_eq, succMany?]
simp [Subarray.array, Subarray.start, Subarray.stop]
simp [this, ListSlice.toList_eq, lslice]
public theorem Subarray.size_eq {xs : Subarray α} :
xs.size = xs.stop - xs.start := by
simp [Subarray.size]
@[simp]
public theorem Subarray.toArray_toList {xs : Subarray α} :
xs.toList.toArray = xs.toArray := by
@@ -158,15 +216,14 @@ public theorem Subarray.toList_toArray {xs : Subarray α} :
@[simp]
public theorem Subarray.length_toList {xs : Subarray α} :
xs.toList.length = xs.size := by
simp [Subarray.toList_eq, Subarray.size]
have : xs.start xs.stop := xs.internalRepresentation.start_le_stop
have : xs.stop xs.array.size := xs.internalRepresentation.stop_le_array_size
omega
simp [Subarray.toList_eq, Subarray.size]; omega
@[simp]
public theorem Subarray.size_toArray {xs : Subarray α} :
xs.toArray.size = xs.size := by
rw [ Subarray.toArray_toList, List.size_toArray, length_toList]
simp [ Subarray.toArray_toList, Subarray.size, Slice.size, SliceSize.size, start, stop]
namespace Array
@@ -241,7 +298,7 @@ public theorem stop_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
@@ -319,12 +376,12 @@ public theorem mkSlice_roo_eq_mkSlice_roo_min {xs : Array α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].toArray = xs.extract (lo + 1) hi := by
rw [mkSlice_roo_eq_mkSlice_rco, toArray_mkSlice_rco]
simp
@[simp]
public theorem size_mkSlice_roo {xs : Array α} {lo hi : Nat} :
@@ -358,12 +415,12 @@ public theorem mkSlice_roc_eq_mkSlice_roo_min {xs : Array α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
rw [mkSlice_roc_eq_mkSlice_roo, toList_mkSlice_roo]
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].toArray = xs.extract (lo + 1) (hi + 1) := by
rw [mkSlice_roc_eq_mkSlice_roo, toArray_mkSlice_roo]
simp
@[simp]
public theorem size_mkSlice_roc {xs : Array α} {lo hi : Nat} :
@@ -407,7 +464,7 @@ public theorem toList_mkSlice_roi {xs : Array α} {lo : Nat} :
@[simp]
public theorem toArray_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo<...*].toArray = xs.drop (lo + 1) := by
rw [mkSlice_roi_eq_mkSlice_rci, toArray_mkSlice_rci]
simp
@[simp]
public theorem size_mkSlice_roi {xs : Array α} {lo : Nat} :
@@ -441,12 +498,12 @@ public theorem mkSlice_rio_eq_mkSlice_rio_min {xs : Array α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].toArray = xs.extract 0 hi := by
rw [mkSlice_rio_eq_mkSlice_rco, toArray_mkSlice_rco]
simp
@[simp]
public theorem size_mkSlice_rio {xs : Array α} {hi : Nat} :
@@ -480,12 +537,12 @@ public theorem mkSlice_ric_eq_mkSlice_rio_min {xs : Array α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
rw [mkSlice_ric_eq_mkSlice_rio, toList_mkSlice_rio]
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].toArray = xs.extract 0 (hi + 1) := by
rw [mkSlice_ric_eq_mkSlice_rio, toArray_mkSlice_rio]
simp
@[simp]
public theorem size_mkSlice_ric {xs : Array α} {hi : Nat} :
@@ -545,9 +602,9 @@ namespace Subarray
@[simp]
public theorem toList_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
xs[lo...hi].toList = (xs.toList.take hi).drop lo := by
simp only [instSliceableSubarrayNat_1, Std.Rco.HasRcoIntersection.intersection,
Std.Rco.Sliceable.mkSlice, toList_eq, Array.start_toSubarray, Array.stop_toSubarray,
Array.toList_extract, List.take_drop, List.take_take]
simp only [Std.Rco.Sliceable.mkSlice, Std.Rco.HasRcoIntersection.intersection, toList_eq,
Array.start_toSubarray, Array.stop_toSubarray, Array.toList_extract, List.take_drop,
List.take_take]
rw [Nat.add_sub_cancel' (by omega)]
simp [Subarray.size, Array.length_toList, List.take_eq_take_min, Nat.add_comm xs.start]
@@ -565,7 +622,7 @@ public theorem mkSlice_rcc_eq_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
@@ -603,7 +660,7 @@ public theorem mkSlice_roo_eq_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : Subarray α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : Subarray α} {lo hi : Nat} :
@@ -619,7 +676,7 @@ public theorem mkSlice_roc_eq_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : Subarray α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
rw [mkSlice_roc_eq_mkSlice_rcc, toList_mkSlice_rcc]
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : Subarray α} {lo hi : Nat} :
@@ -657,7 +714,7 @@ public theorem mkSlice_rio_eq_mkSlice_rco {xs : Subarray α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : Subarray α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : Subarray α} {hi : Nat} :
@@ -673,7 +730,7 @@ public theorem mkSlice_ric_eq_mkSlice_rcc {xs : Subarray α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : Subarray α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
rw [mkSlice_ric_eq_mkSlice_rcc, toList_mkSlice_rcc, List.drop_zero]
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : Subarray α} {hi : Nat} :

View File

@@ -18,20 +18,20 @@ namespace Std.Slice
open Std.Iterators
variable {γ : Type u} {β : Type v}
variable {γ : Type u} {α β : Type v}
theorem Internal.iter_eq_toIteratorIter {γ : Type u} {s : Slice γ}
[ToIterator s Id β] :
theorem Internal.iter_eq_toIteratorIter {γ : Type u}
[ToIterator (Slice γ) Id α β] {s : Slice γ} :
Internal.iter s = ToIterator.iter s :=
(rfl)
theorem forIn_internalIter {γ : Type u} {β : Type v}
{m : Type w Type x} [Monad m] {δ : Type w}
[ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, LawfulIteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] {s : Slice γ}
[ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[LawfulIteratorLoop α Id m]
[Finite α Id] {s : Slice γ}
{init : δ} {f : β δ m (ForInStep δ)} :
ForIn.forIn (Internal.iter s) init f = ForIn.forIn s init f :=
(rfl)
@@ -39,13 +39,13 @@ theorem forIn_internalIter {γ : Type u} {β : Type v}
@[simp]
public theorem forIn_toList {γ : Type u} {β : Type v}
{m : Type w Type x} [Monad m] [LawfulMonad m] {δ : Type w}
[ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, LawfulIteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, IteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, LawfulIteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] {s : Slice γ}
[ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id]
[Finite α Id] {s : Slice γ}
{init : δ} {f : β δ m (ForInStep δ)} :
ForIn.forIn s.toList init f = ForIn.forIn s init f := by
rw [ forIn_internalIter, Iter.forIn_toList, Slice.toList]
@@ -53,70 +53,68 @@ public theorem forIn_toList {γ : Type u} {β : Type v}
@[simp]
public theorem forIn_toArray {γ : Type u} {β : Type v}
{m : Type w Type x} [Monad m] [LawfulMonad m] {δ : Type w}
[ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, LawfulIteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, IteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, LawfulIteratorCollect (ToIterator.State s Id) Id Id]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] {s : Slice γ}
[ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id]
[Finite α Id] {s : Slice γ}
{init : δ} {f : β δ m (ForInStep δ)} :
ForIn.forIn s.toArray init f = ForIn.forIn s init f := by
rw [ forIn_internalIter, Iter.forIn_toArray, Slice.toArray]
theorem Internal.size_eq_count_iter [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[Finite (ToIterator.State s Id) Id]
[IteratorLoop (ToIterator.State s Id) Id Id] [LawfulIteratorLoop (ToIterator.State s Id) Id Id]
[SliceSize γ] [LawfulSliceSize γ] :
theorem Internal.size_eq_count_iter [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{s : Slice γ} [SliceSize γ] [LawfulSliceSize γ] :
s.size = (Internal.iter s).count := by
letI : IteratorCollect (ToIterator.State s Id) Id Id := .defaultImplementation
letI : IteratorCollect α Id Id := .defaultImplementation
simp only [Slice.size, iter, LawfulSliceSize.lawful, Iter.length_toList_eq_count]
theorem Internal.toArray_eq_toArray_iter {s : Slice γ} [ToIterator s Id β]
[Iterator (ToIterator.State s Id) Id β] [IteratorCollect (ToIterator.State s Id) Id Id]
[Finite (ToIterator.State s Id) Id] :
theorem Internal.toArray_eq_toArray_iter {s : Slice γ} [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [IteratorCollect α Id Id]
[Finite α Id] :
s.toArray = (Internal.iter s).toArray :=
(rfl)
theorem Internal.toList_eq_toList_iter {s : Slice γ} [ToIterator s Id β]
[Iterator (ToIterator.State s Id) Id β] [IteratorCollect (ToIterator.State s Id) Id Id]
[Finite (ToIterator.State s Id) Id] :
theorem Internal.toList_eq_toList_iter {s : Slice γ} [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [IteratorCollect α Id Id]
[Finite α Id] :
s.toList = (Internal.iter s).toList :=
(rfl)
theorem Internal.toListRev_eq_toListRev_iter {s : Slice γ} [ToIterator s Id β]
[Iterator (ToIterator.State s Id) Id β] [Finite (ToIterator.State s Id) Id] :
theorem Internal.toListRev_eq_toListRev_iter {s : Slice γ} [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [Finite α Id] :
s.toListRev = (Internal.iter s).toListRev :=
(rfl)
@[simp]
theorem size_toArray_eq_size [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[SliceSize γ] [LawfulSliceSize γ]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id]
[LawfulIteratorCollect (ToIterator.State s Id) Id Id] :
theorem size_toArray_eq_size [ToIterator (Slice γ) Id α β]
[Iterator α Id β] [SliceSize γ] [LawfulSliceSize γ]
[IteratorCollect α Id Id] [Finite α Id]
[LawfulIteratorCollect α Id Id] {s : Slice γ} :
s.toArray.size = s.size := by
letI : IteratorLoop (ToIterator.State s Id) Id Id := .defaultImplementation
letI : IteratorLoop α Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toArray_eq_toArray_iter, Iter.size_toArray_eq_count]
@[simp]
theorem length_toList_eq_size [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[SliceSize γ] [LawfulSliceSize γ] [IteratorCollect (ToIterator.State s Id) Id Id]
[Finite (ToIterator.State s Id) Id] [LawfulIteratorCollect (ToIterator.State s Id) Id Id] :
theorem length_toList_eq_size [ToIterator (Slice γ) Id α β]
[Iterator α Id β] {s : Slice γ}
[SliceSize γ] [LawfulSliceSize γ] [IteratorCollect α Id Id]
[Finite α Id] [LawfulIteratorCollect α Id Id] :
s.toList.length = s.size := by
letI : IteratorLoop (ToIterator.State s Id) Id Id := .defaultImplementation
letI : IteratorLoop α Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toList_eq_toList_iter, Iter.length_toList_eq_count]
@[simp]
theorem length_toListRev_eq_size [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β] {s : Slice γ}
[IteratorLoop (ToIterator.State s Id) Id Id.{v}] [SliceSize γ] [LawfulSliceSize γ]
[Finite (ToIterator.State s Id) Id]
[LawfulIteratorLoop (ToIterator.State s Id) Id Id] :
theorem length_toListRev_eq_size [ToIterator (Slice γ) Id α β]
[Iterator α Id β] {s : Slice γ}
[IteratorLoop α Id Id.{v}] [SliceSize γ] [LawfulSliceSize γ]
[Finite α Id]
[LawfulIteratorLoop α Id Id] :
s.toListRev.length = s.size := by
letI : IteratorCollect (ToIterator.State s Id) Id Id := .defaultImplementation
letI : IteratorCollect α Id Id := .defaultImplementation
rw [Internal.size_eq_count_iter, Internal.toListRev_eq_toListRev_iter,
Iter.length_toListRev_eq_count]

View File

@@ -22,40 +22,27 @@ This module implements an iterator for list slices.
open Std Slice PRange Iterators
variable {shape : RangeShape} {α : Type u}
variable {α : Type u}
instance {s : ListSlice α} : ToIterator s Id α :=
.of _ (match s.internalRepresentation.stop with
@[inline, expose]
def ListSlice.instToIterator :=
ToIterator.of (γ := Slice (Internal.ListSliceData α)) _ (fun s => match s.internalRepresentation.stop with
| some n => s.internalRepresentation.list.iter.take n
| none => s.internalRepresentation.list.iter.toTake)
attribute [instance] ListSlice.instToIterator
universe v w
@[no_expose] instance {s : ListSlice α} : Iterator (ToIterator.State s Id) Id α := inferInstance
@[no_expose] instance {s : ListSlice α} : Finite (ToIterator.State s Id) Id := inferInstance
@[no_expose] instance {s : ListSlice α} : IteratorCollect (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : ListSlice α} : IteratorCollectPartial (ToIterator.State s Id) Id Id := inferInstance
@[no_expose] instance {s : ListSlice α} {m : Type v Type w} [Monad m] :
IteratorLoop (ToIterator.State s Id) Id m := inferInstance
@[no_expose] instance {s : ListSlice α} {m : Type v Type w} [Monad m] :
IteratorLoopPartial (ToIterator.State s Id) Id m := inferInstance
instance : SliceSize (Internal.ListSliceData α) where
size s := (Internal.iter s).count
@[no_expose]
instance {α : Type u} {m : Type v Type w} :
instance {α : Type u} {m : Type v Type w} [Monad m] :
ForIn m (ListSlice α) α where
forIn xs init f := forIn (Internal.iter xs) init f
namespace List
/-- Allocates a new list that contains the contents of the slice. -/
def ofSlice (s : ListSlice α) : List α :=
s.toList
docs_to_verso ofSlice
instance : Append (ListSlice α) where
append x y :=
let a := x.toList ++ y.toList
@@ -73,7 +60,3 @@ instance [ToString α] : ToString (ListSlice α) where
toString s := toString s.toArray
end List
@[inherit_doc List.ofSlice]
def ListSlice.toList (s : ListSlice α) : List α :=
List.ofSlice s

View File

@@ -17,7 +17,9 @@ public import Init.Data.Iterators.Lemmas
open Std.Iterators Std.PRange
namespace Std.Slice.List
namespace ListSlice
open Std.Slice
theorem internalIter_eq {α : Type u} {s : ListSlice α} :
Internal.iter s = match s.internalRepresentation.stop with
@@ -40,36 +42,34 @@ public instance : LawfulSliceSize (Internal.ListSliceData α) where
lawful s := by
simp [ internalIter_eq_toIteratorIter, SliceSize.size]
end Std.Slice.List
public theorem ListSlice.toList_eq {xs : ListSlice α} :
public theorem toList_eq {xs : ListSlice α} :
xs.toList = match xs.internalRepresentation.stop with
| some stop => xs.internalRepresentation.list.take stop
| none => xs.internalRepresentation.list := by
simp only [toList, List.ofSlice, Std.Slice.toList, ToIterator.state_eq]
rw [Std.Slice.List.toList_internalIter]
simp only [Std.Slice.toList, toList_internalIter]
rfl
public theorem ListSlice.toArray_toList {xs : ListSlice α} :
public theorem toArray_toList {xs : ListSlice α} :
xs.toList.toArray = xs.toArray := by
simp [ListSlice.toList, Std.Slice.toArray, List.ofSlice, Std.Slice.toList]
simp [Std.Slice.toArray, Std.Slice.toList]
public theorem ListSlice.toList_toArray {xs : ListSlice α} :
public theorem toList_toArray {xs : ListSlice α} :
xs.toArray.toList = xs.toList := by
simp [ListSlice.toList, Std.Slice.toArray, List.ofSlice, Std.Slice.toList]
simp [Std.Slice.toArray, Std.Slice.toList]
@[simp]
public theorem ListSlice.length_toList {xs : ListSlice α} :
public theorem length_toList {xs : ListSlice α} :
xs.toList.length = xs.size := by
simp [ListSlice.toList_eq, Std.Slice.size, Std.Slice.SliceSize.size, Iter.length_toList_eq_count]
rw [Std.Slice.List.toList_internalIter]
rfl
simp [ListSlice.toList_eq, Std.Slice.size, Std.Slice.SliceSize.size, Iter.length_toList_eq_count,
toList_internalIter]; rfl
@[simp]
public theorem ListSlice.size_toArray {xs : ListSlice α} :
public theorem size_toArray {xs : ListSlice α} :
xs.toArray.size = xs.size := by
simp [ ListSlice.toArray_toList]
end ListSlice
namespace List
@[simp]
@@ -101,7 +101,7 @@ public theorem mkSlice_rcc_eq_mkSlice_rco {xs : List α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : List α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.take (hi + 1)).drop lo := by
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : List α} {lo hi : Nat} :
@@ -137,7 +137,7 @@ public theorem mkSlice_roo_eq_mkSlice_rco {xs : List α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : List α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.take hi).drop (lo + 1) := by
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : List α} {lo hi : Nat} :
@@ -157,7 +157,7 @@ public theorem mkSlice_roc_eq_mkSlice_roo {xs : List α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : List α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.take (hi + 1)).drop (lo + 1) := by
rw [mkSlice_roc_eq_mkSlice_roo, toList_mkSlice_roo]
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : List α} {lo hi : Nat} :
@@ -177,7 +177,7 @@ public theorem mkSlice_roi_eq_mkSlice_rci {xs : List α} {lo : Nat} :
@[simp]
public theorem toList_mkSlice_roi {xs : List α} {lo : Nat} :
xs[lo<...*].toList = xs.drop (lo + 1) := by
rw [mkSlice_roi_eq_mkSlice_rci, toList_mkSlice_rci]
simp
@[simp]
public theorem toArray_mkSlice_roi {xs : List α} {lo : Nat} :
@@ -197,7 +197,7 @@ public theorem mkSlice_rio_eq_mkSlice_rco {xs : List α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : List α} {hi : Nat} :
xs[*...hi].toList = xs.take hi := by
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : List α} {hi : Nat} :
@@ -217,7 +217,7 @@ public theorem mkSlice_ric_eq_mkSlice_rio {xs : List α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : List α} {hi : Nat} :
xs[*...=hi].toList = xs.take (hi + 1) := by
rw [mkSlice_ric_eq_mkSlice_rio, toList_mkSlice_rio]
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : List α} {hi : Nat} :
@@ -237,7 +237,7 @@ public theorem mkSlice_rii_eq_mkSlice_rci {xs : List α} :
@[simp]
public theorem toList_mkSlice_rii {xs : List α} :
xs[*...*].toList = xs := by
rw [mkSlice_rii_eq_mkSlice_rci, toList_mkSlice_rci, List.drop_zero]
simp
@[simp]
public theorem toArray_mkSlice_rii {xs : List α} :
@@ -277,7 +277,7 @@ public theorem mkSlice_rcc_eq_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
rw [mkSlice_rcc_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
@@ -307,7 +307,7 @@ public theorem mkSlice_roo_eq_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roo {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
rw [mkSlice_roo_eq_mkSlice_rco, toList_mkSlice_rco]
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : ListSlice α} {lo hi : Nat} :
@@ -327,7 +327,7 @@ public theorem mkSlice_roc_eq_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
@[simp]
public theorem toList_mkSlice_roc {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
rw [mkSlice_roc_eq_mkSlice_rcc, toList_mkSlice_rcc]
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : ListSlice α} {lo hi : Nat} :
@@ -342,7 +342,7 @@ public theorem mkSlice_roi_eq_mkSlice_rci {xs : ListSlice α} {lo : Nat} :
@[simp]
public theorem toList_mkSlice_roi {xs : ListSlice α} {lo : Nat} :
xs[lo<...*].toList = xs.toList.drop (lo + 1) := by
rw [mkSlice_roi_eq_mkSlice_rci, toList_mkSlice_rci]
simp
@[simp]
public theorem toArray_mkSlice_roi {xs : ListSlice α} {lo : Nat} :
@@ -359,7 +359,7 @@ public theorem mkSlice_rio_eq_mkSlice_rco {xs : ListSlice α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_rio {xs : ListSlice α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
rw [mkSlice_rio_eq_mkSlice_rco, toList_mkSlice_rco, List.drop_zero]
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : ListSlice α} {hi : Nat} :
@@ -379,7 +379,7 @@ public theorem mkSlice_ric_eq_mkSlice_rcc {xs : ListSlice α} {hi : Nat} :
@[simp]
public theorem toList_mkSlice_ric {xs : ListSlice α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
rw [mkSlice_ric_eq_mkSlice_rcc, toList_mkSlice_rcc, List.drop_zero]
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : ListSlice α} {hi : Nat} :
@@ -391,16 +391,6 @@ public theorem mkSlice_rii {xs : ListSlice α} :
xs[*...*] = xs := by
simp [Std.Rii.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rii {xs : ListSlice α} :
xs[*...*].toList = xs.toList := by
rw [mkSlice_rii]
@[simp]
public theorem toArray_mkSlice_rii {xs : ListSlice α} :
xs[*...*].toArray = xs.toArray := by
rw [mkSlice_rii]
end ListSlice
end ListSubslices

View File

@@ -16,37 +16,36 @@ open Std.Iterators
namespace Std.Slice
instance {x : γ} [ToIterator x m β] : ToIterator (Slice.mk x) m β where
State := ToIterator.State x m
iterMInternal := ToIterator.iterMInternal
instance [ToIterator γ m α β] : ToIterator (Slice γ) m α β where
iterMInternal x := ToIterator.iterMInternal x.internalRepresentation
/--
Internal function to obtain an iterator from a slice. Users should import `Std.Data.Iterators`
and use `Std.Slice.iter` instead.
-/
@[always_inline, inline]
def Internal.iter (s : Slice γ) [ToIterator s Id β] :=
def Internal.iter [ToIterator (Slice γ) Id α β] (s : Slice γ) :=
ToIterator.iter s
/--
This type class provides support for the `Slice.size` function.
-/
class SliceSize (α : Type u) where
class SliceSize (γ : Type u) where
/-- Computes the slice of a `Slice`. Use `Slice.size` instead. -/
size (slice : Slice α) : Nat
size (slice : Slice γ) : Nat
/--
This type class states that the slice's iterator emits exactly `Slice.size` elements before
terminating.
-/
class LawfulSliceSize (α : Type u) [SliceSize α] [ s : Slice α, ToIterator s Id β]
[ s : Slice α, Iterator (ToIterator.State s Id) Id β] where
class LawfulSliceSize (γ : Type u) [SliceSize γ] [ToIterator (Slice γ) Id α β]
[Iterator α Id β] where
/-- The iterator for every `Slice α` is finite. -/
[finite : s : Slice α, Finite (ToIterator.State s Id) Id]
/-- The iterator of a slice `s` of type `Slice α` emits exactly `SliceSize.size s` elements. -/
[finite : Finite α Id]
/-- The iterator of a slice `s` of type `Slice γ` emits exactly `SliceSize.size s` elements. -/
lawful :
letI (s : Slice α) : IteratorLoop (ToIterator.State s Id) Id Id := .defaultImplementation
s : Slice α, SliceSize.size s = (ToIterator.iter s).count
letI : IteratorLoop α Id Id := .defaultImplementation
s : Slice γ, SliceSize.size s = (ToIterator.iter (γ := Slice γ) s).count
/--
Returns the number of elements with distinct indices in the given slice.
@@ -59,26 +58,27 @@ def size (s : Slice γ) [SliceSize γ] :=
/-- Allocates a new array that contains the elements of the slice. -/
@[always_inline, inline]
def toArray (s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : Array β :=
def toArray [ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id] (s : Slice γ) : Array β :=
Internal.iter s |>.toArray
/-- Allocates a new list that contains the elements of the slice. -/
@[always_inline, inline]
def toList (s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorCollect (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : List β :=
def toList [ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id]
(s : Slice γ) : List β :=
Internal.iter s |>.toList
/-- Allocates a new list that contains the elements of the slice in reverse order. -/
@[always_inline, inline]
def toListRev (s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[Finite (ToIterator.State s Id) Id] : List β :=
def toListRev [ToIterator (Slice γ) Id α β] [Iterator α Id β]
[Finite α Id] (s : Slice γ) : List β :=
Internal.iter s |>.toListRev
instance {γ : Type u} {β : Type v} [ s : Slice γ, ToIterator s Id β]
[ s : Slice γ, Iterator (ToIterator.State s Id) Id β]
[ s : Slice γ, IteratorLoop (ToIterator.State s Id) Id m]
[ s : Slice γ, Finite (ToIterator.State s Id) Id] :
instance {γ : Type u} {β : Type v} [Monad m] [ToIterator (Slice γ) Id α β]
[Iterator α Id β]
[IteratorLoop α Id m]
[Finite α Id] :
ForIn m (Slice γ) β where
forIn s init f :=
forIn (Internal.iter s) init f
@@ -110,8 +110,9 @@ none
@[always_inline, inline]
def foldlM {γ : Type u} {β : Type v}
{δ : Type w} {m : Type w Type w'} [Monad m] (f : δ β m δ) (init : δ)
(s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorLoop (ToIterator.State s Id) Id m] [Finite (ToIterator.State s Id) Id] : m δ :=
[ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id]
(s : Slice γ) : m δ :=
Internal.iter s |>.foldM f init
/--
@@ -125,8 +126,9 @@ Examples for the special case of subarrays:
@[always_inline, inline]
def foldl {γ : Type u} {β : Type v}
{δ : Type w} (f : δ β δ) (init : δ)
(s : Slice γ) [ToIterator s Id β] [Iterator (ToIterator.State s Id) Id β]
[IteratorLoop (ToIterator.State s Id) Id Id] [Finite (ToIterator.State s Id) Id] : δ :=
[ToIterator (Slice γ) Id α β] [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id]
(s : Slice γ) : δ :=
Internal.iter s |>.fold f init
end Std.Slice

View File

@@ -66,7 +66,7 @@ protected partial def Stream.forIn [Stream ρ α] [Monad m] (s : ρ) (b : β) (f
| none => return b
visit s b
instance (priority := low) [Stream ρ α] : ForIn m ρ α where
instance (priority := low) [Monad m] [Stream ρ α] : ForIn m ρ α where
forIn := Stream.forIn
instance : ToStream (List α) (List α) where

View File

@@ -13,7 +13,6 @@ public import Init.Data.String.Defs
public import Init.Data.String.Extra
public import Init.Data.String.Iterator
public import Init.Data.String.Lemmas
public import Init.Data.String.Repr
public import Init.Data.String.Bootstrap
public import Init.Data.String.Slice
public import Init.Data.String.Pattern

File diff suppressed because it is too large Load Diff

View File

@@ -13,9 +13,6 @@ public section
namespace String
@[deprecated Pos.Raw (since := "2025-09-30")]
abbrev Pos := Pos.Raw
instance : OfNat String.Pos.Raw (nat_lit 0) where
ofNat := {}

View File

@@ -74,11 +74,11 @@ Encodes a string in UTF-8 as an array of bytes.
-/
@[extern "lean_string_to_utf8"]
def String.toUTF8 (a : @& String) : ByteArray :=
a.bytes
a.toByteArray
@[simp] theorem String.toUTF8_eq_bytes {s : String} : s.toUTF8 = s.bytes := (rfl)
@[simp] theorem String.toUTF8_eq_toByteArray {s : String} : s.toUTF8 = s.toByteArray := (rfl)
@[simp] theorem String.bytes_empty : "".bytes = ByteArray.empty := (rfl)
@[simp] theorem String.toByteArray_empty : "".toByteArray = ByteArray.empty := (rfl)
/--
Appends two strings. Usually accessed via the `++` operator.
@@ -92,33 +92,33 @@ Examples:
-/
@[extern "lean_string_append", expose]
def String.append (s : String) (t : @& String) : String where
bytes := s.bytes ++ t.bytes
toByteArray := s.toByteArray ++ t.toByteArray
isValidUTF8 := s.isValidUTF8.append t.isValidUTF8
instance : Append String where
append s t := s.append t
@[simp]
theorem String.bytes_append {s t : String} : (s ++ t).bytes = s.bytes ++ t.bytes := (rfl)
theorem String.toByteArray_append {s t : String} : (s ++ t).toByteArray = s.toByteArray ++ t.toByteArray := (rfl)
theorem String.bytes_inj {s t : String} : s.bytes = t.bytes s = t := by
theorem String.toByteArray_inj {s t : String} : s.toByteArray = t.toByteArray s = t := by
refine fun h => ?_, (· rfl)
rcases s with s
rcases t with t
subst h
rfl
@[simp] theorem String.bytes_ofList {l : List Char} : (String.ofList l).bytes = l.utf8Encode := by
@[simp] theorem String.toByteArray_ofList {l : List Char} : (String.ofList l).toByteArray = l.utf8Encode := by
simp [String.ofList]
@[deprecated String.bytes_ofList (since := "2025-10-30")]
theorem List.bytes_asString {l : List Char} : (String.ofList l).bytes = l.utf8Encode :=
String.bytes_ofList
@[deprecated String.toByteArray_ofList (since := "2025-10-30")]
theorem List.toByteArray_asString {l : List Char} : (String.ofList l).toByteArray = l.utf8Encode :=
String.toByteArray_ofList
theorem String.exists_eq_ofList (s : String) :
l : List Char, s = String.ofList l := by
rcases s with _, l, rfl
refine l, by simp [ String.bytes_inj]
refine l, by simp [ String.toByteArray_inj]
@[deprecated String.exists_eq_ofList (since := "2025-10-30")]
theorem String.exists_eq_asString (s : String) :
@@ -134,18 +134,14 @@ theorem String.utf8ByteSize_append {s t : String} :
simp [utf8ByteSize]
@[simp]
theorem String.size_bytes {s : String} : s.bytes.size = s.utf8ByteSize := rfl
theorem String.size_toByteArray {s : String} : s.toByteArray.size = s.utf8ByteSize := rfl
@[simp]
theorem String.bytes_push {s : String} {c : Char} : (s.push c).bytes = s.bytes ++ [c].utf8Encode := by
theorem String.toByteArray_push {s : String} {c : Char} : (s.push c).toByteArray = s.toByteArray ++ [c].utf8Encode := by
simp [push]
namespace String
@[deprecated rawEndPos (since := "2025-10-20")]
def endPos (s : String) : String.Pos.Raw :=
s.rawEndPos
/-- The start position of the string, as a `String.Pos.Raw.` -/
def rawStartPos (_s : String) : String.Pos.Raw :=
0
@@ -164,11 +160,11 @@ theorem utf8ByteSize_ofByteArray {b : ByteArray} {h} :
(String.ofByteArray b h).utf8ByteSize = b.size := rfl
@[simp]
theorem bytes_singleton {c : Char} : (String.singleton c).bytes = [c].utf8Encode := by
theorem toByteArray_singleton {c : Char} : (String.singleton c).toByteArray = [c].utf8Encode := by
simp [singleton]
theorem singleton_eq_ofList {c : Char} : String.singleton c = String.ofList [c] := by
simp [ String.bytes_inj]
simp [ String.toByteArray_inj]
@[deprecated singleton_eq_ofList (since := "2025-10-30")]
theorem singleton_eq_asString {c : Char} : String.singleton c = String.ofList [c] :=
@@ -176,20 +172,20 @@ theorem singleton_eq_asString {c : Char} : String.singleton c = String.ofList [c
@[simp]
theorem append_singleton {s : String} {c : Char} : s ++ singleton c = s.push c := by
simp [ bytes_inj]
simp [ toByteArray_inj]
@[simp]
theorem append_left_inj {s₁ s₂ : String} (t : String) :
s₁ ++ t = s₂ ++ t s₁ = s₂ := by
simp [ bytes_inj]
simp [ toByteArray_inj]
theorem append_assoc {s₁ s₂ s₃ : String} : s₁ ++ s₂ ++ s₃ = s₁ ++ (s₂ ++ s₃) := by
simp [ bytes_inj, ByteArray.append_assoc]
simp [ toByteArray_inj, ByteArray.append_assoc]
@[simp]
theorem utf8ByteSize_eq_zero_iff {s : String} : s.utf8ByteSize = 0 s = "" := by
refine fun h => ?_, fun h => h utf8ByteSize_empty
simpa [ bytes_inj, ByteArray.size_eq_zero_iff] using h
simpa [ toByteArray_inj, ByteArray.size_eq_zero_iff] using h
theorem rawEndPos_eq_zero_iff {b : String} : b.rawEndPos = 0 b = "" := by
simp
@@ -300,14 +296,14 @@ Examples:
-/
structure Pos.Raw.IsValid (s : String) (off : String.Pos.Raw) : Prop where private mk ::
le_rawEndPos : off s.rawEndPos
isValidUTF8_extract_zero : (s.bytes.extract 0 off.byteIdx).IsValidUTF8
isValidUTF8_extract_zero : (s.toByteArray.extract 0 off.byteIdx).IsValidUTF8
theorem Pos.Raw.IsValid.le_utf8ByteSize {s : String} {off : String.Pos.Raw} (h : off.IsValid s) :
off.byteIdx s.utf8ByteSize := by
simpa [Pos.Raw.le_iff] using h.le_rawEndPos
theorem Pos.Raw.isValid_iff_isValidUTF8_extract_zero {s : String} {p : Pos.Raw} :
p.IsValid s p s.rawEndPos (s.bytes.extract 0 p.byteIdx).IsValidUTF8 :=
p.IsValid s p s.rawEndPos (s.toByteArray.extract 0 p.byteIdx).IsValidUTF8 :=
fun h₁, h₂ => h₁, h₂, fun h₁, h₂ => h₁, h₂
@[deprecated le_rawEndPos (since := "2025-10-20")]
@@ -323,7 +319,7 @@ theorem Pos.Raw.isValid_zero {s : String} : (0 : Pos.Raw).IsValid s where
@[simp]
theorem Pos.Raw.isValid_rawEndPos {s : String} : s.rawEndPos.IsValid s where
le_rawEndPos := by simp
isValidUTF8_extract_zero := by simp [ size_bytes, s.isValidUTF8]
isValidUTF8_extract_zero := by simp [ size_toByteArray, s.isValidUTF8]
theorem Pos.Raw.isValid_of_eq_rawEndPos {s : String} {p : Pos.Raw} (h : p = s.rawEndPos) :
p.IsValid s := by
@@ -341,55 +337,55 @@ theorem Pos.Raw.isValid_empty_iff {p : Pos.Raw} : p.IsValid "" ↔ p = 0 := by
simp
/--
A `ValidPos s` is a byte offset in `s` together with a proof that this position is at a UTF-8
A `Pos s` is a byte offset in `s` together with a proof that this position is at a UTF-8
character boundary.
-/
@[ext]
structure ValidPos (s : String) where
/-- The underlying byte offset of the `ValidPos`. -/
structure Pos (s : String) where
/-- The underlying byte offset of the `Pos`. -/
offset : Pos.Raw
/-- The proof that `offset` is valid for the string `s`. -/
isValid : offset.IsValid s
deriving @[expose] DecidableEq
/-- The start position of `s`, as an `s.ValidPos`. -/
/-- The start position of `s`, as an `s.Pos`. -/
@[inline, expose]
def startValidPos (s : String) : s.ValidPos where
def startPos (s : String) : s.Pos where
offset := 0
isValid := by simp
@[simp]
theorem offset_startValidPos {s : String} : s.startValidPos.offset = 0 := (rfl)
theorem offset_startPos {s : String} : s.startPos.offset = 0 := (rfl)
instance {s : String} : Inhabited s.ValidPos where
default := s.startValidPos
instance {s : String} : Inhabited s.Pos where
default := s.startPos
/-- The past-the-end position of `s`, as an `s.ValidPos`. -/
/-- The past-the-end position of `s`, as an `s.Pos`. -/
@[inline, expose]
def endValidPos (s : String) : s.ValidPos where
def endPos (s : String) : s.Pos where
offset := s.rawEndPos
isValid := by simp
@[simp]
theorem offset_endValidPos {s : String} : s.endValidPos.offset = s.rawEndPos := (rfl)
theorem offset_endPos {s : String} : s.endPos.offset = s.rawEndPos := (rfl)
instance {s : String} : LE s.ValidPos where
instance {s : String} : LE s.Pos where
le l r := l.offset r.offset
instance {s : String} : LT s.ValidPos where
instance {s : String} : LT s.Pos where
lt l r := l.offset < r.offset
theorem ValidPos.le_iff {s : String} {l r : s.ValidPos} : l r l.offset r.offset :=
theorem Pos.le_iff {s : String} {l r : s.Pos} : l r l.offset r.offset :=
Iff.rfl
theorem ValidPos.lt_iff {s : String} {l r : s.ValidPos} : l < r l.offset < r.offset :=
theorem Pos.lt_iff {s : String} {l r : s.Pos} : l < r l.offset < r.offset :=
Iff.rfl
instance {s : String} (l r : s.ValidPos) : Decidable (l r) :=
decidable_of_iff' _ ValidPos.le_iff
instance {s : String} (l r : s.Pos) : Decidable (l r) :=
decidable_of_iff' _ Pos.le_iff
instance {s : String} (l r : s.ValidPos) : Decidable (l < r) :=
decidable_of_iff' _ ValidPos.lt_iff
instance {s : String} (l r : s.Pos) : Decidable (l < r) :=
decidable_of_iff' _ Pos.lt_iff
/--
A region or slice of some underlying string.
@@ -406,14 +402,14 @@ structure Slice where
/-- The underlying strings. -/
str : String
/-- The byte position of the start of the string slice. -/
startInclusive : str.ValidPos
startInclusive : str.Pos
/-- The byte position of the end of the string slice. -/
endExclusive : str.ValidPos
endExclusive : str.Pos
/-- The slice is not degenerate (but it may be empty). -/
startInclusive_le_endExclusive : startInclusive endExclusive
instance : Inhabited Slice where
default := "", "".startValidPos, "".startValidPos, by simp [ValidPos.le_iff]
default := "", "".startPos, "".startPos, by simp [Pos.le_iff]
/--
Returns a slice that contains the entire string.
@@ -421,15 +417,18 @@ Returns a slice that contains the entire string.
@[inline, expose] -- expose for the defeq `s.toSlice.str = s`.
def toSlice (s : String) : Slice where
str := s
startInclusive := s.startValidPos
endExclusive := s.endValidPos
startInclusive_le_endExclusive := by simp [ValidPos.le_iff, Pos.Raw.le_iff]
startInclusive := s.startPos
endExclusive := s.endPos
startInclusive_le_endExclusive := by simp [Pos.le_iff, Pos.Raw.le_iff]
instance : Coe String String.Slice where
coe := String.toSlice
@[simp]
theorem startInclusive_toSlice {s : String} : s.toSlice.startInclusive = s.startValidPos := rfl
theorem startInclusive_toSlice {s : String} : s.toSlice.startInclusive = s.startPos := rfl
@[simp]
theorem endExclusive_toSlice {s : String} : s.toSlice.endExclusive = s.endValidPos := rfl
theorem endExclusive_toSlice {s : String} : s.toSlice.endExclusive = s.endPos := rfl
@[simp]
theorem str_toSlice {s : String} : s.toSlice.str = s := rfl
@@ -532,7 +531,7 @@ instance {s : Slice} : Inhabited s.Pos where
theorem Slice.offset_startInclusive_add_self {s : Slice} :
s.startInclusive.offset + s = s.endExclusive.offset := by
have := s.startInclusive_le_endExclusive
simp_all [String.Pos.Raw.ext_iff, ValidPos.le_iff, Pos.Raw.le_iff, utf8ByteSize_eq]
simp_all [String.Pos.Raw.ext_iff, Pos.le_iff, Pos.Raw.le_iff, utf8ByteSize_eq]
@[simp]
theorem Pos.Raw.offsetBy_rawEndPos_left {p : Pos.Raw} {s : String} :
@@ -591,18 +590,18 @@ instance {s : Slice} (l r : s.Pos) : Decidable (l < r) :=
decidable_of_iff' _ Slice.Pos.lt_iff
/--
`pos.IsAtEnd` is just shorthand for `pos = s.endValidPos` that is easier to write if `s` is long.
`pos.IsAtEnd` is just shorthand for `pos = s.endPos` that is easier to write if `s` is long.
-/
abbrev ValidPos.IsAtEnd {s : String} (pos : s.ValidPos) : Prop :=
pos = s.endValidPos
abbrev Pos.IsAtEnd {s : String} (pos : s.Pos) : Prop :=
pos = s.endPos
@[simp]
theorem ValidPos.isAtEnd_iff {s : String} {pos : s.ValidPos} :
pos.IsAtEnd pos = s.endValidPos := Iff.rfl
theorem Pos.isAtEnd_iff {s : String} {pos : s.Pos} :
pos.IsAtEnd pos = s.endPos := Iff.rfl
@[inline]
instance {s : String} {pos : s.ValidPos} : Decidable pos.IsAtEnd :=
decidable_of_iff _ ValidPos.isAtEnd_iff
instance {s : String} {pos : s.Pos} : Decidable pos.IsAtEnd :=
decidable_of_iff _ Pos.isAtEnd_iff
/--
`pos.IsAtEnd` is just shorthand for `pos = s.endPos` that is easier to write if `s` is long.
@@ -639,4 +638,20 @@ def toSubstring (s : String) : Substring.Raw :=
def toSubstring' (s : String) : Substring.Raw :=
s.toRawSubstring'
@[deprecated String.Pos (since := "2025-11-24")]
abbrev ValidPos (s : String) : Type :=
s.Pos
@[deprecated String.startPos (since := "2025-11-24")]
abbrev startValidPos (s : String) : s.Pos :=
s.startPos
@[deprecated String.endPos (since := "2025-11-24")]
abbrev endValidPos (s : String) : s.Pos :=
s.endPos
@[deprecated String.toByteArray (since := "2025-11-24")]
abbrev String.bytes (s : String) : ByteArray :=
s.toByteArray
end String

View File

@@ -17,27 +17,6 @@ public section
namespace String
/--
Interprets a string as the decimal representation of a natural number, returning it. Panics if the
string does not contain a decimal natural number.
A string can be interpreted as a decimal natural number if it is not empty and all the characters in
it are digits.
Use `String.isNat` to check whether `String.toNat!` would return a value. `String.toNat?` is a safer
alternative that returns `none` instead of panicking when the string is not a natural number.
Examples:
* `"0".toNat! = 0`
* `"5".toNat! = 5`
* `"587".toNat! = 587`
-/
def toNat! (s : String) : Nat :=
if s.isNat then
s.foldl (fun n c => n*10 + (c.toNat - '0'.toNat)) 0
else
panic! "Nat expected"
@[deprecated ByteArray.utf8DecodeChar? (since := "2025-10-01")]
abbrev utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char :=
a.utf8DecodeChar? i
@@ -50,28 +29,28 @@ abbrev validateUTF8 (a : ByteArray) : Bool :=
a.validateUTF8
private def findLeadingSpacesSize (s : String) : Nat :=
let it := s.startValidPos
let it := it.find? (· == '\n') |>.bind String.ValidPos.next?
let it := s.startPos
let it := it.find? (· == '\n') |>.bind String.Pos.next?
match it with
| some it => consumeSpaces it 0 s.length
| none => 0
where
consumeSpaces {s : String} (it : s.ValidPos) (curr min : Nat) : Nat :=
consumeSpaces {s : String} (it : s.Pos) (curr min : Nat) : Nat :=
if h : it.IsAtEnd then min
else if it.get h == ' ' || it.get h == '\t' then consumeSpaces (it.next h) (curr + 1) min
else if it.get h == '\n' then findNextLine (it.next h) min
else findNextLine (it.next h) (Nat.min curr min)
termination_by it
findNextLine {s : String} (it : s.ValidPos) (min : Nat) : Nat :=
findNextLine {s : String} (it : s.Pos) (min : Nat) : Nat :=
if h : it.IsAtEnd then min
else if it.get h == '\n' then consumeSpaces (it.next h) 0 min
else findNextLine (it.next h) min
termination_by it
private def removeNumLeadingSpaces (n : Nat) (s : String) : String :=
consumeSpaces n s.startValidPos ""
consumeSpaces n s.startPos ""
where
consumeSpaces (n : Nat) {s : String} (it : s.ValidPos) (r : String) : String :=
consumeSpaces (n : Nat) {s : String} (it : s.Pos) (r : String) : String :=
match n with
| 0 => saveLine it r
| n+1 =>
@@ -79,7 +58,7 @@ where
else if it.get h == ' ' || it.get h == '\t' then consumeSpaces n (it.next h) r
else saveLine it r
termination_by (it, 1)
saveLine {s : String} (it : s.ValidPos) (r : String) : String :=
saveLine {s : String} (it : s.Pos) (r : String) : String :=
if h : it.IsAtEnd then r
else if it.get h == '\n' then consumeSpaces n (it.next h) (r.push '\n')
else saveLine (it.next h) (r.push (it.get h))

View File

@@ -25,9 +25,9 @@ An iterator over the characters (Unicode code points) in a `String`. Typically c
`String.iter`.
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`.
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
String iterators pair a string with a valid byte index. This allows efficient character-by-character
processing of strings while avoiding the need to manually ensure that byte indices are used with the
@@ -57,9 +57,9 @@ structure Iterator where
/-- Creates an iterator at the beginning of the string.
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`.
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
-/
@[inline] def mkIterator (s : String) : Iterator :=
s, 0
@@ -95,9 +95,9 @@ def pos := Iterator.i
Gets the character at the iterator's current position.
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`.
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
A run-time bounds check is performed. Use `String.Iterator.curr'` to avoid redundant bounds checks.
@@ -110,9 +110,9 @@ If the position is invalid, returns `(default : Char)`.
Moves the iterator's position forward by one character, unconditionally.
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`.
`String.Pos` instead, which is similar, but safer. To iterate over a string `s`, start with
`p : s.startPos`, advance it using `p.next`, access the current character using `p.get` and
check if the position is at the end using `p = s.endPos` or `p.IsAtEnd`.
It is only valid to call this function if the iterator is not at the end of the string (i.e.
if `Iterator.atEnd` is `false`); otherwise, the resulting iterator will be invalid.

View File

@@ -8,6 +8,7 @@ module
prelude
public import Init.Data.String.Lemmas.Splits
public import Init.Data.String.Lemmas.Modify
public import Init.Data.String.Lemmas.Search
public import Init.Data.Char.Order
public import Init.Data.Char.Lemmas
public import Init.Data.List.Lex

View File

@@ -41,6 +41,18 @@ theorem singleton_ne_empty {c : Char} : singleton c ≠ "" := by
@[simp]
theorem Slice.Pos.toCopy_inj {s : Slice} {p₁ p₂ : s.Pos} : p₁.toCopy = p₂.toCopy p₁ = p₂ := by
simp [Pos.ext_iff, ValidPos.ext_iff]
simp [String.Pos.ext_iff, Pos.ext_iff]
@[simp]
theorem Pos.startPos_le {s : String} (p : s.Pos) : s.startPos p := by
simp [Pos.le_iff, Pos.Raw.le_iff]
@[simp]
theorem Slice.Pos.startPos_le {s : Slice} (p : s.Pos) : s.startPos p := by
simp [Pos.le_iff, Pos.Raw.le_iff]
@[simp]
theorem Slice.Pos.le_endPos {s : Slice} (p : s.Pos) : p s.endPos :=
p.isValidForSlice.le_rawEndPos
end String

View File

@@ -22,22 +22,22 @@ public section
namespace String
/-- You might want to invoke `ValidPos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem ValidPos.Splits.pastSet {s : String} {p : s.ValidPos} {t₁ t₂ : String}
/-- You might want to invoke `Pos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem Pos.Splits.pastSet {s : String} {p : s.Pos} {t₁ t₂ : String}
{c d : Char} (h : p.Splits t₁ (singleton c ++ t₂)) :
(p.pastSet d h.ne_endValidPos_of_singleton).Splits (t₁ ++ singleton d) t₂ := by
generalize h.ne_endValidPos_of_singleton = hp
(p.pastSet d h.ne_endPos_of_singleton).Splits (t₁ ++ singleton d) t₂ := by
generalize h.ne_endPos_of_singleton = hp
obtain rfl, rfl, rfl := by simpa using h.eq (p.splits_next_right hp)
apply splits_pastSet
/-- You might want to invoke `ValidPos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem ValidPos.Splits.pastModify {s : String} {p : s.ValidPos} {t₁ t₂ : String}
/-- You might want to invoke `Pos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem Pos.Splits.pastModify {s : String} {p : s.Pos} {t₁ t₂ : String}
{c : Char} (h : p.Splits t₁ (singleton c ++ t₂)) :
(p.pastModify f h.ne_endValidPos_of_singleton).Splits
(t₁ ++ singleton (f (p.get h.ne_endValidPos_of_singleton))) t₂ :=
(p.pastModify f h.ne_endPos_of_singleton).Splits
(t₁ ++ singleton (f (p.get h.ne_endPos_of_singleton))) t₂ :=
h.pastSet
theorem toList_mapAux {f : Char Char} {s : String} {p : s.ValidPos}
theorem toList_mapAux {f : Char Char} {s : String} {p : s.Pos}
(h : p.Splits t₁ t₂) : (mapAux f s p).toList = t₁.toList ++ t₂.toList.map f := by
fun_induction mapAux generalizing t₁ t₂ with
| case1 s => simp_all
@@ -47,7 +47,7 @@ theorem toList_mapAux {f : Char → Char} {s : String} {p : s.ValidPos}
@[simp]
theorem toList_map {f : Char Char} {s : String} : (s.map f).toList = s.toList.map f := by
simp [map, toList_mapAux s.splits_startValidPos]
simp [map, toList_mapAux s.splits_startPos]
@[simp]
theorem length_map {f : Char Char} {s : String} : (s.map f).length = s.length := by

View File

@@ -0,0 +1,32 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
public import Init.Data.String.Search
import all Init.Data.String.Search
public section
namespace String
open String.Slice Pattern
variable {ρ : Type} {σ : Slice Type}
variable [ s, Std.Iterators.Iterator (σ s) Id (SearchStep s)]
variable [ s, Std.Iterators.Finite (σ s) Id]
variable [ s, Std.Iterators.IteratorLoop (σ s) Id Id]
@[simp]
theorem Slice.Pos.le_find {s : Slice} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
pos pos.find pattern := by
simp [Slice.Pos.find]
@[simp]
theorem Pos.le_find {s : String} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
pos pos.find pattern := by
simp [Pos.find, toSlice_le]
end String

View File

@@ -11,7 +11,7 @@ import Init.Data.ByteArray.Lemmas
import Init.Data.String.Lemmas.Basic
/-!
# `Splits` predicates on `String.ValidPos` and `String.Slice.Pos`.
# `Splits` predicates on `String.Pos` and `String.Slice.Pos`.
We introduce the predicate `p.Splits t₁ t₂` for a position `p` on a string or slice `s`, which means
that `s = t₁ ++ t₂` with `p` lying between the two parts. This is a useful primitive when verifying
@@ -26,7 +26,7 @@ namespace String
We say that `p` splits `s` into `t₁` and `t₂` if `s = t₁ ++ t₂` and `p` is the position between `t₁`
and `t₂`.
-/
structure ValidPos.Splits {s : String} (p : s.ValidPos) (t₁ t₂ : String) : Prop where
structure Pos.Splits {s : String} (p : s.Pos) (t₁ t₂ : String) : Prop where
eq_append : s = t₁ ++ t₂
offset_eq_rawEndPos : p.offset = t₁.rawEndPos
@@ -39,11 +39,11 @@ structure Slice.Pos.Splits {s : Slice} (p : s.Pos) (t₁ t₂ : String) : Prop w
offset_eq_rawEndPos : p.offset = t₁.rawEndPos
@[simp]
theorem ValidPos.splits_cast_iff {s₁ s₂ : String} {h : s₁ = s₂} {p : s₁.ValidPos} {t₁ t₂ : String} :
theorem Pos.splits_cast_iff {s₁ s₂ : String} {h : s₁ = s₂} {p : s₁.Pos} {t₁ t₂ : String} :
(p.cast h).Splits t₁ t₂ p.Splits t₁ t₂ := by
subst h; simp
theorem ValidPos.Splits.cast {s₁ s₂ : String} {p : s₁.ValidPos} {t₁ t₂ : String} (h : s₁ = s₂) :
theorem Pos.Splits.cast {s₁ s₂ : String} {p : s₁.Pos} {t₁ t₂ : String} (h : s₁ = s₂) :
p.Splits t₁ t₂ (p.cast h).Splits t₁ t₂ :=
splits_cast_iff.mpr
@@ -72,17 +72,17 @@ theorem Slice.Pos.splits_toCopy_iff {s : Slice} {p : s.Pos} {t₁ t₂ : String}
splits_of_splits_toCopy, (·.toCopy)
@[simp]
theorem ValidPos.splits_toSlice_iff {s : String} {p : s.ValidPos} {t₁ t₂ : String} :
theorem Pos.splits_toSlice_iff {s : String} {p : s.Pos} {t₁ t₂ : String} :
p.toSlice.Splits t₁ t₂ p.Splits t₁ t₂ := by
rw [ Slice.Pos.splits_toCopy_iff, p.toCopy_toSlice_eq_cast, splits_cast_iff]
theorem ValidPos.Splits.toSlice {s : String} {p : s.ValidPos} {t₁ t₂ : String}
theorem Pos.Splits.toSlice {s : String} {p : s.Pos} {t₁ t₂ : String}
(h : p.Splits t₁ t₂) : p.toSlice.Splits t₁ t₂ :=
splits_toSlice_iff.mpr h
theorem ValidPos.splits {s : String} (p : s.ValidPos) :
theorem Pos.splits {s : String} (p : s.Pos) :
p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where
eq_append := by simp [ bytes_inj, Slice.bytes_copy, size_bytes]
eq_append := by simp [ toByteArray_inj, Slice.toByteArray_copy, size_toByteArray]
offset_eq_rawEndPos := by simp
theorem Slice.Pos.splits {s : Slice} (p : s.Pos) :
@@ -90,23 +90,23 @@ theorem Slice.Pos.splits {s : Slice} (p : s.Pos) :
eq_append := copy_eq_copy_sliceTo
offset_eq_rawEndPos := by simp
theorem ValidPos.Splits.bytes_left_eq {s : String} {p : s.ValidPos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₁.bytes = s.bytes.extract 0 p.offset.byteIdx := by
theorem Pos.Splits.toByteArray_left_eq {s : String} {p : s.Pos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₁.toByteArray = s.toByteArray.extract 0 p.offset.byteIdx := by
simp [h.eq_append, h.offset_eq_rawEndPos, ByteArray.extract_append_eq_left]
theorem ValidPos.Splits.bytes_right_eq {s : String} {p : s.ValidPos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₂.bytes = s.bytes.extract p.offset.byteIdx s.utf8ByteSize := by
theorem Pos.Splits.toByteArray_right_eq {s : String} {p : s.Pos} {t₁ t₂}
(h : p.Splits t₁ t₂) : t₂.toByteArray = s.toByteArray.extract p.offset.byteIdx s.utf8ByteSize := by
simp [h.eq_append, h.offset_eq_rawEndPos, ByteArray.extract_append_eq_right]
theorem ValidPos.Splits.eq_left {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄}
theorem Pos.Splits.eq_left {s : String} {p : s.Pos} {t₁ t₂ t₃ t₄}
(h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ := by
rw [ String.bytes_inj, h₁.bytes_left_eq, h₂.bytes_left_eq]
rw [ String.toByteArray_inj, h₁.toByteArray_left_eq, h₂.toByteArray_left_eq]
theorem ValidPos.Splits.eq_right {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄}
theorem Pos.Splits.eq_right {s : String} {p : s.Pos} {t₁ t₂ t₃ t₄}
(h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₂ = t₄ := by
rw [ String.bytes_inj, h₁.bytes_right_eq, h₂.bytes_right_eq]
rw [ String.toByteArray_inj, h₁.toByteArray_right_eq, h₂.toByteArray_right_eq]
theorem ValidPos.Splits.eq {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄}
theorem Pos.Splits.eq {s : String} {p : s.Pos} {t₁ t₂ t₃ t₄}
(h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ t₂ = t₄ :=
h₁.eq_left h₂, h₁.eq_right h₂
@@ -123,35 +123,35 @@ theorem Slice.Pos.Splits.eq {s : Slice} {p : s.Pos} {t₁ t₂ t₃ t₄}
(splits_toCopy_iff.2 h₁).eq (splits_toCopy_iff.2 h₂)
@[simp]
theorem splits_endValidPos (s : String) : s.endValidPos.Splits s "" where
theorem splits_endPos (s : String) : s.endPos.Splits s "" where
eq_append := by simp
offset_eq_rawEndPos := by simp
@[simp]
theorem splits_endValidPos_iff {s : String} :
s.endValidPos.Splits t₁ t₂ t₁ = s t₂ = "" :=
fun h => h.eq_left s.splits_endValidPos, h.eq_right s.splits_endValidPos,
by rintro rfl, rfl; exact t₁.splits_endValidPos
theorem splits_endPos_iff {s : String} :
s.endPos.Splits t₁ t₂ t₁ = s t₂ = "" :=
fun h => h.eq_left s.splits_endPos, h.eq_right s.splits_endPos,
by rintro rfl, rfl; exact t₁.splits_endPos
theorem ValidPos.Splits.eq_endValidPos_iff {s : String} {p : s.ValidPos} (h : p.Splits t₁ t₂) :
p = s.endValidPos t₂ = "" :=
fun h' => h.eq_right (h' s.splits_endValidPos),
by rintro rfl; simp [ValidPos.ext_iff, h.offset_eq_rawEndPos, h.eq_append]
theorem Pos.Splits.eq_endPos_iff {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.endPos t₂ = "" :=
fun h' => h.eq_right (h' s.splits_endPos),
by rintro rfl; simp [Pos.ext_iff, h.offset_eq_rawEndPos, h.eq_append]
theorem splits_startValidPos (s : String) : s.startValidPos.Splits "" s where
theorem splits_startPos (s : String) : s.startPos.Splits "" s where
eq_append := by simp
offset_eq_rawEndPos := by simp
@[simp]
theorem splits_startValidPos_iff {s : String} :
s.startValidPos.Splits t₁ t₂ t₁ = "" t₂ = s :=
fun h => h.eq_left s.splits_startValidPos, h.eq_right s.splits_startValidPos,
by rintro rfl, rfl; exact t₂.splits_startValidPos
theorem splits_startPos_iff {s : String} :
s.startPos.Splits t₁ t₂ t₁ = "" t₂ = s :=
fun h => h.eq_left s.splits_startPos, h.eq_right s.splits_startPos,
by rintro rfl, rfl; exact t₂.splits_startPos
theorem ValidPos.Splits.eq_startValidPos_iff {s : String} {p : s.ValidPos} (h : p.Splits t₁ t₂) :
p = s.startValidPos t₁ = "" :=
fun h' => h.eq_left (h' s.splits_startValidPos),
by rintro rfl; simp [ValidPos.ext_iff, h.offset_eq_rawEndPos]
theorem Pos.Splits.eq_startPos_iff {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.startPos t₁ = "" :=
fun h' => h.eq_left (h' s.splits_startPos),
by rintro rfl; simp [Pos.ext_iff, h.offset_eq_rawEndPos]
@[simp]
theorem Slice.splits_endPos (s : Slice) : s.endPos.Splits s.copy "" where
@@ -161,11 +161,11 @@ theorem Slice.splits_endPos (s : Slice) : s.endPos.Splits s.copy "" where
@[simp]
theorem Slice.splits_endPos_iff {s : Slice} :
s.endPos.Splits t₁ t₂ t₁ = s.copy t₂ = "" := by
rw [ Pos.splits_toCopy_iff, endValidPos_copy, splits_endValidPos_iff]
rw [ Pos.splits_toCopy_iff, endPos_copy, String.splits_endPos_iff]
theorem Slice.Pos.Splits.eq_endPos_iff {s : Slice} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.endPos t₂ = "" := by
rw [ toCopy_inj, endValidPos_copy, h.toCopy.eq_endValidPos_iff]
rw [ toCopy_inj, endPos_copy, h.toCopy.eq_endPos_iff]
@[simp]
theorem Slice.splits_startPos (s : Slice) : s.startPos.Splits "" s.copy where
@@ -175,18 +175,18 @@ theorem Slice.splits_startPos (s : Slice) : s.startPos.Splits "" s.copy where
@[simp]
theorem Slice.splits_startPos_iff {s : Slice} :
s.startPos.Splits t₁ t₂ t₁ = "" t₂ = s.copy := by
rw [ Pos.splits_toCopy_iff, startValidPos_copy, splits_startValidPos_iff]
rw [ Pos.splits_toCopy_iff, startPos_copy, String.splits_startPos_iff]
theorem Slice.Pos.Splits.eq_startPos_iff {s : Slice} {p : s.Pos} (h : p.Splits t₁ t₂) :
p = s.startPos t₁ = "" := by
rw [ toCopy_inj, startValidPos_copy, h.toCopy.eq_startValidPos_iff]
rw [ toCopy_inj, startPos_copy, h.toCopy.eq_startPos_iff]
theorem ValidPos.splits_next_right {s : String} (p : s.ValidPos) (hp : p s.endValidPos) :
theorem Pos.splits_next_right {s : String} (p : s.Pos) (hp : p s.endPos) :
p.Splits (s.sliceTo p).copy (singleton (p.get hp) ++ (s.sliceFrom (p.next hp)).copy) where
eq_append := by simpa [ append_assoc] using p.eq_copy_sliceTo_append_get hp
offset_eq_rawEndPos := by simp
theorem ValidPos.splits_next {s : String} (p : s.ValidPos) (hp : p s.endValidPos) :
theorem Pos.splits_next {s : String} (p : s.Pos) (hp : p s.endPos) :
(p.next hp).Splits ((s.sliceTo p).copy ++ singleton (p.get hp)) (s.sliceFrom (p.next hp)).copy where
eq_append := p.eq_copy_sliceTo_append_get hp
offset_eq_rawEndPos := by simp
@@ -201,12 +201,12 @@ theorem Slice.Pos.splits_next {s : Slice} (p : s.Pos) (hp : p ≠ s.endPos) :
eq_append := p.copy_eq_copy_sliceTo_append_get hp
offset_eq_rawEndPos := by simp
theorem ValidPos.Splits.exists_eq_singleton_append {s : String} {p : s.ValidPos}
(hp : p s.endValidPos) (h : p.Splits t₁ t₂) : t₂', t₂ = singleton (p.get hp) ++ t₂' :=
theorem Pos.Splits.exists_eq_singleton_append {s : String} {p : s.Pos}
(hp : p s.endPos) (h : p.Splits t₁ t₂) : t₂', t₂ = singleton (p.get hp) ++ t₂' :=
(s.sliceFrom (p.next hp)).copy, h.eq_right (p.splits_next_right hp)
theorem ValidPos.Splits.exists_eq_append_singleton {s : String} {p : s.ValidPos}
(hp : p s.endValidPos) (h : (p.next hp).Splits t₁ t₂) : t₁', t₁ = t₁' ++ singleton (p.get hp) :=
theorem Pos.Splits.exists_eq_append_singleton {s : String} {p : s.Pos}
(hp : p s.endPos) (h : (p.next hp).Splits t₁ t₂) : t₁', t₁ = t₁' ++ singleton (p.get hp) :=
(s.sliceTo p).copy, h.eq_left (p.splits_next hp)
theorem Slice.Pos.Splits.exists_eq_singleton_append {s : Slice} {p : s.Pos}
@@ -217,13 +217,13 @@ theorem Slice.Pos.Splits.exists_eq_append_singleton {s : Slice} {p : s.Pos}
(hp : p s.endPos) (h : (p.next hp).Splits t₁ t₂) : t₁', t₁ = t₁' ++ singleton (p.get hp) :=
(s.sliceTo p).copy, h.eq_left (p.splits_next hp)
theorem ValidPos.Splits.ne_endValidPos_of_singleton {s : String} {p : s.ValidPos}
(h : p.Splits t₁ (singleton c ++ t₂)) : p s.endValidPos := by
simp [h.eq_endValidPos_iff]
theorem Pos.Splits.ne_endPos_of_singleton {s : String} {p : s.Pos}
(h : p.Splits t₁ (singleton c ++ t₂)) : p s.endPos := by
simp [h.eq_endPos_iff]
theorem ValidPos.Splits.ne_startValidPos_of_singleton {s : String} {p : s.ValidPos}
(h : p.Splits (t₁ ++ singleton c) t₂) : p s.startValidPos := by
simp [h.eq_startValidPos_iff]
theorem Pos.Splits.ne_startPos_of_singleton {s : String} {p : s.Pos}
(h : p.Splits (t₁ ++ singleton c) t₂) : p s.startPos := by
simp [h.eq_startPos_iff]
theorem Slice.Pos.Splits.ne_endPos_of_singleton {s : Slice} {p : s.Pos}
(h : p.Splits t₁ (singleton c ++ t₂)) : p s.endPos := by
@@ -233,10 +233,10 @@ theorem Slice.Pos.Splits.ne_startPos_of_singleton {s : Slice} {p : s.Pos}
(h : p.Splits (t₁ ++ singleton c) t₂) : p s.startPos := by
simp [h.eq_startPos_iff]
/-- You might want to invoke `ValidPos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem ValidPos.Splits.next {s : String} {p : s.ValidPos}
(h : p.Splits t₁ (singleton c ++ t₂)) : (p.next h.ne_endValidPos_of_singleton).Splits (t₁ ++ singleton c) t₂ := by
generalize h.ne_endValidPos_of_singleton = hp
/-- You might want to invoke `Pos.Splits.exists_eq_singleton_append` to be able to apply this. -/
theorem Pos.Splits.next {s : String} {p : s.Pos}
(h : p.Splits t₁ (singleton c ++ t₂)) : (p.next h.ne_endPos_of_singleton).Splits (t₁ ++ singleton c) t₂ := by
generalize h.ne_endPos_of_singleton = hp
obtain rfl, rfl, rfl := by simpa using h.eq (splits_next_right p hp)
exact splits_next p hp

View File

@@ -33,28 +33,28 @@ Examples:
* `("L∃∀N".pos ⟨4⟩ (by decide)).set 'X' (by decide) = "L∃XN"`
-/
@[extern "lean_string_utf8_set", expose]
def ValidPos.set {s : String} (p : s.ValidPos) (c : Char) (hp : p s.endValidPos) : String :=
def Pos.set {s : String} (p : s.Pos) (c : Char) (hp : p s.endPos) : String :=
if hc : c.utf8Size = 1 (p.byte hp).utf8ByteSize isUTF8FirstByte_byte = 1 then
.ofByteArray (s.bytes.set p.offset.byteIdx c.toUInt8 (p.byteIdx_lt_utf8ByteSize hp)) (by
.ofByteArray (s.toByteArray.set p.offset.byteIdx c.toUInt8 (p.byteIdx_lt_utf8ByteSize hp)) (by
rw [ByteArray.set_eq_push_extract_append_extract, hc.2, utf8ByteSize_byte,
ValidPos.byteIdx_offset_next]
Pos.byteIdx_offset_next]
refine ByteArray.IsValidUTF8.append ?_ (p.next hp).isValid.isValidUTF8_extract_utf8ByteSize
exact p.isValid.isValidUTF8_extract_zero.push hc.1)
else
(s.sliceTo p).copy ++ singleton c ++ (s.sliceFrom (p.next hp)).copy
theorem ValidPos.set_eq_append {s : String} {p : s.ValidPos} {c : Char} {hp} :
theorem Pos.set_eq_append {s : String} {p : s.Pos} {c : Char} {hp} :
p.set c hp = (s.sliceTo p).copy ++ singleton c ++ (s.sliceFrom (p.next hp)).copy := by
rw [set]
split
· rename_i h
simp [ bytes_inj, ByteArray.set_eq_push_extract_append_extract, Slice.bytes_copy,
simp [ toByteArray_inj, ByteArray.set_eq_push_extract_append_extract, Slice.toByteArray_copy,
List.utf8Encode_singleton, String.utf8EncodeChar_eq_singleton h.1, utf8ByteSize_byte h.2]
· rfl
theorem Pos.Raw.IsValid.set_of_le {s : String} {p : s.ValidPos} {c : Char} {hp : p s.endValidPos}
theorem Pos.Raw.IsValid.set_of_le {s : String} {p : s.Pos} {c : Char} {hp : p s.endPos}
{q : Pos.Raw} (hq : q.IsValid s) (hpq : q p.offset) : q.IsValid (p.set c hp) := by
rw [ValidPos.set_eq_append, String.append_assoc]
rw [Pos.set_eq_append, String.append_assoc]
apply append_right
rw [isValid_copy_iff, isValidForSlice_stringSliceTo]
exact hpq, hq
@@ -62,40 +62,40 @@ theorem Pos.Raw.IsValid.set_of_le {s : String} {p : s.ValidPos} {c : Char} {hp :
/-- Given a valid position in a string, obtain the corresponding position after setting a character on
that string, provided that the position was before the changed position. -/
@[inline]
def ValidPos.toSetOfLE {s : String} (q p : s.ValidPos) (c : Char) (hp : p s.endValidPos)
(hpq : q p) : (p.set c hp).ValidPos where
def Pos.toSetOfLE {s : String} (q p : s.Pos) (c : Char) (hp : p s.endPos)
(hpq : q p) : (p.set c hp).Pos where
offset := q.offset
isValid := q.isValid.set_of_le hpq
@[simp]
theorem ValidPos.offset_toSetOfLE {s : String} {q p : s.ValidPos} {c : Char} {hp : p s.endValidPos}
theorem Pos.offset_toSetOfLE {s : String} {q p : s.Pos} {c : Char} {hp : p s.endPos}
{hpq : q p} : (q.toSetOfLE p c hp hpq).offset = q.offset := (rfl)
theorem Pos.Raw.isValid_add_char_set {s : String} {p : s.ValidPos} {c : Char} {hp} :
theorem Pos.Raw.isValid_add_char_set {s : String} {p : s.Pos} {c : Char} {hp} :
(p.offset + c).IsValid (p.set c hp) :=
ValidPos.set_eq_append IsValid.append_right (isValid_of_eq_rawEndPos (by simp)) _
Pos.set_eq_append IsValid.append_right (isValid_of_eq_rawEndPos (by simp)) _
/-- The position just after the position that changed in a `ValidPos.set` call. -/
/-- The position just after the position that changed in a `Pos.set` call. -/
@[inline]
def ValidPos.pastSet {s : String} (p : s.ValidPos) (c : Char) (hp) : (p.set c hp).ValidPos where
def Pos.pastSet {s : String} (p : s.Pos) (c : Char) (hp) : (p.set c hp).Pos where
offset := p.offset + c
isValid := Pos.Raw.isValid_add_char_set
@[simp]
theorem ValidPos.offset_pastSet {s : String} {p : s.ValidPos} {c : Char} {hp} :
theorem Pos.offset_pastSet {s : String} {p : s.Pos} {c : Char} {hp} :
(p.pastSet c hp).offset = p.offset + c := (rfl)
@[inline]
def ValidPos.appendRight {s : String} (p : s.ValidPos) (t : String) : (s ++ t).ValidPos where
def Pos.appendRight {s : String} (p : s.Pos) (t : String) : (s ++ t).Pos where
offset := p.offset
isValid := p.isValid.append_right t
theorem ValidPos.splits_pastSet {s : String} {p : s.ValidPos} {c : Char} {hp} :
theorem Pos.splits_pastSet {s : String} {p : s.Pos} {c : Char} {hp} :
(p.pastSet c hp).Splits ((s.sliceTo p).copy ++ singleton c) (s.sliceFrom (p.next hp)).copy where
eq_append := set_eq_append
offset_eq_rawEndPos := by simp
theorem remainingBytes_pastSet {s : String} {p : s.ValidPos} {c : Char} {hp} :
theorem remainingBytes_pastSet {s : String} {p : s.Pos} {c : Char} {hp} :
(p.pastSet c hp).remainingBytes = (p.next hp).remainingBytes := by
rw [(p.next hp).splits.remainingBytes_eq, p.splits_pastSet.remainingBytes_eq]
@@ -110,34 +110,34 @@ Examples:
* `("abc".pos ⟨1⟩ (by decide)).modify Char.toUpper (by decide) = "aBc"`
-/
@[inline]
def ValidPos.modify {s : String} (p : s.ValidPos) (f : Char Char) (hp : p s.endValidPos) :
def Pos.modify {s : String} (p : s.Pos) (f : Char Char) (hp : p s.endPos) :
String :=
p.set (f <| p.get hp) hp
theorem Pos.Raw.IsValid.modify_of_le {s : String} {p : s.ValidPos} {f : Char Char}
{hp : p s.endValidPos} {q : Pos.Raw} (hq : q.IsValid s) (hpq : q p.offset) :
theorem Pos.Raw.IsValid.modify_of_le {s : String} {p : s.Pos} {f : Char Char}
{hp : p s.endPos} {q : Pos.Raw} (hq : q.IsValid s) (hpq : q p.offset) :
q.IsValid (p.modify f hp) :=
set_of_le hq hpq
/-- Given a valid position in a string, obtain the corresponding position after modifying a character
in that string, provided that the position was before the changed position. -/
@[inline]
def ValidPos.toModifyOfLE {s : String} (q p : s.ValidPos) (f : Char Char)
(hp : p s.endValidPos) (hpq : q p) : (p.modify f hp).ValidPos where
def Pos.toModifyOfLE {s : String} (q p : s.Pos) (f : Char Char)
(hp : p s.endPos) (hpq : q p) : (p.modify f hp).Pos where
offset := q.offset
isValid := q.isValid.modify_of_le hpq
@[simp]
theorem ValidPos.offset_toModifyOfLE {s : String} {q p : s.ValidPos} {f : Char Char}
{hp : p s.endValidPos} {hpq : q p} : (q.toModifyOfLE p f hp hpq).offset = q.offset := (rfl)
theorem Pos.offset_toModifyOfLE {s : String} {q p : s.Pos} {f : Char Char}
{hp : p s.endPos} {hpq : q p} : (q.toModifyOfLE p f hp hpq).offset = q.offset := (rfl)
/-- The position just after the position that was modified in a `ValidPos.modify` call. -/
/-- The position just after the position that was modified in a `Pos.modify` call. -/
@[inline]
def ValidPos.pastModify {s : String} (p : s.ValidPos) (f : Char Char)
(hp : p s.endValidPos) : (p.modify f hp).ValidPos :=
def Pos.pastModify {s : String} (p : s.Pos) (f : Char Char)
(hp : p s.endPos) : (p.modify f hp).Pos :=
p.pastSet _ _
theorem remainingBytes_pastModify {s : String} {p : s.ValidPos} {f : Char Char} {hp} :
theorem remainingBytes_pastModify {s : String} {p : s.Pos} {f : Char Char} {hp} :
(p.pastModify f hp).remainingBytes = (p.next hp).remainingBytes :=
remainingBytes_pastSet
@@ -148,8 +148,8 @@ invalid, the string is returned unchanged.
If both the replacement character and the replaced character are 7-bit ASCII characters and the
string is not shared, then it is updated in-place and not copied.
This is a legacy function. The recommended alternative is `String.ValidPos.set`, combined with
`String.pos` or another means of obtaining a `String.ValidPos`.
This is a legacy function. The recommended alternative is `String.Pos.set`, combined with
`String.pos` or another means of obtaining a `String.Pos`.
Examples:
* `"abc".set ⟨1⟩ 'B' = "aBc"`
@@ -173,8 +173,8 @@ character. If `p` is an invalid position, the string is returned unchanged.
If both the replacement character and the replaced character are 7-bit ASCII characters and the
string is not shared, then it is updated in-place and not copied.
This is a legacy function. The recommended alternative is `String.ValidPos.set`, combined with
`String.pos` or another means of obtaining a `String.ValidPos`.
This is a legacy function. The recommended alternative is `String.Pos.set`, combined with
`String.pos` or another means of obtaining a `String.Pos`.
Examples:
* `"abc".modify ⟨1⟩ Char.toUpper = "aBc"`
@@ -188,14 +188,14 @@ def Pos.Raw.modify (s : String) (i : Pos.Raw) (f : Char → Char) : String :=
def modify (s : String) (i : Pos.Raw) (f : Char Char) : String :=
i.set s (f (i.get s))
@[specialize] def mapAux (f : Char Char) (s : String) (p : s.ValidPos) : String :=
if h : p = s.endValidPos then
@[specialize] def mapAux (f : Char Char) (s : String) (p : s.Pos) : String :=
if h : p = s.endPos then
s
else
mapAux f (p.modify f h) (p.pastModify f h)
termination_by p.remainingBytes
decreasing_by
simp [remainingBytes_pastModify, ValidPos.lt_iff_remainingBytes_lt]
simp [remainingBytes_pastModify, Pos.lt_iff_remainingBytes_lt]
/--
Applies the function `f` to every character in a string, returning a string that contains the
@@ -206,7 +206,7 @@ Examples:
* `"".map Char.toUpper = ""`
-/
@[inline] def map (f : Char Char) (s : String) : String :=
mapAux f s s.startValidPos
mapAux f s s.startPos
/--
Replaces each character in `s` with the result of applying `Char.toUpper` to it.

View File

@@ -112,12 +112,12 @@ def memcmpSlice (lhs rhs : Slice) (lstart : String.Pos.Raw) (rstart : String.Pos
(by
have := lhs.startInclusive_le_endExclusive
have := lhs.endExclusive.isValid.le_utf8ByteSize
simp [ValidPos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
simp [String.Pos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
omega)
(by
have := rhs.startInclusive_le_endExclusive
have := rhs.endExclusive.isValid.le_utf8ByteSize
simp [ValidPos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
simp [String.Pos.le_iff, Pos.Raw.le_iff, Slice.utf8ByteSize_eq] at *
omega)
end Internal

View File

@@ -108,7 +108,7 @@ At runtime, this function is implemented by efficient, constant-time code.
-/
@[extern "lean_string_get_byte_fast", expose]
def getUTF8Byte (s : @& String) (p : Pos.Raw) (h : p < s.rawEndPos) : UInt8 :=
s.bytes[p.byteIdx]
s.toByteArray[p.byteIdx]
@[deprecated getUTF8Byte (since := "2025-10-01"), extern "lean_string_get_byte_fast", expose]
abbrev getUtf8Byte (s : String) (p : Pos.Raw) (h : p < s.rawEndPos) : UInt8 :=
@@ -216,7 +216,7 @@ theorem Pos.Raw.increaseBy_charUtf8Size {p : Pos.Raw} {c : Char} :
p.increaseBy c.utf8Size = p + c := by
simp [Pos.Raw.ext_iff]
/-- Increases the byte offset of the position by `1`. Not to be confused with `ValidPos.next`. -/
/-- Increases the byte offset of the position by `1`. Not to be confused with `Pos.next`. -/
@[inline, expose]
def Pos.Raw.inc (p : Pos.Raw) : Pos.Raw :=
p.byteIdx + 1
@@ -224,7 +224,7 @@ def Pos.Raw.inc (p : Pos.Raw) : Pos.Raw :=
@[simp]
theorem Pos.Raw.byteIdx_inc {p : Pos.Raw} : p.inc.byteIdx = p.byteIdx + 1 := (rfl)
/-- Decreases the byte offset of the position by `1`. Not to be confused with `ValidPos.prev`. -/
/-- Decreases the byte offset of the position by `1`. Not to be confused with `Pos.prev`. -/
@[inline, expose]
def Pos.Raw.dec (p : Pos.Raw) : Pos.Raw :=
p.byteIdx - 1

View File

@@ -1,88 +0,0 @@
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Mario Carneiro
-/
module
prelude
public import Init.Data.String.Substring
public section
/--
Interprets a string as the decimal representation of an integer, returning it. Returns `none` if
the string does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally `-` in front. Leading `+` characters are not allowed.
Use `String.isInt` to check whether `String.toInt?` would return `some`. `String.toInt!` is an
alternative that panics instead of returning `none` when the string is not an integer.
Examples:
* `"".toInt? = none`
* `"-".toInt? = none`
* `"0".toInt? = some 0`
* `"5".toInt? = some 5`
* `"-5".toInt? = some (-5)`
* `"587".toInt? = some 587`
* `"-587".toInt? = some (-587)`
* `" 5".toInt? = none`
* `"2-3".toInt? = none`
* `"0xff".toInt? = none`
-/
def String.toInt? (s : String) : Option Int := do
if s.front = '-' then do
let v (s.toRawSubstring.drop 1).toNat?;
pure <| - Int.ofNat v
else
Int.ofNat <$> s.toNat?
/--
Checks whether the string can be interpreted as the decimal representation of an integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally `-` in front. Leading `+` characters are not allowed.
Use `String.toInt?` or `String.toInt!` to convert such a string to an integer.
Examples:
* `"".isInt = false`
* `"-".isInt = false`
* `"0".isInt = true`
* `"-0".isInt = true`
* `"5".isInt = true`
* `"587".isInt = true`
* `"-587".isInt = true`
* `"+587".isInt = false`
* `" 5".isInt = false`
* `"2-3".isInt = false`
* `"0xff".isInt = false`
-/
def String.isInt (s : String) : Bool :=
if s.front = '-' then
(s.toRawSubstring.drop 1).isNat
else
s.isNat
/--
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally `-` in front. Leading `+` characters are not allowed.
Use `String.isInt` to check whether `String.toInt!` would return a value. `String.toInt?` is a safer
alternative that returns `none` instead of panicking when the string is not an integer.
Examples:
* `"0".toInt! = 0`
* `"5".toInt! = 5`
* `"587".toInt! = 587`
* `"-587".toInt! = -587`
-/
def String.toInt! (s : String) : Int :=
match s.toInt? with
| some v => v
| none => panic "Int expected"

View File

@@ -66,6 +66,21 @@ def Slice.Pos.find? {s : Slice} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher
Option s.Pos :=
((s.sliceFrom pos).find? pattern).map ofSliceFrom
/--
Finds the position of the first match of the pattern {name}`pattern` in after the position
{name}`pos`. If there is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".toSlice.startPos.find Char.isWhitespace).get! == ' '`
* {lean}`("tea".toSlice.pos ⟨1⟩ (by decide)).find (fun (c : Char) => c == 't') == "tea".toSlice.endPos`
-/
@[inline]
def Slice.Pos.find {s : Slice} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
s.Pos :=
ofSliceFrom ((s.sliceFrom pos).find pattern)
/--
Finds the position of the first match of the pattern {name}`pattern` in after the position
{name}`pos`. If there is no match {name}`none` is returned.
@@ -73,14 +88,29 @@ Finds the position of the first match of the pattern {name}`pattern` in after th
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".startValidPos.find? Char.isWhitespace).map (·.get!) == some ' '`
* {lean}`("coffee tea water".startPos.find? Char.isWhitespace).map (·.get!) == some ' '`
* {lean}`("tea".pos ⟨1⟩ (by decide)).find? (fun (c : Char) => c == 't') == none`
-/
@[inline]
def ValidPos.find? {s : String} (pos : s.ValidPos) (pattern : ρ)
[ToForwardSearcher pattern σ] : Option s.ValidPos :=
def Pos.find? {s : String} (pos : s.Pos) (pattern : ρ)
[ToForwardSearcher pattern σ] : Option s.Pos :=
(pos.toSlice.find? pattern).map (·.ofSlice)
/--
Finds the position of the first match of the pattern {name}`pattern` in after the position
{name}`pos`. If there is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".startPos.find Char.isWhitespace).get! == ' '`
* {lean}`("tea".pos ⟨1⟩ (by decide)).find (fun (c : Char) => c == 't') == "tea".endPos`
-/
@[inline]
def Pos.find {s : String} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
s.Pos :=
(pos.toSlice.find pattern).ofSlice
/--
Finds the position of the first match of the pattern {name}`pattern` in a string {name}`s`. If
there is no match {name}`none` is returned.
@@ -93,8 +123,120 @@ Examples:
* {lean}`("coffee tea water".find? "tea").map (·.get!) == some 't'`
-/
@[inline]
def find? (s : String) (pattern : ρ) [ToForwardSearcher pattern σ] : Option s.ValidPos :=
s.startValidPos.find? pattern
def find? (s : String) (pattern : ρ) [ToForwardSearcher pattern σ] : Option s.Pos :=
s.startPos.find? pattern
/--
Finds the position of the first match of the pattern {name}`pattern` in a slice {name}`s`. If there
is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".find Char.isWhitespace).get! == ' '`
* {lean}`"tea".find (fun (c : Char) => c == 'X') == "tea".endPos`
* {lean}`("coffee tea water".find "tea").get! == 't'`
-/
@[inline]
def find (s : String) (pattern : ρ) [ToForwardSearcher pattern σ] : s.Pos :=
s.startPos.find pattern
/--
Finds the position of the first match of the pattern {name}`pattern` in a slice {name}`s` that is
strictly before {name}`pos`. If there is no such match {lean}`none` is returned.
This function is generic over all currently supported patterns except
{name}`String`/{name}`String.Slice`.
Examples:
* {lean}`(("abc".toSlice.endPos.prev (by decide)).revFind? Char.isAlpha).map (·.get!) == some 'b'`
* {lean}`"abc".toSlice.startPos.revFind? Char.isAlpha == none`
-/
@[inline]
def Slice.Pos.revFind? {s : Slice} (pos : s.Pos) (pattern : ρ) [ToBackwardSearcher pattern σ] :
Option s.Pos :=
((s.sliceTo pos).revFind? pattern).map ofSliceTo
/--
Finds the position of the first match of the pattern {name}`pattern` in a slice {name}`s` that is
strictly before {name}`pos`. If there is no such match {lean}`none` is returned.
This function is generic over all currently supported patterns except
{name}`String`/{name}`String.Slice`.
Examples:
* {lean}`(("ab1c".endPos.prev (by decide)).revFind? Char.isAlpha).map (·.get!) == some 'b'`
* {lean}`"abc".startPos.revFind? Char.isAlpha == none`
-/
@[inline]
def Pos.revFind? {s : String} (pos : s.Pos) (pattern : ρ) [ToBackwardSearcher pattern σ] :
Option s.Pos :=
(pos.toSlice.revFind? pattern).map (·.ofSlice)
/--
Finds the position of the first match of the pattern {name}`pattern` in a string, starting
from the end of the slice and traversing towards the start. If there is no match {name}`none` is
returned.
This function is generic over all currently supported patterns except
{name}`String`/{name}`String.Slice`.
Examples:
* {lean}`("coffee tea water".toSlice.revFind? Char.isWhitespace).map (·.get!) == some ' '`
* {lean}`"tea".toSlice.revFind? (fun (c : Char) => c == 'X') == none`
-/
@[inline]
def revFind? (s : String) (pattern : ρ) [ToBackwardSearcher pattern σ] : Option s.Pos :=
s.endPos.revFind? pattern
@[export lean_string_posof]
def Internal.posOfImpl (s : String) (c : Char) : Pos.Raw :=
(s.find c).offset
@[deprecated String.Pos.find (since := "2025-11-19")]
def findAux (s : String) (p : Char Bool) (stopPos : Pos.Raw) (pos : Pos.Raw) : Pos.Raw :=
if h : pos stopPos pos.IsValid s stopPos.IsValid s then
(String.Slice.mk s (s.pos pos h.2.1) (s.pos stopPos h.2.2)
(by simp [Pos.le_iff, h.1])).find p |>.str.offset
else stopPos
@[deprecated String.Pos.find (since := "2025-11-19")]
def posOfAux (s : String) (c : Char) (stopPos : Pos.Raw) (pos : Pos.Raw) : Pos.Raw :=
if h : pos stopPos pos.IsValid s stopPos.IsValid s then
(String.Slice.mk s (s.pos pos h.2.1) (s.pos stopPos h.2.2)
(by simp [Pos.le_iff, h.1])).find c |>.str.offset
else stopPos
@[deprecated String.find (since := "2025-11-19")]
def posOf (s : String) (c : Char) : Pos.Raw :=
(s.find c).offset
@[deprecated String.Pos.revFind? (since := "2025-11-19")]
def revPosOfAux (s : String) (c : Char) (pos : Pos.Raw) : Option Pos.Raw :=
s.pos? pos |>.bind (·.revFind? c) |>.map (·.offset)
@[deprecated String.revFind? (since := "2025-11-19")]
def revPosOf (s : String) (c : Char) : Option Pos.Raw :=
s.revFind? c |>.map (·.offset)
@[deprecated String.Pos.revFind? (since := "2025-11-19")]
def revFindAux (s : String) (p : Char Bool) (pos : Pos.Raw) : Option Pos.Raw :=
s.pos? pos |>.bind (·.revFind? p) |>.map (·.offset)
@[deprecated String.revFind? (since := "2025-11-19")]
def revFind (s : String) (p : Char Bool) : Option Pos.Raw :=
s.revFind? p |>.map (·.offset)
/--
Returns the position of the beginning of the line that contains the position {name}`pos`.
Lines are ended by {lean}`'\n'`, and the returned position is either {lean}`0 : String.Pos.Raw` or
immediately after a {lean}`'\n'` character.
-/
@[deprecated String.Pos.revFind? (since := "2025-11-19")]
def findLineStart (s : String) (pos : String.Pos.Raw) : String.Pos.Raw :=
s.pos? pos |>.bind (·.revFind? '\n') |>.map (·.offset) |>.getD s.startPos.offset
/--
Splits a string at each subslice that matches the pattern {name}`pat`.
@@ -112,9 +254,385 @@ Examples:
* {lean}`("baaab".split "aa").toList == ["b".toSlice, "ab".toSlice]`
-/
@[inline]
def split (s : String) (pat : ρ) [ToForwardSearcher pat σ] :=
def split (s : String) (pat : ρ) [ToForwardSearcher pat σ] :=
(s.toSlice.split pat : Std.Iter String.Slice)
/--
Splits a string at each subslice that matches the pattern {name}`pat`. Unlike {name}`split` the
matched subslices are included at the end of each subslice.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".splitInclusive Char.isWhitespace).toList == ["coffee ".toSlice, "tea ".toSlice, "water".toSlice]`
* {lean}`("coffee tea water".splitInclusive ' ').toList == ["coffee ".toSlice, "tea ".toSlice, "water".toSlice]`
* {lean}`("coffee tea water".splitInclusive " tea ").toList == ["coffee tea ".toSlice, "water".toSlice]`
* {lean}`("baaab".splitInclusive "aa").toList == ["baa".toSlice, "ab".toSlice]`
-/
@[inline]
def splitInclusive (s : String) (pat : ρ) [ToForwardSearcher pat σ] :=
(s.toSlice.splitInclusive pat : Std.Iter String.Slice)
@[deprecated String.Slice.foldl (since := "2025-11-20")]
def foldlAux {α : Type u} (f : α Char α) (s : String) (stopPos : Pos.Raw) (i : Pos.Raw) (a : α) : α :=
s.slice! (s.pos! i) (s.pos! stopPos) |>.foldl f a
/--
Folds a function over a string from the start, accumulating a value starting with {name}`init`. The
accumulated value is combined with each character in order, using {name}`f`.
Examples:
* {lean}`"coffee tea water".foldl (fun n c => if c.isWhitespace then n + 1 else n) 0 = 2`
* {lean}`"coffee tea and water".foldl (fun n c => if c.isWhitespace then n + 1 else n) 0 = 3`
* {lean}`"coffee tea water".foldl (·.push ·) "" = "coffee tea water"`
-/
@[inline] def foldl {α : Type u} (f : α Char α) (init : α) (s : String) : α :=
s.toSlice.foldl f init
@[export lean_string_foldl]
def Internal.foldlImpl (f : String Char String) (init : String) (s : String) : String :=
String.foldl f init s
@[deprecated String.Slice.foldr (since := "2025-11-25")]
def foldrAux {α : Type u} (f : Char α α) (a : α) (s : String) (i begPos : Pos.Raw) : α :=
s.slice! (s.pos! begPos) (s.pos! i) |>.foldr f a
/--
Folds a function over a string from the right, accumulating a value starting with {lean}`init`. The
accumulated value is combined with each character in reverse order, using {lean}`f`.
Examples:
* {lean}`"coffee tea water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 2`
* {lean}`"coffee tea and water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 3`
* {lean}`"coffee tea water".foldr (fun c s => s.push c) "" = "retaw aet eeffoc"`
-/
@[inline] def foldr {α : Type u} (f : Char α α) (init : α) (s : String) : α :=
s.toSlice.foldr f init
@[deprecated String.Slice.any (since := "2025-11-25")]
def anyAux (s : String) (stopPos : Pos.Raw) (p : Char Bool) (i : Pos.Raw) : Bool :=
s.slice! (s.pos! i) (s.pos! stopPos) |>.any p
/--
Checks whether a string has a match of the pattern {name}`pat` anywhere.
This function is generic over all currently supported patterns.
Examples:
* {lean}`"coffee tea water".contains Char.isWhitespace = true`
* {lean}`"tea".contains (fun (c : Char) => c == 'X') = false`
* {lean}`"coffee tea water".contains "tea" = true`
-/
@[inline] def contains (s : String) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
s.toSlice.contains pat
@[export lean_string_contains]
def Internal.containsImpl (s : String) (c : Char) : Bool :=
String.contains s c
@[inline, inherit_doc contains] def any (s : String) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
s.contains pat
@[export lean_string_any]
def Internal.anyImpl (s : String) (p : Char Bool) :=
String.any s p
/--
Checks whether a slice only consists of matches of the pattern {name}`pat`.
Short-circuits at the first pattern mis-match.
This function is generic over all currently supported patterns.
Examples:
* {lean}`"brown".all Char.isLower = true`
* {lean}`"brown and orange".all Char.isLower = false`
* {lean}`"aaaaaa".all 'a' = true`
* {lean}`"aaaaaa".all "aa" = true`
* {lean}`"aaaaaaa".all "aa" = false`
-/
@[inline] def all (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
s.toSlice.all pat
/--
Checks whether the string can be interpreted as the decimal representation of a natural number.
A slice can be interpreted as a decimal natural number if it is not empty and all the characters in
it are digits.
Use {name (scope := "Init.Data.String.Search")}`toNat?` or
{name (scope := "Init.Data.String.Search")}`toNat!` to convert such a slice to a natural number.
Examples:
* {lean}`"".isNat = false`
* {lean}`"0".isNat = true`
* {lean}`"5".isNat = true`
* {lean}`"05".isNat = true`
* {lean}`"587".isNat = true`
* {lean}`"-587".isNat = false`
* {lean}`" 5".isNat = false`
* {lean}`"2+3".isNat = false`
* {lean}`"0xff".isNat = false`
-/
@[inline] def isNat (s : String) : Bool :=
s.toSlice.isNat
/--
Interprets a string as the decimal representation of a natural number, returning it. Returns
{name}`none` if the slice does not contain a decimal natural number.
A slice can be interpreted as a decimal natural number if it is not empty and all the characters in
it are digits.
Use {name}`isNat` to check whether {name}`toNat?` would return {name}`some`.
{name (scope := "Init.Data.String.Search")}`toNat!` is an alternative that panics instead of
returning {name}`none` when the slice is not a natural number.
Examples:
* {lean}`"".toNat? = none`
* {lean}`"0".toNat? = some 0`
* {lean}`"5".toNat? = some 5`
* {lean}`"587".toNat? = some 587`
* {lean}`"-587".toNat? = none`
* {lean}`" 5".toNat? = none`
* {lean}`"2+3".toNat? = none`
* {lean}`"0xff".toNat? = none`
-/
@[inline] def toNat? (s : String) : Option Nat :=
s.toSlice.toNat?
/--
Interprets a string as the decimal representation of a natural number, returning it. Panics if the
slice does not contain a decimal natural number.
A slice can be interpreted as a decimal natural number if it is not empty and all the characters in
it are digits.
Use {name}`isNat` to check whether {name}`toNat!` would return a value. {name}`toNat?` is a safer
alternative that returns {name}`none` instead of panicking when the string is not a natural number.
Examples:
* {lean}`"0".toNat! = 0`
* {lean}`"5".toNat! = 5`
* {lean}`"587".toNat! = 587`
-/
@[inline] def toNat! (s : String) : Nat :=
s.toSlice.toNat!
/--
Interprets a string as the decimal representation of an integer, returning it. Returns {lean}`none`
if the string does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name (scope := "Init.Data.String.Search")}`String.isInt` to check whether {name}`String.toInt?`
would return {lean}`some`. {name (scope := "Init.Data.String.Search")}`String.toInt!` is an
alternative that panics instead of returning {lean}`none` when the string is not an integer.
Examples:
* {lean}`"".toInt? = none`
* {lean}`"-".toInt? = none`
* {lean}`"0".toInt? = some 0`
* {lean}`"5".toInt? = some 5`
* {lean}`"-5".toInt? = some (-5)`
* {lean}`"587".toInt? = some 587`
* {lean}`"-587".toInt? = some (-587)`
* {lean}`" 5".toInt? = none`
* {lean}`"2-3".toInt? = none`
* {lean}`"0xff".toInt? = none`
-/
@[inline] def toInt? (s : String) : Option Int :=
s.toSlice.toInt?
/--
Checks whether the string can be interpreted as the decimal representation of an integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name}`String.toInt?` or {name (scope := "Init.Data.String.Search")}`String.toInt!` to convert
such a string to an integer.
Examples:
* {lean}`"".isInt = false`
* {lean}`"-".isInt = false`
* {lean}`"0".isInt = true`
* {lean}`"-0".isInt = true`
* {lean}`"5".isInt = true`
* {lean}`"587".isInt = true`
* {lean}`"-587".isInt = true`
* {lean}`"+587".isInt = false`
* {lean}`" 5".isInt = false`
* {lean}`"2-3".isInt = false`
* {lean}`"0xff".isInt = false`
-/
@[inline] def isInt (s : String) : Bool :=
s.toSlice.isInt
/--
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name}`String.isInt` to check whether {name}`String.toInt!` would return a value.
{name}`String.toInt?` is a safer alternative that returns {lean}`none` instead of panicking when the
string is not an integer.
Examples:
* {lean}`"0".toInt! = 0`
* {lean}`"5".toInt! = 5`
* {lean}`"587".toInt! = 587`
* {lean}`"-587".toInt! = -587`
-/
@[inline] def toInt! (s : String) : Int :=
s.toSlice.toInt!
/--
Returns the first character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
Examples:
* {lean}`"abc".front? = some 'a'`
* {lean}`"".front? = none`
-/
@[inline]
def front? (s : String) : Option Char :=
s.toSlice.front?
/--
Returns the first character in {name}`s`. If {lean}`s = ""`, returns {lean}`(default : Char)`.
Examples:
* {lean}`"abc".front = 'a'`
* {lean}`"".front = (default : Char)`
-/
@[inline, expose] def front (s : String) : Char :=
s.toSlice.front
@[export lean_string_front]
def Internal.frontImpl (s : String) : Char :=
String.front s
/--
Returns the last character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
Examples:
* {lean}`"abc".back? = some 'c'`
* {lean}`"".back? = none`
-/
@[inline]
def back? (s : String) : Option Char :=
s.toSlice.back?
/--
Returns the last character in {name}`s`. If {lean}`s = ""`, returns {lean}`(default : Char)`.
Examples:
* {lean}`"abc".back = 'c'`
* {lean}`"".back = (default : Char)`
-/
@[inline, expose] def back (s : String) : Char :=
s.toSlice.back
theorem Slice.Pos.ofSlice_ne_endPos {s : String} {p : s.toSlice.Pos}
(h : p s.toSlice.endPos) : p.ofSlice s.endPos := by
rwa [ne_eq, Pos.toSlice_inj, toSlice_ofSlice, endPos_toSlice]
@[inline]
def Internal.toSliceWithProof {s : String} :
{ p : s.toSlice.Pos // p s.toSlice.endPos } { p : s.Pos // p s.endPos } :=
fun p, h => p.ofSlice, Slice.Pos.ofSlice_ne_endPos h
/--
Creates an iterator over all valid positions within {name}`s`.
Examples
* {lean}`("abc".positions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['a', 'b', 'c']`
* {lean}`("abc".positions.map (·.val.offset.byteIdx) |>.toList) = [0, 1, 2]`
* {lean}`("ab∀c".positions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['a', 'b', '∀', 'c']`
* {lean}`("ab∀c".positions.map (·.val.offset.byteIdx) |>.toList) = [0, 1, 2, 5]`
-/
@[inline]
def positions (s : String) :=
(s.toSlice.positions.map Internal.toSliceWithProof : Std.Iter { p : s.Pos // p s.endPos })
/--
Creates an iterator over all characters (Unicode code points) in {name}`s`.
Examples:
* {lean}`"abc".chars.toList = ['a', 'b', 'c']`
* {lean}`"ab∀c".chars.toList = ['a', 'b', '∀', 'c']`
-/
@[inline]
def chars (s : String) :=
(s.toSlice.chars : Std.Iter Char)
/--
Creates an iterator over all valid positions within {name}`s`, starting from the last valid
position and iterating towards the first one.
Examples
* {lean}`("abc".revPositions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['c', 'b', 'a']`
* {lean}`("abc".revPositions.map (·.val.offset.byteIdx) |>.toList) = [2, 1, 0]`
* {lean}`("ab∀c".revPositions.map (fun ⟨p, h⟩ => p.get h) |>.toList) = ['c', '∀', 'b', 'a']`
* {lean}`("ab∀c".toSlice.revPositions.map (·.val.offset.byteIdx) |>.toList) = [5, 2, 1, 0]`
-/
@[inline]
def revPositions (s : String) :=
(s.toSlice.revPositions.map Internal.toSliceWithProof : Std.Iter { p : s.Pos // p s.endPos })
/--
Creates an iterator over all characters (Unicode code points) in {name}`s`, starting from the end
of the slice and iterating towards the start.
Example:
* {lean}`"abc".revChars.toList = ['c', 'b', 'a']`
* {lean}`"ab∀c".revChars.toList = ['c', '∀', 'b', 'a']`
-/
@[inline]
def revChars (s : String) :=
(s.toSlice.revChars : Std.Iter Char)
/--
Creates an iterator over all bytes in {name}`s`.
Examples:
* {lean}`"abc".byteIterator.toList = [97, 98, 99]`
* {lean}`"ab∀c".byteIterator.toList = [97, 98, 226, 136, 128, 99]`
-/
@[inline]
def byteIterator (s : String) :=
(s.toSlice.bytes : Std.Iter UInt8)
/--
Creates an iterator over all bytes in {name}`s`, starting from the last one and iterating towards
the first one.
Examples:
* {lean}`"abc".revBytes.toList = [99, 98, 97]`
* {lean}`"ab∀c".revBytes.toList = [99, 128, 136, 226, 98, 97]`
-/
@[inline]
def revBytes (s : String) :=
(s.toSlice.revBytes : Std.Iter UInt8)
/--
Creates an iterator over all lines in {name}`s` with the line ending characters `\r\n` or `\n` being
stripped.
Examples:
* {lean}`"foo\r\nbar\n\nbaz\n".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz".toSlice]`
* {lean}`"foo\r\nbar\n\nbaz".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz".toSlice]`
* {lean}`"foo\r\nbar\n\nbaz\r".lines.toList == ["foo".toSlice, "bar".toSlice, "".toSlice, "baz\r".toSlice]`
-/
def lines (s : String) :=
s.toSlice.lines
end
end String

View File

@@ -489,11 +489,26 @@ Examples:
* {lean}`"tea".toSlice.find? (fun (c : Char) => c == 'X') == none`
* {lean}`("coffee tea water".toSlice.find? "tea").map (·.get!) == some 't'`
-/
@[specialize pat]
@[inline]
def find? (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : Option s.Pos :=
let searcher := ToForwardSearcher.toSearcher pat s
searcher.findSome? (fun | .matched startPos _ => some startPos | .rejected .. => none)
/--
Finds the position of the first match of the pattern {name}`pat` in a slice {name}`s`. If there
is no match {lean}`s.endPos` is returned.
This function is generic over all currently supported patterns.
Examples:
* {lean}`("coffee tea water".toSlice.find Char.isWhitespace).get! == ' '`
* {lean}`"tea".toSlice.find (fun (c : Char) => c == 'X') == "tea".toSlice.endPos`
* {lean}`("coffee tea water".toSlice.find "tea").get! == 't'`
-/
@[inline]
def find (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : s.Pos :=
s.find? pat |>.getD s.endPos
/--
Checks whether a slice has a match of the pattern {name}`pat` anywhere.
@@ -514,7 +529,7 @@ def any (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
s.contains pat
/--
Checks whether a slice only consists of matches of the pattern {name}`pat` anywhere.
Checks whether a slice only consists of matches of the pattern {name}`pat`.
Short-circuits at the first pattern mis-match.
@@ -791,7 +806,7 @@ where
termination_by curr.down
/--
Finds the position of the first match of the pattern {name}`pat` in a slice {name}`true`, starting
Finds the position of the first match of the pattern {name}`pat` in a slice, starting
from the end of the slice and traversing towards the start. If there is no match {name}`none` is
returned.
@@ -1303,13 +1318,13 @@ def toNat! (s : Slice) : Nat :=
panic! "Nat expected"
/--
Returns the first character in {name}`s`. If {name}`s` is empty, {name}`none`.
Returns the first character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
Examples:
* {lean}`"abc".toSlice.front? = some 'a'`
* {lean}`"".toSlice.front? = none`
-/
@[inline]
@[inline, expose]
def front? (s : Slice) : Option Char :=
s.startPos.get?
@@ -1320,10 +1335,89 @@ Examples:
* {lean}`"abc".toSlice.front = 'a'`
* {lean}`"".toSlice.front = (default : Char)`
-/
@[inline]
@[inline, expose]
def front (s : Slice) : Char :=
s.front?.getD default
/--
Checks whether the slice can be interpreted as the decimal representation of an integer.
A slice can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading {lit}`+` characters are not allowed.
Use {name (scope := "Init.Data.String.Slice")}`String.Slice.toInt?` or {name (scope := "Init.Data.String.Slice")}`String.toInt!` to convert such a string to an integer.
Examples:
* {lean}`"".toSlice.isInt = false`
* {lean}`"-".toSlice.isInt = false`
* {lean}`"0".toSlice.isInt = true`
* {lean}`"-0".toSlice.isInt = true`
* {lean}`"5".toSlice.isInt = true`
* {lean}`"587".toSlice.isInt = true`
* {lean}`"-587".toSlice.isInt = true`
* {lean}`"+587".toSlice.isInt = false`
* {lean}`" 5".toSlice.isInt = false`
* {lean}`"2-3".toSlice.isInt = false`
* {lean}`"0xff".toSlice.isInt = false`
-/
def isInt (s : Slice) : Bool :=
if s.front = '-' then
(s.drop 1).isNat
else
s.isNat
/--
Interprets a slice as the decimal representation of an integer, returning it. Returns {lean}`none` if
the string does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading {lit}`+` characters are not allowed.
Use {name}`Slice.isInt` to check whether {name}`Slice.toInt?` would return {lean}`some`.
{name (scope := "Init.Data.String.Slice")}`Slice.toInt!` is an alternative that panics instead of
returning {lean}`none` when the string is not an integer.
Examples:
* {lean}`"".toSlice.toInt? = none`
* {lean}`"-".toSlice.toInt? = none`
* {lean}`"0".toSlice.toInt? = some 0`
* {lean}`"5".toSlice.toInt? = some 5`
* {lean}`"-5".toSlice.toInt? = some (-5)`
* {lean}`"587".toSlice.toInt? = some 587`
* {lean}`"-587".toSlice.toInt? = some (-587)`
* {lean}`" 5".toSlice.toInt? = none`
* {lean}`"2-3".toSlice.toInt? = none`
* {lean}`"0xff".toSlice.toInt? = none`
-/
def toInt? (s : Slice) : Option Int :=
if s.front = '-' then
Int.negOfNat <$> (s.drop 1).toNat?
else
Int.ofNat <$> s.toNat?
/--
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
does not contain a decimal integer.
A string can be interpreted as a decimal integer if it only consists of at least one decimal digit
and optionally {lit}`-` in front. Leading `+` characters are not allowed.
Use {name}`Slice.isInt` to check whether {name}`Slice.toInt!` would return a value.
{name}`Slice.toInt?` is a safer alternative that returns {lean}`none` instead of panicking when the
string is not an integer.
Examples:
* {lean}`"0".toSlice.toInt! = 0`
* {lean}`"5".toSlice.toInt! = 5`
* {lean}`"587".toSlice.toInt! = 587`
* {lean}`"-587".toSlice.toInt! = -587`
-/
@[inline]
def toInt! (s : Slice) : Int :=
match s.toInt? with
| some v => v
| none => panic "Int expected"
/--
Returns the last character in {name}`s`. If {name}`s` is empty, returns {name}`none`.
@@ -1331,7 +1425,7 @@ Examples:
* {lean}`"abc".toSlice.back? = some 'c'`
* {lean}`"".toSlice.back? = none`
-/
@[inline]
@[inline, expose]
def back? (s : Slice) : Option Char :=
s.endPos.prev? |>.bind (·.get?)
@@ -1342,7 +1436,7 @@ Examples:
* {lean}`"abc".toSlice.back = 'c'`
* {lean}`"".toSlice.back = (default : Char)`
-/
@[inline]
@[inline, expose]
def back (s : Slice) : Char :=
s.back?.getD default

View File

@@ -6,7 +6,7 @@ Author: Leonardo de Moura, Mario Carneiro
module
prelude
public import Init.Data.String.Basic
public import Init.Data.String.Slice
/-!
# The `Substring` type
@@ -19,6 +19,26 @@ public section
namespace Substring.Raw
/--
Converts a `String.Slice` into a `Substring.Raw`.
-/
@[inline]
def ofSlice (s : String.Slice) : Substring.Raw where
str := s.str
startPos := s.startInclusive.offset
stopPos := s.endExclusive.offset
/--
Converts a `Substring.Raw` into a `String.Slice`, returning `none` if the substring is invalid.
-/
@[inline]
def toSlice? (s : Substring.Raw) : Option String.Slice :=
if h : s.startPos.IsValid s.str s.stopPos.IsValid s.str s.startPos s.stopPos then
some (String.Slice.mk s.str (s.str.pos s.startPos h.1) (s.str.pos s.stopPos h.2.1)
(by simp [String.Pos.le_iff, h.2.2]))
else
none
/--
Checks whether a substring is empty.
@@ -31,7 +51,7 @@ A substring is empty if its start and end positions are the same.
def Internal.isEmptyImpl (ss : Substring.Raw) : Bool :=
Substring.Raw.isEmpty ss
/--
/--{}
Copies the region of the underlying string pointed to by a substring into a fresh string.
-/
@[inline] def toString : Substring.Raw String
@@ -135,8 +155,7 @@ Returns the substring-relative position of the first occurrence of `c` in `s`, o
doesn't occur.
-/
@[inline] def posOf (s : Substring.Raw) (c : Char) : String.Pos.Raw :=
match s with
| s, b, e => { byteIdx := (String.posOfAux s c e b).byteIdx - b.byteIdx }
s.toSlice?.map (·.find c |>.offset) |>.getD s.bsize
/--
Removes the specified number of characters (Unicode code points) from the beginning of a substring
@@ -241,16 +260,14 @@ Folds a function over a substring from the left, accumulating a value starting w
accumulated value is combined with each character in order, using `f`.
-/
@[inline] def foldl {α : Type u} (f : α Char α) (init : α) (s : Substring.Raw) : α :=
match s with
| s, b, e => String.foldlAux f s e b init
s.toSlice?.get!.foldl f init
/--
Folds a function over a substring from the right, accumulating a value starting with `init`. The
accumulated value is combined with each character in reverse order, using `f`.
-/
@[inline] def foldr {α : Type u} (f : Char α α) (init : α) (s : Substring.Raw) : α :=
match s with
| s, b, e => String.foldrAux f init s e b
s.toSlice?.get!.foldr f init
/--
Checks whether the Boolean predicate `p` returns `true` for any character in a substring.
@@ -258,8 +275,7 @@ Checks whether the Boolean predicate `p` returns `true` for any character in a s
Short-circuits at the first character for which `p` returns `true`.
-/
@[inline] def any (s : Substring.Raw) (p : Char Bool) : Bool :=
match s with
| s, b, e => String.anyAux s e p b
s.toSlice?.get!.any p
/--
Checks whether the Boolean predicate `p` returns `true` for every character in a substring.
@@ -267,7 +283,7 @@ Checks whether the Boolean predicate `p` returns `true` for every character in a
Short-circuits at the first character for which `p` returns `false`.
-/
@[inline] def all (s : Substring.Raw) (p : Char Bool) : Bool :=
!s.any (fun c => !p c)
s.toSlice?.get!.all p
@[export lean_substring_all]
def Internal.allImpl (s : Substring.Raw) (p : Char Bool) : Bool :=

View File

@@ -113,125 +113,119 @@ theorem prev_prev_lt {s : Slice} {p : s.Pos} {h h'} : (p.prev h).prev h' < p :=
end Slice.Pos
namespace ValidPos
namespace Pos
/-- The number of bytes between `p` and the end position. This number decreases as `p` advances. -/
def remainingBytes {s : String} (p : s.ValidPos) : Nat :=
def remainingBytes {s : String} (p : s.Pos) : Nat :=
p.toSlice.remainingBytes
@[simp]
theorem remainingBytes_toSlice {s : String} {p : s.ValidPos} :
theorem remainingBytes_toSlice {s : String} {p : s.Pos} :
p.toSlice.remainingBytes = p.remainingBytes := (rfl)
theorem remainingBytes_eq_byteDistance {s : String} {p : s.ValidPos} :
p.remainingBytes = p.offset.byteDistance s.endValidPos.offset := (rfl)
theorem remainingBytes_eq_byteDistance {s : String} {p : s.Pos} :
p.remainingBytes = p.offset.byteDistance s.endPos.offset := (rfl)
theorem remainingBytes_eq {s : String} {p : s.ValidPos} :
theorem remainingBytes_eq {s : String} {p : s.Pos} :
p.remainingBytes = s.utf8ByteSize - p.offset.byteIdx := by
simp [remainingBytes_eq_byteDistance, Pos.Raw.byteDistance_eq]
theorem remainingBytes_inj {s : String} {p q : s.ValidPos} :
theorem remainingBytes_inj {s : String} {p q : s.Pos} :
p.remainingBytes = q.remainingBytes p = q := by
simp [ remainingBytes_toSlice, ValidPos.toSlice_inj, Slice.Pos.remainingBytes_inj]
simp [ remainingBytes_toSlice, Pos.toSlice_inj, Slice.Pos.remainingBytes_inj]
theorem le_iff_remainingBytes_le {s : String} (p q : s.ValidPos) :
theorem le_iff_remainingBytes_le {s : String} (p q : s.Pos) :
p q q.remainingBytes p.remainingBytes := by
simp [ remainingBytes_toSlice, Slice.Pos.le_iff_remainingBytes_le]
theorem lt_iff_remainingBytes_lt {s : String} (p q : s.ValidPos) :
theorem lt_iff_remainingBytes_lt {s : String} (p q : s.Pos) :
p < q q.remainingBytes < p.remainingBytes := by
simp [ remainingBytes_toSlice, Slice.Pos.lt_iff_remainingBytes_lt]
theorem wellFounded_lt {s : String} : WellFounded (fun (p : s.ValidPos) q => p < q) := by
theorem wellFounded_lt {s : String} : WellFounded (fun (p : s.Pos) q => p < q) := by
simpa [lt_iff, Pos.Raw.lt_iff] using
InvImage.wf (Pos.Raw.byteIdx ValidPos.offset) Nat.lt_wfRel.wf
InvImage.wf (Pos.Raw.byteIdx Pos.offset) Nat.lt_wfRel.wf
theorem wellFounded_gt {s : String} : WellFounded (fun (p : s.ValidPos) q => q < p) := by
theorem wellFounded_gt {s : String} : WellFounded (fun (p : s.Pos) q => q < p) := by
simpa [lt_iff_remainingBytes_lt] using
InvImage.wf ValidPos.remainingBytes Nat.lt_wfRel.wf
InvImage.wf Pos.remainingBytes Nat.lt_wfRel.wf
instance {s : String} : WellFoundedRelation s.ValidPos where
instance {s : String} : WellFoundedRelation s.Pos where
rel p q := q < p
wf := ValidPos.wellFounded_gt
wf := Pos.wellFounded_gt
/-- Type alias for `String.ValidPos` representing that the given position is expected to decrease
/-- Type alias for `String.Pos` representing that the given position is expected to decrease
in recursive calls. -/
structure Down (s : String) : Type where
inner : s.ValidPos
inner : s.Pos
/-- Use `termination_by pos.down` to signify that in a recursive call, the parameter `pos` is
expected to decrease. -/
def down {s : String} (p : s.ValidPos) : ValidPos.Down s where
def down {s : String} (p : s.Pos) : Pos.Down s where
inner := p
@[simp]
theorem inner_down {s : String} {p : s.ValidPos} : p.down.inner = p := (rfl)
theorem inner_down {s : String} {p : s.Pos} : p.down.inner = p := (rfl)
instance {s : String} : WellFoundedRelation (ValidPos.Down s) where
instance {s : String} : WellFoundedRelation (Pos.Down s) where
rel p q := p.inner < q.inner
wf := InvImage.wf ValidPos.Down.inner ValidPos.wellFounded_lt
wf := InvImage.wf Pos.Down.inner Pos.wellFounded_lt
theorem map_toSlice_next? {s : String} {p : s.ValidPos} :
p.next?.map ValidPos.toSlice = p.toSlice.next? := by
theorem map_toSlice_next? {s : String} {p : s.Pos} :
p.next?.map Pos.toSlice = p.toSlice.next? := by
simp [next?]
theorem map_toSlice_prev? {s : String} {p : s.ValidPos} :
p.prev?.map ValidPos.toSlice = p.toSlice.prev? := by
theorem map_toSlice_prev? {s : String} {p : s.Pos} :
p.prev?.map Pos.toSlice = p.toSlice.prev? := by
simp [prev?]
theorem ne_endValidPos_of_next?_eq_some {s : String} {p q : s.ValidPos}
(h : p.next? = some q) : p s.endValidPos :=
ne_of_apply_ne ValidPos.toSlice (Slice.Pos.ne_endPos_of_next?_eq_some
(by simpa only [ValidPos.map_toSlice_next?, Option.map_some] using congrArg (·.map toSlice) h))
theorem ne_endPos_of_next?_eq_some {s : String} {p q : s.Pos}
(h : p.next? = some q) : p s.endPos :=
ne_of_apply_ne Pos.toSlice (Slice.Pos.ne_endPos_of_next?_eq_some
(by simpa only [Pos.map_toSlice_next?, Option.map_some] using congrArg (·.map toSlice) h))
theorem eq_next_of_next?_eq_some {s : String} {p q : s.ValidPos} (h : p.next? = some q) :
q = p.next (ne_endValidPos_of_next?_eq_some h) := by
theorem eq_next_of_next?_eq_some {s : String} {p q : s.Pos} (h : p.next? = some q) :
q = p.next (ne_endPos_of_next?_eq_some h) := by
simpa only [ toSlice_inj, toSlice_next] using Slice.Pos.eq_next_of_next?_eq_some
(by simpa [ValidPos.map_toSlice_next?] using congrArg (·.map toSlice) h)
(by simpa [Pos.map_toSlice_next?] using congrArg (·.map toSlice) h)
theorem ne_startValidPos_of_prev?_eq_some {s : String} {p q : s.ValidPos}
(h : p.prev? = some q) : p s.startValidPos :=
ne_of_apply_ne ValidPos.toSlice (Slice.Pos.ne_startPos_of_prev?_eq_some
(by simpa only [ValidPos.map_toSlice_prev?, Option.map_some] using congrArg (·.map toSlice) h))
theorem ne_startPos_of_prev?_eq_some {s : String} {p q : s.Pos}
(h : p.prev? = some q) : p s.startPos :=
ne_of_apply_ne Pos.toSlice (Slice.Pos.ne_startPos_of_prev?_eq_some
(by simpa only [Pos.map_toSlice_prev?, Option.map_some] using congrArg (·.map toSlice) h))
theorem eq_prev_of_prev?_eq_some {s : String} {p q : s.ValidPos} (h : p.prev? = some q) :
q = p.prev (ne_startValidPos_of_prev?_eq_some h) := by
theorem eq_prev_of_prev?_eq_some {s : String} {p q : s.Pos} (h : p.prev? = some q) :
q = p.prev (ne_startPos_of_prev?_eq_some h) := by
simpa only [ toSlice_inj, toSlice_prev] using Slice.Pos.eq_prev_of_prev?_eq_some
(by simpa [ValidPos.map_toSlice_prev?] using congrArg (·.map toSlice) h)
(by simpa [Pos.map_toSlice_prev?] using congrArg (·.map toSlice) h)
@[simp]
theorem le_refl {s : String} (p : s.ValidPos) : p p := by
simp [ValidPos.le_iff]
theorem le_refl {s : String} (p : s.Pos) : p p := by
simp [Pos.le_iff]
theorem lt_trans {s : String} {p q r : s.ValidPos} : p < q q < r p < r := by
simpa [ValidPos.lt_iff, Pos.Raw.lt_iff] using Nat.lt_trans
theorem le_trans {s : String} {p q r : s.ValidPos} : p q q r p r := by
simpa [ValidPos.le_iff, Pos.Raw.le_iff] using Nat.le_trans
theorem le_of_lt {s : String} {p q : s.ValidPos} : p < q p q := by
simpa [ValidPos.le_iff, ValidPos.lt_iff, Pos.Raw.le_iff, Pos.Raw.lt_iff] using Nat.le_of_lt
theorem lt_trans {s : String} {p q r : s.Pos} : p < q q < r p < r := by
simpa [Pos.lt_iff, Pos.Raw.lt_iff] using Nat.lt_trans
@[simp]
theorem lt_next_next {s : String} {p : s.ValidPos} {h h'} : p < (p.next h).next h' :=
theorem lt_next_next {s : String} {p : s.Pos} {h h'} : p < (p.next h).next h' :=
lt_trans p.lt_next (p.next h).lt_next
@[simp]
theorem prev_prev_lt {s : String} {p : s.ValidPos} {h h'} : (p.prev h).prev h' < p :=
theorem prev_prev_lt {s : String} {p : s.Pos} {h h'} : (p.prev h).prev h' < p :=
lt_trans (p.prev h).prev_lt p.prev_lt
theorem Splits.remainingBytes_eq {s : String} {p : s.ValidPos} {t₁ t₂}
theorem Splits.remainingBytes_eq {s : String} {p : s.Pos} {t₁ t₂}
(h : p.Splits t₁ t₂) : p.remainingBytes = t₂.utf8ByteSize := by
simp [ValidPos.remainingBytes_eq, h.eq_append, h.offset_eq_rawEndPos]
simp [Pos.remainingBytes_eq, h.eq_append, h.offset_eq_rawEndPos]
end ValidPos
end Pos
namespace Slice.Pos
@[simp]
theorem remainingBytes_toCopy {s : Slice} {p : s.Pos} :
p.toCopy.remainingBytes = p.remainingBytes := by
simp [remainingBytes_eq, ValidPos.remainingBytes_eq, Slice.utf8ByteSize_eq]
simp [remainingBytes_eq, String.Pos.remainingBytes_eq, Slice.utf8ByteSize_eq]
theorem Splits.remainingBytes_eq {s : Slice} {p : s.Pos} {t₁ t₂} (h : p.Splits t₁ t₂) :
p.remainingBytes = t₂.utf8ByteSize := by
@@ -250,14 +244,14 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
Slice.Pos.eq_prev_of_prev?_eq_some (by assumption),
]) <;> done)
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
(with_reducible change (_ : String.ValidPos _) < _
(with_reducible change (_ : String.Pos _) < _
simp [
ValidPos.eq_next_of_next?_eq_some (by assumption),
Pos.eq_next_of_next?_eq_some (by assumption),
]) <;> done)
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
(with_reducible change (_ : String.ValidPos _) < _
(with_reducible change (_ : String.Pos _) < _
simp [
ValidPos.eq_prev_of_prev?_eq_some (by assumption),
Pos.eq_prev_of_prev?_eq_some (by assumption),
]) <;> done)
end String

View File

@@ -8,6 +8,7 @@ module
prelude
public import Init.Data.String.Substring
import Init.Data.String.TakeDrop
import Init.Data.String.Search
/-!
Here we give the. implementation of `Name.toString`. There is also a private implementation in

View File

@@ -525,12 +525,12 @@ and do not provide separate verification theorems.
@[simp] theorem mem_toArray_iff (a : α) (xs : Vector α n) : a xs.toArray a xs :=
fun h => h, fun h => h
instance : ForIn' m (Vector α n) α inferInstance where
instance [Monad m] : ForIn' m (Vector α n) α inferInstance where
forIn' xs b f := Array.forIn' xs.toArray b (fun a h b => f a (by simpa using h) b)
/-! ### ForM instance -/
instance : ForM m (Vector α n) α where
instance [Monad m] : ForM m (Vector α n) α where
forM := Vector.forM
-- We simplify `Vector.forM` to `forM`.

View File

@@ -26,3 +26,5 @@ public import Init.Grind.Injective
public import Init.Grind.Order
public import Init.Grind.Interactive
public import Init.Grind.Lint
public import Init.Grind.Annotated
public import Init.Grind.FieldNormNum

View File

@@ -0,0 +1,34 @@
/-
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.Tactics
public section
namespace Lean.Parser.Command
/--
`grind_annotated "YYYY-MM-DD"` marks the current file as having been manually annotated for `grind`.
When the LibrarySuggestion framework is called with `caller := "grind"` (as happens when using
`grind +suggestions`), theorems from grind-annotated files are excluded from premise selection.
This is because these files have already been manually reviewed and annotated with appropriate
`@[grind]` attributes.
The date argument (in YYYY-MM-DD format) records when the file was annotated. This is currently
informational only, but may be used in the future to detect files that have been significantly
modified since annotation and may need re-review.
Example:
```
grind_annotated "2025-01-15"
```
This command should typically appear near the top of a file, after imports.
-/
syntax (name := grindAnnotated) "grind_annotated" str : command
end Lean.Parser.Command

View File

@@ -188,6 +188,16 @@ where the term `f` contains at least one constant symbol.
-/
syntax grindInj := &"inj"
/--
The `funCC` modifier marks global functions that support **function-valued congruence closure**.
Given an application `f a₁ a₂ … aₙ`, when `funCC := true`,
`grind` generates and tracks equalities for all partial applications:
- `f a₁`
- `f a₁ a₂`
- `…`
- `f a₁ a₂ … aₙ`
-/
syntax grindFunCC := &"funCC"
/--
`symbol <prio>` sets the priority of a constant for `grind`s pattern-selection
procedure. `grind` prefers patterns that contain higher-priority symbols.
Example:
@@ -214,7 +224,7 @@ syntax grindMod :=
grindEqBoth <|> grindEqRhs <|> grindEq <|> grindEqBwd <|> grindBwd
<|> grindFwd <|> grindRL <|> grindLR <|> grindUsr <|> grindCasesEager
<|> grindCases <|> grindIntro <|> grindExt <|> grindGen <|> grindSym <|> grindInj
<|> grindDef
<|> grindFunCC <|> grindDef
/--
Marks a theorem or definition for use by the `grind` tactic.

View File

@@ -172,6 +172,36 @@ structure Config where
and then reintroduces them while simplifying and applying eager `cases`.
-/
revert := false
/--
When `true`, it enables **function-valued congruence closure**.
`grind` treats equalities of partially applied functions as first-class equalities
and propagates them through further applications.
Given an application `f a₁ a₂ … aₙ`, when `funCC := true` *and* function equality is enabled for `f`,
`grind` generates and tracks equalities for all partial applications:
- `f a₁`
- `f a₁ a₂`
- `…`
- `f a₁ a₂ … aₙ`
This allows equalities such as `f a₁ = g` to propagate to
`f a₁ a₂ = g a₂`.
**When is function equality enabled for a symbol?**
Function equality is automatically enabled in the following cases:
1. **`f` is not a constant.** (For example, a lambda expression, a local variable, or a function parameter.)
2. **`f` is a structure field projection**, *provided the structure is not a `class`.*
3. **`f` is a constant marked with the attribute:** `@[grind funCC]`
If none of the above conditions apply, function equality is disabled for `f`, and congruence
closure behaves almost like it does in SMT solvers for first-order logic.
Here is an example, `grind` can solve when `funCC := true`
```
example (a b : Nat) (g : Nat → Nat) (f : Nat → Nat → Nat) (h : f a = g) :
f a b = g b := by
grind
```
-/
funCC := true
deriving Inhabited, BEq
/--

View File

@@ -0,0 +1,219 @@
/-
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
-/
module
prelude
public import Init.Grind.Ring.Field
public import Init.Data.Rat.Basic
import Init.Data.Rat.Lemmas
public section
namespace Lean.Grind.Field.NormNum
attribute [local instance] Semiring.natCast Ring.intCast
abbrev ofRat {α} [Field α] (r : Rat) : α :=
(r.num : α)/(r.den : α)
attribute [local simp]
Field.inv_one Semiring.natCast_zero Semiring.natCast_one Ring.intCast_zero Ring.intCast_one Semiring.one_mul Semiring.mul_one
Semiring.pow_zero Field.inv_one Field.inv_zero
private theorem dvd_helper₁ {z : Int} {n : Nat} : (z.natAbs.gcd n : Int) z :=
Rat.normalize.dvd_num rfl
private theorem dvd_helper₂ {z : Int} {n : Nat} : z.natAbs.gcd n n :=
Nat.gcd_dvd_right z.natAbs n
private theorem nonzero_helper {α} [Field α] {z : Int} {n m : Nat} (hn : (n : α) 0) (hm : (m : α) 0) :
(z.natAbs.gcd (n * m) : α) 0 := by
intro h
have : z.natAbs.gcd (n * m) (n * m) := Nat.gcd_dvd_right z.natAbs (n * m)
obtain k, hk := this
replace hk := congrArg (fun x : Nat => (x : α)) hk
dsimp at hk
rw [Semiring.natCast_mul, Semiring.natCast_mul, h, Semiring.zero_mul] at hk
replace hk := Field.of_mul_eq_zero hk
simp_all
theorem ofRat_add' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.den : α) 0) :
(ofRat (a + b) : α) = ofRat a + ofRat b := by
rw [ofRat, ofRat, ofRat, Rat.num_add, Rat.den_add]
rw [Field.intCast_div_of_dvd dvd_helper₁ (by simpa [Ring.intCast_natCast] using (nonzero_helper ha hb))]
rw [Field.natCast_div_of_dvd dvd_helper₂ (nonzero_helper ha hb)]
rw [Ring.intCast_natCast, Field.div_div_right]
rw [Field.div_mul_cancel (nonzero_helper ha hb)]
rw [Field.add_div hb, Field.div_mul, Field.div_add ha]
rw [Ring.intCast_add, Ring.intCast_mul, Ring.intCast_mul, Ring.intCast_natCast,
Ring.intCast_natCast, CommSemiring.mul_comm (a.den : α), Field.div_div_left,
Semiring.natCast_mul]
theorem ofRat_mul' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.den : α) 0) : (ofRat (a * b) : α) = ofRat a * ofRat b := by
rw [ofRat, ofRat, ofRat, Rat.num_mul, Rat.den_mul]
rw [Field.intCast_div_of_dvd dvd_helper₁ (by simpa [Ring.intCast_natCast] using (nonzero_helper ha hb))]
rw [Field.natCast_div_of_dvd dvd_helper₂ (nonzero_helper ha hb)]
rw [Ring.intCast_natCast, Field.div_div_right]
rw [Field.div_mul_cancel (nonzero_helper ha hb)]
rw [Field.div_mul, Field.mul_div, Field.div_div_left, Ring.intCast_mul, Semiring.natCast_mul,
CommSemiring.mul_comm (b.den : α)]
-- Note: false without `IsCharP α 0` (consider `a = b = 1/2` in `/2`):
theorem ofRat_add {α} [Field α] [IsCharP α 0] (a b : Rat) :
(ofRat (a + b) : α) = ofRat a + ofRat b :=
ofRat_add' (natCast_ne_zero a.den_nz) (natCast_ne_zero b.den_nz)
-- Note: false without `IsCharP α 0` (consider `a = 2/3` and `b = 1/2` in `/2`):
theorem ofRat_mul {α} [Field α] [IsCharP α 0] (a b : Rat) : (ofRat (a * b) : α) = ofRat a * ofRat b :=
ofRat_mul' (natCast_ne_zero a.den_nz) (natCast_ne_zero b.den_nz)
theorem ofRat_inv {α} [Field α] (a : Rat) : (ofRat (a⁻¹) : α) = (ofRat a)⁻¹ := by
simp [ofRat]; split
next h => simp [h, Field.div_eq_mul_inv]
next =>
simp [Field.div_eq_mul_inv, Field.inv_mul, Field.inv_inv, Ring.intCast_mul, Ring.intCast_natCast]
generalize a.num = n
generalize a.den = d
conv => rhs; rw [ Int.sign_mul_natAbs n]
simp [Ring.intCast_mul, Ring.intCast_natCast, Field.inv_mul]
have : (Int.cast n.sign : α) = (Int.cast n.sign : α)⁻¹ := by
cases Int.sign_trichotomy n
next h => simp [h]
next h => cases h <;> simp [*, Ring.intCast_neg, Field.inv_neg]
rw [ this, Semiring.mul_assoc, Semiring.mul_assoc]
congr 1
rw [CommSemiring.mul_comm]
theorem ofRat_div' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.num : α) 0) :
(ofRat (a / b) : α) = ofRat a / ofRat b := by
replace hb : ((b⁻¹).den : α) 0 := by
simp only [Rat.den_inv, Rat.num_eq_zero, ne_eq]
rw [if_neg (by intro h; simp_all)]
rw [ Ring.intCast_natCast]
by_cases h : 0 b.num
· have : (b.num.natAbs : Int) = b.num := Int.natAbs_of_nonneg h
rwa [this]
· have : (b.num.natAbs : Int) = -b.num := Int.ofNat_natAbs_of_nonpos (by omega)
rw [this, Ring.intCast_neg]
rwa [AddCommGroup.neg_eq_iff, AddCommGroup.neg_zero]
rw [Rat.div_def, ofRat_mul' ha hb, ofRat_inv, Field.div_eq_mul_inv (ofRat a)]
theorem ofRat_div {α} [Field α] [IsCharP α 0] (a b : Rat) : (ofRat (a / b) : α) = ofRat a / ofRat b := by
rw [Rat.div_def, ofRat_mul, ofRat_inv, Field.div_eq_mul_inv (ofRat a)]
theorem ofRat_neg {α} [Field α] (a : Rat) : (ofRat (-a) : α) = -ofRat a := by
simp [ofRat, Field.div_eq_mul_inv, Ring.intCast_neg, Ring.neg_mul]
theorem ofRat_sub' {α} [Field α] {a b : Rat} (ha : (a.den : α) 0) (hb : (b.den : α) 0) :
(ofRat (a - b) : α) = ofRat a - ofRat b := by
replace hb : ((-b).den : α) 0 := by simpa
rw [Rat.sub_eq_add_neg, ofRat_add' ha hb, ofRat_neg, Ring.sub_eq_add_neg]
theorem ofRat_sub {α} [Field α] [IsCharP α 0] (a b : Rat) :
(ofRat (a - b) : α) = ofRat a - ofRat b := by
rw [Rat.sub_eq_add_neg, ofRat_add, ofRat_neg, Ring.sub_eq_add_neg]
theorem ofRat_npow' {α} [Field α] {a : Rat} (ha : (a.den : α) 0) (n : Nat) : (ofRat (a^n) : α) = ofRat a ^ n := by
have h : n : Nat, ((a^n).den : α) 0 := by
intro n
rw [Rat.den_pow, ne_eq, Semiring.natCast_pow]
intro h
induction n with
| zero =>
rw [Semiring.pow_zero] at h
exact Field.zero_ne_one h.symm
| succ n ih' =>
rw [Semiring.pow_succ] at h
replace h := Field.of_mul_eq_zero h
rcases h with h | h
· exact ih' h
· exact ha h
induction n
next => simp [Field.div_eq_mul_inv, ofRat]
next n ih =>
rw [Rat.pow_succ, ofRat_mul' (h _) ha, ih, Semiring.pow_succ]
theorem ofRat_npow {α} [Field α] [IsCharP α 0] (a : Rat) (n : Nat) : (ofRat (a^n) : α) = ofRat a ^ n := by
induction n
next => simp [Field.div_eq_mul_inv, ofRat]
next n ih => rw [Rat.pow_succ, ofRat_mul, ih, Semiring.pow_succ]
theorem ofRat_zpow' {α} [Field α] {a : Rat} (ha : (a.den : α) 0) (n : Int) : (ofRat (a^n) : α) = ofRat a ^ n := by
cases n
next => rw [Int.ofNat_eq_natCast, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow' ha]
next =>
rw [Int.negSucc_eq, Rat.zpow_neg, Field.zpow_neg, ofRat_inv]
congr 1
have : (1 : Int) = (1 : Nat) := rfl
rw [this, Int.natCast_add, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow' ha]
theorem ofRat_zpow {α} [Field α] [IsCharP α 0] (a : Rat) (n : Int) : (ofRat (a^n) : α) = ofRat a ^ n := by
cases n
next => rw [Int.ofNat_eq_natCast, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow]
next =>
rw [Int.negSucc_eq, Rat.zpow_neg, Field.zpow_neg, ofRat_inv]
congr 1
have : (1 : Int) = (1 : Nat) := rfl
rw [this, Int.natCast_add, Rat.zpow_natCast, Field.zpow_natCast, ofRat_npow]
theorem natCast_eq {α} [Field α] (n : Nat) : (NatCast.natCast n : α) = ofRat n := by
simp [ofRat, Ring.intCast_natCast, Semiring.natCast_one, Field.div_eq_mul_inv,
Field.inv_one, Semiring.mul_one]
theorem ofNat_eq {α} [Field α] (n : Nat) : (OfNat.ofNat n : α) = ofRat n := by
rw [Semiring.ofNat_eq_natCast]
apply natCast_eq
theorem intCast_eq {α} [Field α] (n : Int) : (IntCast.intCast n : α) = ofRat n := by
simp [ofRat, Semiring.natCast_one, Field.div_eq_mul_inv, Field.inv_one, Semiring.mul_one]
theorem add_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ + v₂ a = ofRat v₁ b = ofRat v₂ a + b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_add]
theorem sub_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ - v₂ a = ofRat v₁ b = ofRat v₂ a - b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_sub]
theorem mul_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ * v₂ a = ofRat v₁ b = ofRat v₂ a * b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_mul]
theorem div_eq {α} [Field α] [IsCharP α 0] (a b : α) (v₁ v₂ v : Rat)
: v == v₁ / v₂ a = ofRat v₁ b = ofRat v₂ a / b = ofRat v := by
simp; intros; subst v a b; rw [ofRat_div]
theorem inv_eq {α} [Field α] (a : α) (v₁ v : Rat)
: v == v₁⁻¹ a = ofRat v₁ a⁻¹ = ofRat v := by
simp; intros; subst v a; rw [ofRat_inv]
theorem neg_eq {α} [Field α] (a : α) (v₁ v : Rat)
: v == -v₁ a = ofRat v₁ -a = ofRat v := by
simp; intros; subst v a; rw [ofRat_neg]
theorem npow_eq {α} [Field α] [IsCharP α 0] (a : α) (n : Nat) (v₁ v : Rat)
: v == v₁^n a = ofRat v₁ a ^ n = ofRat v := by
simp; intros; subst v a; rw [ofRat_npow]
theorem zpow_eq {α} [Field α] [IsCharP α 0] (a : α) (n : Int) (v₁ v : Rat)
: v == v₁^n a = ofRat v₁ a ^ n = ofRat v := by
simp; intros; subst v a; rw [ofRat_zpow]
theorem eq_int {α} [Field α] (a : α) (v : Rat) (n : Int)
: n == v.num && v.den == 1 a = ofRat v a = IntCast.intCast n := by
simp; cases v; simp [ofRat]
next den _ _ =>
intros; subst den n a
simp [Semiring.natCast_one, Field.div_eq_mul_inv, Field.inv_one, Semiring.mul_one]
theorem eq_inv {α} [Field α] (a : α) (v : Rat) (d : Nat)
: v.num == 1 && v.den == d a = ofRat v a = (NatCast.natCast d : α)⁻¹ := by
simp; cases v; simp [ofRat]
next num _ _ _ =>
intros; subst num d a
simp [Ring.intCast_one, Field.div_eq_mul_inv, Semiring.one_mul]
theorem eq_mul_inv {α} [Field α] (a : α) (v : Rat) (n : Int) (d : Nat)
: v.num == n && v.den == d a = ofRat v a = (IntCast.intCast n : α) * (NatCast.natCast d : α)⁻¹ := by
cases v; simp [ofRat]
intros; subst d n a
simp [Field.div_eq_mul_inv]
end Lean.Grind.Field.NormNum

View File

@@ -72,7 +72,9 @@ syntax (name := linarith) "linarith" : grind
/-- The `sorry` tactic is a temporary placeholder for an incomplete tactic proof. -/
syntax (name := «sorry») "sorry" : grind
syntax thm := anchor <|> grindLemmaMin <|> grindLemma
syntax thmNs := &"namespace" ident
syntax thm := anchor <|> thmNs <|> grindLemmaMin <|> grindLemma
/--
Instantiates theorems using E-matching.

View File

@@ -77,11 +77,16 @@ syntax (name := grindLintMute) "#grind_lint" ppSpace &"mute" ident+ : command
`#grind_lint skip thm₁ …` marks the given theorem(s) to be skipped entirely by `#grind_lint check`.
Skipped theorems are neither analyzed nor reported, but may still be used for
instantiation when analyzing other theorems.
Example:
`#grind_lint skip suffix name₁ …` marks all theorems with the given suffix(es) to be skipped.
For example, `#grind_lint skip suffix foo` will skip `bar.foo`, `qux.foo`, etc.
Examples:
```
#grind_lint skip Array.range_succ
#grind_lint skip suffix append
```
-/
syntax (name := grindLintSkip) "#grind_lint" ppSpace &"skip" ident+ : command
syntax (name := grindLintSkip) "#grind_lint" ppSpace &"skip" (ppSpace &"suffix")? ident+ : command
end Lean.Grind

View File

@@ -8,6 +8,7 @@ prelude
public import Init.Data.Int.Linear
public import Init.Grind.Ring.Field
public import Init.Data.Rat.Lemmas
public import Init.Grind.Ring.OfScientific
public section
namespace Lean.Grind
@@ -207,7 +208,9 @@ init_grind_norm
Ring.intCast_mul
Ring.intCast_pow
Ring.intCast_sub
-- OfScientific
LawfulOfScientific.ofScientific_def
-- Rationals
Rat.ofScientific_def_eq_if Rat.zpow_neg
Rat.zpow_neg
end Lean.Grind

View File

@@ -1393,15 +1393,19 @@ theorem simp {α} [CommRing α] (ctx : Context α) (k₁ : Int) (p₁ : Poly) (k
noncomputable def mul_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
p₁.mulConst_k k |>.beq' p
def mul {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
theorem mul {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_cert p₁ k p p₁.denote ctx = 0 p.denote ctx = 0 := by
simp [mul_cert]; intro _ h; subst p
simp [Poly.denote_mulConst, *, mul_zero]
theorem inv {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (p : Poly)
: mul_cert p₁ (-1) p p₁.denote ctx = 0 p.denote ctx = 0 :=
mul ctx p₁ (-1) p
noncomputable def div_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
!Int.beq' k 0 |>.and' (p.mulConst_k k |>.beq' p₁)
def div {α} [CommRing α] (ctx : Context α) [NoNatZeroDivisors α] (p₁ : Poly) (k : Int) (p : Poly)
theorem div {α} [CommRing α] (ctx : Context α) [NoNatZeroDivisors α] (p₁ : Poly) (k : Int) (p : Poly)
: div_cert p₁ k p p₁.denote ctx = 0 p.denote ctx = 0 := by
simp [div_cert]; intro hnz _ h; subst p₁
simp [Poly.denote_mulConst, zsmul_eq_intCast_mul] at h
@@ -1575,60 +1579,62 @@ theorem Poly.denoteAsIntModule_eq_denote {α} [CommRing α] (ctx : Context α) (
open Stepwise
theorem eq_norm {α} [CommRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denoteAsIntModule ctx = 0 := by
rw [Poly.denoteAsIntModule_eq_denote]; apply core
: core_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote ctx = 0 := by
apply core
theorem eq_int_module {α} [CommRing α] (ctx : Context α) (p : Poly)
: p.denote ctx = 0 p.denoteAsIntModule ctx = 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
theorem diseq_norm {α} [CommRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote ctx 0 := by
simp [core_cert]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
intro h; rw [sub_eq_zero_iff] at h; contradiction
theorem diseq_int_module {α} [CommRing α] (ctx : Context α) (p : Poly)
: p.denote ctx 0 p.denoteAsIntModule ctx 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
open OrderedAdd
theorem le_norm {α} [CommRing α] [LE α] [LT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote ctx 0 := by
simp [core_cert]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h := add_le_left h ((-1) * rhs.denote ctx)
rw [neg_mul, sub_eq_add_neg, one_mul, sub_eq_add_neg, sub_self] at h
assumption
theorem le_int_module {α} [CommRing α] [LE α] (ctx : Context α) (p : Poly)
: p.denote ctx 0 p.denoteAsIntModule ctx 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
theorem lt_norm {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx < rhs.denote ctx p.denoteAsIntModule ctx < 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert lhs rhs p lhs.denote ctx < rhs.denote ctx p.denote ctx < 0 := by
simp [core_cert]; intro _ h; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h := add_lt_left h ((-1) * rhs.denote ctx)
rw [neg_mul, sub_eq_add_neg, one_mul, sub_eq_add_neg, sub_self] at h
assumption
theorem lt_int_module {α} [CommRing α] [LT α] (ctx : Context α) (p : Poly)
: p.denote ctx < 0 p.denoteAsIntModule ctx < 0 := by
simp [Poly.denoteAsIntModule_eq_denote]
theorem not_le_norm {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert rhs lhs p ¬ lhs.denote ctx rhs.denote ctx p.denoteAsIntModule ctx < 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert rhs lhs p ¬ lhs.denote ctx rhs.denote ctx p.denote ctx < 0 := by
simp [core_cert]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h₁ := LinearOrder.lt_of_not_le h₁
replace h₁ := add_lt_left h₁ (-lhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_lt_norm {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert rhs lhs p ¬ lhs.denote ctx < rhs.denote ctx p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
: core_cert rhs lhs p ¬ lhs.denote ctx < rhs.denote ctx p.denote ctx 0 := by
simp [core_cert]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]
replace h₁ := LinearOrder.le_of_not_lt h₁
replace h₁ := add_le_left h₁ (-lhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_le_norm' {α} [CommRing α] [LE α] [LT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p ¬ lhs.denote ctx rhs.denote ctx ¬ p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]; intro h
replace h : rhs.denote ctx + (lhs.denote ctx - rhs.denote ctx) _ := add_le_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp [add_zero] at h
contradiction
theorem not_lt_norm' {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p ¬ lhs.denote ctx < rhs.denote ctx ¬ p.denoteAsIntModule ctx < 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]; intro h
replace h : rhs.denote ctx + (lhs.denote ctx - rhs.denote ctx) < _ := add_lt_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp [add_zero] at h
contradiction
theorem inv_int_eq [Field α] [IsCharP α 0] (b : Int) : b != 0 (denoteInt b : α) * (denoteInt b)⁻¹ = 1 := by
simp; intro h
have : (denoteInt b : α) 0 := by

View File

@@ -94,7 +94,7 @@ theorem inv_eq_zero_iff {a : α} : a⁻¹ = 0 ↔ a = 0 := by
theorem zero_eq_inv_iff {a : α} : 0 = a⁻¹ 0 = a := by
rw [eq_comm, inv_eq_zero_iff, eq_comm]
theorem of_mul_eq_zero {a b : α} : a*b = 0 a = 0 b = 0 := by
theorem of_mul_eq_zero {a b : α} : a * b = 0 a = 0 b = 0 := by
cases (Classical.em (a = 0)); · simp [*, Semiring.zero_mul]
cases (Classical.em (b = 0)); · simp [*, Semiring.mul_zero]
rename_i h₁ h₂
@@ -169,6 +169,52 @@ theorem zpow_add {a : α} (h : a ≠ 0) (m n : Int) : a ^ (m + n) = a ^ m * a ^
| zero => simp [Int.add_neg_one, zpow_sub_one h, zpow_neg_one]
| succ n ih => rw [Int.natCast_add_one, Int.neg_add, Int.add_neg_one, Int.add_sub_assoc, zpow_sub_one h, zpow_sub_one h, ih, Semiring.mul_assoc]
theorem div_zero {x : α} : x / 0 = 0 := by rw [div_eq_mul_inv, inv_zero, Semiring.mul_zero]
theorem mul_div {x y z : α} : x * (y / z) = (x * y) / z := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.mul_assoc]
theorem div_mul {x y z : α} : x / y * z = x * z / y := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.mul_assoc, CommSemiring.mul_comm y⁻¹,
Semiring.mul_assoc]
theorem div_add {x y z : α} (hy : y 0) : x / y + z = (x + y * z) / y := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.right_distrib, CommSemiring.mul_comm y,
Semiring.mul_assoc, Field.mul_inv_cancel hy, Semiring.mul_one]
theorem add_div {x y z : α} (hz : z 0) : x + y / z = (x * z + y) / z := by
rw [div_eq_mul_inv, div_eq_mul_inv, Semiring.right_distrib, Semiring.mul_assoc,
Field.mul_inv_cancel hz, Semiring.mul_one]
theorem div_div_right {x y z : α} : x / (y / z) = x * z / y := by
rw [div_eq_mul_inv, div_eq_mul_inv, div_eq_mul_inv, inv_mul, inv_inv, CommSemiring.mul_comm y⁻¹,
Semiring.mul_assoc]
theorem div_div_left {x y z : α} : (x / y) / z = x / (y * z) := by
rw [div_eq_mul_inv, div_eq_mul_inv, div_eq_mul_inv, inv_mul, Semiring.mul_assoc]
theorem div_mul_cancel {x y : α} (h : y 0) : x / y * y = x := by
rw [div_eq_mul_inv, Semiring.mul_assoc, Field.inv_mul_cancel h, Semiring.mul_one]
attribute [local instance] Semiring.natCast in
theorem natCast_ne_zero [IsCharP α 0] {n : Nat} (h : n 0) : (n : α) 0 := by
simpa [IsCharP.natCast_eq_zero_iff]
attribute [local instance] Ring.intCast in
theorem intCast_div_of_dvd {x y : Int} (h : y x) (w : (y : α) 0) :
((x / y : Int) : α) = ((x : α) / (y : α)) := by
obtain z, rfl := h
by_cases hy : y = 0
· simp_all [Ring.intCast_zero]
· rw [Int.mul_ediv_cancel_left _ hy]
rw [Ring.intCast_mul, CommSemiring.mul_comm, div_eq_mul_inv, Semiring.mul_assoc,
mul_inv_cancel w, Semiring.mul_one]
attribute [local instance] Semiring.natCast in
theorem natCast_div_of_dvd {x y : Nat} (h : y x) (w : (y : α) 0) :
((x / y : Nat) : α) = ((x : α) / (y : α)) := by
obtain z, rfl := h
by_cases hy : y = 0
· simp_all [Semiring.natCast_zero]
· rw [Nat.mul_div_cancel_left _ (by omega)]
rw [Semiring.natCast_mul, CommSemiring.mul_comm, div_eq_mul_inv, Semiring.mul_assoc,
mul_inv_cancel w, Semiring.mul_one]
-- This is expensive as an instance. Let's see what breaks without it.
def noNatZeroDivisors.ofIsCharPZero [IsCharP α 0] : NoNatZeroDivisors α := NoNatZeroDivisors.mk' <| by
intro a b h w

View File

@@ -299,9 +299,19 @@ syntax (name := grindTrace)
It is a implemented as a thin wrapper around the `grind` tactic, enabling only the `cutsat` solver.
Please use `grind` instead if you need additional capabilities.
**Deprecated**: Use `lia` instead.
-/
syntax (name := cutsat) "cutsat" optConfig : tactic
/--
`lia` solves linear integer arithmetic goals.
It is a implemented as a thin wrapper around the `grind` tactic, enabling only the `cutsat` solver.
Please use `grind` instead if you need additional capabilities.
-/
syntax (name := lia) "lia" optConfig : tactic
/--
`grobner` solves goals that can be phrased as polynomial equations (with further polynomial equations as hypotheses)
over commutative (semi)rings, using the Grobner basis algorithm.

View File

@@ -108,7 +108,7 @@ instance : ToInt (Fin n) (.co 0 n) where
toInt_inj x y w := Fin.eq_of_val_eq (Int.ofNat_inj.mp w)
toInt_mem := by simp
@[simp] theorem toInt_fin (x : Fin n) : ToInt.toInt x = (x.val : Int) := rfl
@[simp, grind =] theorem toInt_fin (x : Fin n) : ToInt.toInt x = (x.val : Int) := rfl
instance [NeZero n] : ToInt.Zero (Fin n) (.co 0 n) where
toInt_zero := rfl
@@ -116,6 +116,9 @@ instance [NeZero n] : ToInt.Zero (Fin n) (.co 0 n) where
instance [NeZero n] : ToInt.OfNat (Fin n) (.co 0 n) where
toInt_ofNat x := by simp; rfl
theorem ofNat_FinZero (n : Nat) [NeZero n] : ToInt.toInt (OfNat.ofNat 0 : Fin n) = 0 := by
rw [ToInt.toInt, instToIntFinCoOfNatIntCast, Fin.instOfNat, Fin.ofNat]; simp
instance : ToInt.Add (Fin n) (.co 0 n) where
toInt_add x y := by rfl

View File

@@ -631,6 +631,8 @@ structure Subtype {α : Sort u} (p : α → Prop) where
-/
property : p val
grind_pattern Subtype.property => self.val
set_option linter.unusedVariables.funArgs false in
/--
Gadget for optional parameter support.
@@ -2262,6 +2264,7 @@ structure Fin (n : Nat) where
isLt : LT.lt val n
attribute [coe] Fin.val
grind_pattern Fin.isLt => self.val
theorem Fin.eq_of_val_eq {n} : {i j : Fin n}, Eq i.val j.val Eq i j
| _, _, _, _, rfl => rfl
@@ -3446,11 +3449,11 @@ structure String where ofByteArray ::
/-- The bytes of the UTF-8 encoding of the string. Since strings have a special representation in
the runtime, this function actually takes linear time and space at runtime. For efficient access
to the string's bytes, use `String.utf8ByteSize` and `String.getUTF8Byte`. -/
bytes : ByteArray
toByteArray : ByteArray
/-- The bytes of the string form valid UTF-8. -/
isValidUTF8 : ByteArray.IsValidUTF8 bytes
isValidUTF8 : ByteArray.IsValidUTF8 toByteArray
attribute [extern "lean_string_to_utf8"] String.bytes
attribute [extern "lean_string_to_utf8"] String.toByteArray
attribute [extern "lean_string_from_utf8_unchecked"] String.ofByteArray
/--
@@ -3465,7 +3468,7 @@ def String.decEq (s₁ s₂ : @& String) : Decidable (Eq s₁ s₂) :=
| s₁, _, s₂, _ =>
dite (Eq s₁ s₂) (fun h => match s₁, s₂, h with | _, _, Eq.refl _ => isTrue rfl)
(fun h => isFalse
(fun h' => h (congrArg (fun s => Array.toList (ByteArray.data (String.bytes s))) h')))
(fun h' => h (congrArg (fun s => Array.toList (ByteArray.data (String.toByteArray s))) h')))
instance : DecidableEq String := String.decEq
@@ -3479,7 +3482,7 @@ be translated internally to byte positions, which takes linear time.
A byte position `p` is *valid* for a string `s` if `0 ≤ p ≤ s.rawEndPos` and `p` lies on a UTF-8
character boundary, see `String.Pos.IsValid`.
There is another type, `String.ValidPos`, which bundles the validity predicate. Using `String.ValidPos`
There is another type, `String.Pos`, which bundles the validity predicate. Using `String.Pos`
instead of `String.Pos.Raw` is recommended because it will lead to less error handling and fewer edge cases.
-/
structure String.Pos.Raw where
@@ -3531,7 +3534,7 @@ At runtime, this function takes constant time because the byte length of strings
-/
@[extern "lean_string_utf8_byte_size"]
def String.utf8ByteSize (s : @& String) : Nat :=
s.bytes.size
s.toByteArray.size
/--
A UTF-8 byte position that points at the end of a string, just after the last character.

View File

@@ -93,7 +93,7 @@ An absolute path starts at the root directory or a drive letter. Accessing files
path does not depend on the current working directory.
-/
def isAbsolute (p : FilePath) : Bool :=
pathSeparators.contains p.toString.front || (isWindows && p.toString.length > 1 && p.toString.startValidPos.next?.bind (·.get?) == some ':')
pathSeparators.contains p.toString.front || (isWindows && p.toString.length > 1 && p.toString.startPos.next?.bind (·.get?) == some ':')
/--
A relative path is one that depends on the current working directory for interpretation. Relative
@@ -122,7 +122,7 @@ instance : HDiv FilePath String FilePath where
hDiv p sub := FilePath.join p sub
private def posOfLastSep (p : FilePath) : Option String.Pos.Raw :=
p.toString.revFind pathSeparators.contains
p.toString.revFind? pathSeparators.contains |>.map String.Pos.offset
/--
Returns the parent directory of a path, if there is one.
@@ -173,7 +173,7 @@ Examples:
-/
def fileStem (p : FilePath) : Option String :=
p.fileName.map fun fname =>
match fname.revPosOf '.' with
match fname.revFind? '.' |>.map String.Pos.offset with
| some 0 => fname
| some pos => String.Pos.Raw.extract fname 0 pos
| none => fname
@@ -192,7 +192,7 @@ Examples:
-/
def extension (p : FilePath) : Option String :=
p.fileName.bind fun fname =>
match fname.revPosOf '.' with
match fname.revFind? '.' |>.map String.Pos.offset with
| some 0 => none
| some pos => some <| String.Pos.Raw.extract fname (pos + '.') fname.rawEndPos
| none => none

View File

@@ -10,6 +10,7 @@ public import Init.System.IOError
public import Init.System.FilePath
public import Init.Data.Ord.UInt
import Init.Data.String.TakeDrop
import Init.Data.String.Search
public section
@@ -564,9 +565,20 @@ Waits until any of the tasks in the list has finished, then return its result.
(h : tasks.length > 0 := by exact Nat.zero_lt_succ _) : BaseIO α :=
return tasks[0].get
/--
Given a non-empty list of tasks, wait for the first to complete.
Return the value and the list of remaining tasks.
-/
def waitAny' (tasks : List (Task α)) (h : 0 < tasks.length := by exact Nat.zero_lt_succ _) :
BaseIO (α × List (Task α)) := do
let (i, a) IO.waitAny
(tasks.mapIdx fun i t => t.map (sync := true) fun a => (i, a))
(by simp_all)
return (a, tasks.eraseIdx i)
/--
Returns the number of _heartbeats_ that have occurred during the current thread's execution. The
heartbeat count is the number of small memory allocations performed in a thread.
heartbeat count is the number of "small" memory allocations performed in a thread.
Heartbeats used to implement timeouts that are more deterministic across different hardware.
-/

View File

@@ -9,6 +9,7 @@ prelude
public import Init.System.FilePath
import Init.Data.String.TakeDrop
import Init.Data.String.Modify
import Init.Data.String.Search
public section

View File

@@ -55,6 +55,9 @@ syntax (name := tryTrace) "try?" optConfig : tactic
/-- Helper internal tactic for implementing the tactic `try?`. -/
syntax (name := attemptAll) "attempt_all " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic for implementing the tactic `try?` with parallel execution. -/
syntax (name := attemptAllPar) "attempt_all_par " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic used to implement `evalSuggest` in `try?` -/
syntax (name := tryResult) "try_suggestions " tactic* : tactic

View File

@@ -29,7 +29,7 @@ partial def Loop.forIn {β : Type u} {m : Type u → Type v} [Monad m] (_ : Loop
| ForInStep.yield b => loop b
loop init
instance : ForIn m Loop Unit where
instance [Monad m] : ForIn m Loop Unit where
forIn := Loop.forIn
syntax "repeat " doSeq : doElem

View File

@@ -76,7 +76,7 @@ builtin_initialize externAttr : ParametricAttribute ExternAttrData ←
def getExternAttrData? (env : Environment) (n : Name) : Option ExternAttrData :=
externAttr.getParam? env n
private def parseOptNum : Nat (pattern : String) (it : pattern.ValidPos) Nat pattern.ValidPos × Nat
private def parseOptNum : Nat (pattern : String) (it : pattern.Pos) Nat pattern.Pos × Nat
| 0, _ , it, r => (it, r)
| n+1, pattern, it, r =>
if h : it.IsAtEnd then (it, r)
@@ -86,7 +86,7 @@ private def parseOptNum : Nat → (pattern : String) → (it : pattern.ValidPos)
then parseOptNum n pattern (it.next h) (r*10 + (c.toNat - '0'.toNat))
else (it, r)
def expandExternPatternAux (args : List String) : Nat (pattern : String) (it : pattern.ValidPos) String String
def expandExternPatternAux (args : List String) : Nat (pattern : String) (it : pattern.Pos) String String
| 0, _, _, r => r
| i+1, pattern, it, r =>
if h : it.IsAtEnd then r
@@ -99,7 +99,7 @@ def expandExternPatternAux (args : List String) : Nat → (pattern : String) →
expandExternPatternAux args i pattern it (r ++ args.getD j "")
def expandExternPattern (pattern : String) (args : List String) : String :=
expandExternPatternAux args pattern.length pattern pattern.startValidPos ""
expandExternPatternAux args pattern.length pattern pattern.startPos ""
def mkSimpleFnCall (fn : String) (args : List String) : String :=
fn ++ "(" ++ ((args.intersperse ", ").foldl (·++·) "") ++ ")"

View File

@@ -43,31 +43,26 @@ structure ConfigOptions where
register_builtin_option compiler.small : Nat := {
defValue := 1
group := "compiler"
descr := "(compiler) function declarations with size `≤ small` is inlined even if there are multiple occurrences."
}
register_builtin_option compiler.maxRecInline : Nat := {
defValue := 1
group := "compiler"
descr := "(compiler) maximum number of times a recursive definition tagged with `[inline]` can be recursively inlined before generating an error during compilation."
}
register_builtin_option compiler.maxRecInlineIfReduce : Nat := {
defValue := 16
group := "compiler"
descr := "(compiler) maximum number of times a recursive definition tagged with `[inline_if_reduce]` can be recursively inlined before generating an error during compilation."
}
register_builtin_option compiler.checkTypes : Bool := {
defValue := false
group := "compiler"
descr := "(compiler) perform type compatibility checking after each compiler pass. Note this is not a complete check, and it is used only for debugging purposes. It fails in code that makes heavy use of dependent types."
}
register_builtin_option compiler.extract_closed : Bool := {
defValue := true
group := "compiler"
descr := "(compiler) enable/disable closed term caching"
}

View File

@@ -35,7 +35,7 @@ inductive Value where
A set of values are possible.
-/
| choice (vs : List Value)
deriving Inhabited, Repr
deriving Inhabited
namespace Value
@@ -43,17 +43,36 @@ namespace Value
def maxValueDepth := 8
protected partial def beq : Value Value Bool
| bot, bot => true
| top, top => true
| ctor i1 vs1 , ctor i2 vs2 =>
i1 == i2 && Array.isEqv vs1 vs2 Value.beq
| choice vs1 , choice vs2 =>
let isSubset as bs := as.all (fun a => bs.any fun b => Value.beq a b)
isSubset vs1 vs2 && isSubset vs2 vs1
| _, _ => false
| bot, bot => true
| top, top => true
| ctor i1 vs1 , ctor i2 vs2 =>
i1 == i2 && Array.isEqv vs1 vs2 Value.beq
| choice vs1 , choice vs2 =>
let isSubset as bs := as.all (fun a => bs.any fun b => Value.beq a b)
isSubset vs1 vs2 && isSubset vs2 vs1
| _, _ => false
instance : BEq Value := Value.beq
protected partial def toFormat : Value Format
| bot => ""
| top => ""
| ctor i vs =>
if vs.isEmpty then
format i
else
.paren <| format i ++ .join (vs.toList.map fun v => " " ++ Value.toFormat v)
| choice vs =>
.paren <| .joinSep (vs.map Value.toFormat) " | "
instance : Repr Value where
reprPrec v _ := Value.toFormat v
def inductValOfCtor (ctorName : Name) (env : Environment) : InductiveVal := Id.run do
let some (.ctorInfo info) env.find? ctorName | unreachable!
let some (.inductInfo info) env.find? info.induct | unreachable!
return info
mutual
/--
@@ -62,32 +81,60 @@ is a constructor that is already contained within `vs` try to detect
the difference between these values and merge them accordingly into a
choice node further down the tree.
-/
partial def addChoice (vs : List Value) (v : Value) : List Value :=
partial def addChoice (env : Environment) (vs : List Value) (v : Value) : List Value :=
match vs, v with
| [], v => [v]
| v1@(ctor i1 _ ) :: cs, ctor i2 _ =>
if i1 == i2 then
(merge v1 v) :: cs
(merge env v1 v) :: cs
else
v1 :: addChoice cs v
v1 :: addChoice env cs v
| _, _ => panic! "invalid addChoice"
/--
Merge two values into one. `bot` is the neutral element, `top` the annihilator.
-/
partial def merge (v1 v2 : Value) : Value :=
match v1, v2 with
| bot, v | v, bot => v
| top, _ | _, top => top
| ctor i1 vs1, ctor i2 vs2 =>
if i1 == i2 then
ctor i1 (Array.zipWith merge vs1 vs2)
partial def merge (env : Environment) (v1 v2 : Value) : Value :=
let newValue :=
match v1, v2 with
| bot, v | v, bot => v
| top, _ | _, top => top
| ctor i1 vs1, ctor i2 vs2 =>
if i1 == i2 then
ctor i1 (Array.zipWith (merge env) vs1 vs2)
else
choice [v1, v2]
| choice vs1, choice vs2 =>
choice (vs1.foldl (addChoice env) vs2)
| choice vs, v | v, choice vs =>
choice (addChoice env vs v)
match newValue with
| .top | .bot => newValue
| .choice vs => cleanup vs
| .ctor ctorName .. =>
if eligible newValue && inductHasNumCtors ctorName env 1 then
top
else
choice [v1, v2]
| choice vs1, choice vs2 =>
choice (vs1.foldl addChoice vs2)
| choice vs, v | v, choice vs =>
choice (addChoice vs v)
newValue
where
cleanup (vs : List Value) : Value := Id.run do
if vs.all eligible then
let .ctor ctorName .. := vs.head! | unreachable!
if inductHasNumCtors ctorName env vs.length then
top
else
choice vs
else
choice vs
inductHasNumCtors (ctorName : Name) (env : Environment) (n : Nat) : Bool := Id.run do
let induct := inductValOfCtor ctorName env
n == induct.numCtors
@[inline]
eligible (value : Value) : Bool := Id.run do
let .ctor _ args := value | return false
args.all (· == .top)
end
@@ -106,19 +153,16 @@ where
| remainingDepth + 1 =>
match v with
| ctor i vs =>
let typeName := i.getPrefix
if forbiddenTypes.contains typeName then
let induct := inductValOfCtor i env
if forbiddenTypes.contains induct.name then
top
else
let cont forbiddenTypes' :=
ctor i (vs.map (go · forbiddenTypes' remainingDepth))
match env.find? typeName with
| some (.inductInfo type) =>
if type.isRec then
cont <| forbiddenTypes.insert typeName
else
cont forbiddenTypes
| _ => cont forbiddenTypes
if induct.isRec then
cont <| forbiddenTypes.insert induct.name
else
cont forbiddenTypes
| choice vs =>
let vs := vs.map (go · forbiddenTypes remainingDepth)
if vs.elem top then
@@ -129,7 +173,7 @@ where
/-- Widening operator that guarantees termination in our abstract interpreter. -/
def widening (env : Environment) (v1 v2 : Value) : Value :=
truncate env (merge v1 v2)
truncate env (merge env v1 v2)
/--
Check whether a certain constructor is part of a `Value` by name.

View File

@@ -238,6 +238,13 @@ def mkKey (params : Array Param) (decls : Array CodeDecl) (body : LetValue) : Co
let key := ToExpr.run do
ToExpr.withParams params do
ToExpr.mkLambdaM params ( ToExpr.abstractM body)
let pre e := do
-- beta reduce if possible
if e.isHeadBetaTarget then
return .visit e.headBeta
else
return .continue
let key Meta.MetaM.run' <| Meta.transform (usedLetOnly := true) key pre
return normLevelParams key
open Internalize in

View File

@@ -19,9 +19,10 @@ def findStructCtorInfo? (typeName : Name) : CoreM (Option ConstructorVal) := do
let some (.ctorInfo ctorInfo) := ( getEnv).find? ctorName | return none
return ctorInfo
def mkFieldParamsForCtorType (ctorType : Expr) (numParams : Nat) (numFields : Nat)
: CompilerM (Array Param) := do
let mut type := ctorType
def mkFieldParamsForCtorType (ctorType : Expr) (numParams : Nat) (numFields : Nat) :
CompilerM (Array Param) := do
let mut type Meta.MetaM.run' <| toLCNFType ctorType
type toMonoType type
for _ in *...numParams do
match type with
| .forallE _ _ body _ =>
@@ -31,7 +32,7 @@ def mkFieldParamsForCtorType (ctorType : Expr) (numParams : Nat) (numFields : Na
for _ in *...numFields do
match type with
| .forallE name fieldType body _ =>
let param mkParam name ( toMonoType fieldType) false
let param mkParam name fieldType false
fields := fields.push param
type := body
| _ => unreachable!

View File

@@ -29,8 +29,8 @@ where finally
simp [i, UInt32.lt_iff_toNat_lt, this]; omega
def mangleAux (s : String) (pos : s.ValidPos) (r : String) : String :=
if h : pos = s.endValidPos then r else
def mangleAux (s : String) (pos : s.Pos) (r : String) : String :=
if h : pos = s.endPos then r else
let c := pos.get h
let pos := pos.next h
if c.isAlpha || c.isDigit then
@@ -46,16 +46,16 @@ def mangleAux (s : String) (pos : s.ValidPos) (r : String) : String :=
termination_by pos
public def mangle (s : String) : String :=
mangleAux s s.startValidPos ""
mangleAux s s.startPos ""
end String
namespace Lean
def checkLowerHex : Nat (s : String) s.ValidPos Bool
def checkLowerHex : Nat (s : String) s.Pos Bool
| 0, _, _ => true
| k + 1, s, pos =>
if h : pos = s.endValidPos then
if h : pos = s.endPos then
false
else
let ch := pos.get h
@@ -69,19 +69,19 @@ def fromHex? (c : Char) : Option Nat :=
some (c.val - 87).toNat
else none
def parseLowerHex? (k : Nat) (s : String) (p : s.ValidPos) (acc : Nat) :
Option (s.ValidPos × Nat) :=
def parseLowerHex? (k : Nat) (s : String) (p : s.Pos) (acc : Nat) :
Option (s.Pos × Nat) :=
match k with
| 0 => some (p, acc)
| k + 1 =>
if h : p = s.endValidPos then
if h : p = s.endPos then
none
else
match fromHex? (p.get h) with
| some d => parseLowerHex? k s (p.next h) (acc <<< 4 ||| d)
| none => none
theorem lt_of_parseLowerHex?_eq_some {k : Nat} {s : String} {p q : s.ValidPos} {acc : Nat}
theorem lt_of_parseLowerHex?_eq_some {k : Nat} {s : String} {p q : s.Pos} {acc : Nat}
{r : Nat} (hk : 0 < k) : parseLowerHex? k s p acc = some (q, r) p < q := by
fun_induction parseLowerHex? with
| case1 => simp at hk
@@ -89,10 +89,10 @@ theorem lt_of_parseLowerHex?_eq_some {k : Nat} {s : String} {p q : s.ValidPos} {
| case3 p acc k h d x ih =>
match k with
| 0 => simpa [parseLowerHex?] using fun h _ => h p.lt_next
| k + 1 => exact fun h => String.ValidPos.lt_trans p.lt_next (ih (by simp) h)
| k + 1 => exact fun h => String.Pos.lt_trans p.lt_next (ih (by simp) h)
| case4 => simp
def checkDisambiguation (s : String) (p : s.ValidPos) : Bool :=
def checkDisambiguation (s : String) (p : s.Pos) : Bool :=
if h : _ then
let b := p.get h
if b = '_' then
@@ -111,8 +111,8 @@ termination_by p
def needDisambiguation (prev : Name) (next : String) : Bool :=
(match prev with
| .str _ s => h, (s.endValidPos.prev h).get (by simp) = '_'
| _ => false) || checkDisambiguation next next.startValidPos
| .str _ s => h, (s.endPos.prev h).get (by simp) = '_'
| _ => false) || checkDisambiguation next next.startPos
def Name.mangleAux : Name String
| Name.anonymous => ""
@@ -120,7 +120,7 @@ def Name.mangleAux : Name → String
let m := String.mangle s
match p with
| Name.anonymous =>
if checkDisambiguation m m.startValidPos then "00" ++ m else m
if checkDisambiguation m m.startPos then "00" ++ m else m
| p =>
let m1 := mangleAux p
m1 ++ (if needDisambiguation p m then "_00" else "_") ++ m
@@ -149,9 +149,9 @@ public def mkPackageSymbolPrefix (pkg? : Option PkgId) : String :=
pkg?.elim "l_" (s!"lp_{·.mangle}_")
-- assumes `s` has been generated `Name.mangle n ""`
def Name.demangleAux (s : String) (p₀ : s.ValidPos) (res : Name)
def Name.demangleAux (s : String) (p₀ : s.Pos) (res : Name)
(acc : String) (ucount : Nat) : Name :=
if hp₀ : p₀ = s.endValidPos then res.str (acc.pushn '_' (ucount / 2)) else
if hp₀ : p₀ = s.endPos then res.str (acc.pushn '_' (ucount / 2)) else
let ch := p₀.get hp₀
let p := p₀.next hp₀
if ch = '_' then demangleAux s p res acc (ucount + 1) else
@@ -159,7 +159,7 @@ def Name.demangleAux (s : String) (p₀ : s.ValidPos) (res : Name)
demangleAux s p res (acc.pushn '_' (ucount / 2) |>.push ch) 0
else if ch.isDigit then
let res := res.str (acc.pushn '_' (ucount / 2))
if h : ch = '0' h : p s.endValidPos, p.get h = '0' then
if h : ch = '0' h : p s.endPos, p.get h = '0' then
demangleAux s (p.next h.2.1) res "" 0
else
decodeNum s p res (ch.val - 48).toNat
@@ -167,40 +167,40 @@ def Name.demangleAux (s : String) (p₀ : s.ValidPos) (res : Name)
match ch, h₁ : parseLowerHex? 2 s p 0 with
| 'x', some (q, v) =>
let acc := acc.pushn '_' (ucount / 2)
have : p₀ < q := String.ValidPos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₁)
have : p₀ < q := String.Pos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₁)
demangleAux s q res (acc.push (Char.ofNat v)) 0
| _, _ =>
match ch, h₂ : parseLowerHex? 4 s p 0 with
| 'u', some (q, v) =>
let acc := acc.pushn '_' (ucount / 2)
have : p₀ < q := String.ValidPos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₂)
have : p₀ < q := String.Pos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₂)
demangleAux s q res (acc.push (Char.ofNat v)) 0
| _, _ =>
match ch, h₃ : parseLowerHex? 8 s p 0 with
| 'U', some (q, v) =>
let acc := acc.pushn '_' (ucount / 2)
have : p₀ < q := String.ValidPos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₃)
have : p₀ < q := String.Pos.lt_trans p₀.lt_next (lt_of_parseLowerHex?_eq_some (by decide) h₃)
demangleAux s q res (acc.push (Char.ofNat v)) 0
| _, _ => demangleAux s p (res.str acc) ("".pushn '_' (ucount / 2) |>.push ch) 0
termination_by p₀
where
decodeNum (s : String) (p : s.ValidPos) (res : Name) (n : Nat) : Name :=
if h : p = s.endValidPos then res.num n else
decodeNum (s : String) (p : s.Pos) (res : Name) (n : Nat) : Name :=
if h : p = s.endPos then res.num n else
let ch := p.get h
let p := p.next h
if ch.isDigit then
decodeNum s p res (n * 10 + (ch.val - 48).toNat)
else -- assume ch = '_'
let res := res.num n
if h : p = s.endValidPos then res else
if h : p = s.endPos then res else
nameStart s (p.next h) res -- assume s.get' p h = '_'
termination_by p
nameStart (s : String) (p : s.ValidPos) (res : Name) : Name :=
if h : p = s.endValidPos then res else
nameStart (s : String) (p : s.Pos) (res : Name) : Name :=
if h : p = s.endPos then res else
let ch := p.get h
let p := p.next h
if ch.isDigit then
if h : ch = '0' h : p s.endValidPos, p.get h = '0' then
if h : ch = '0' h : p s.endPos, p.get h = '0' then
demangleAux s (p.next h.2.1) res "" 0
else
decodeNum s p res (ch.val - 48).toNat
@@ -212,7 +212,7 @@ where
/-- Assuming `s` has been produced by `Name.mangle _ ""`, return the original name. -/
public def Name.demangle (s : String) : Name :=
demangleAux.nameStart s s.startValidPos .anonymous
demangleAux.nameStart s s.startPos .anonymous
/--
Returns the demangled version of `s`, if it's the result of `Name.mangle _ ""`. Otherwise returns

View File

@@ -14,7 +14,6 @@ namespace Lean.Compiler
register_builtin_option compiler.check : Bool := {
defValue := false
group := "compiler"
descr := "type check code after each compiler step (this is useful for debugging purses)"
}

View File

@@ -15,13 +15,11 @@ public section
namespace Lean
register_builtin_option diagnostics : Bool := {
defValue := false
group := "diagnostics"
descr := "collect diagnostic information"
}
register_builtin_option diagnostics.threshold : Nat := {
defValue := 20
group := "diagnostics"
descr := "only diagnostic counters above this threshold are reported by the definitional equality"
}
@@ -437,6 +435,9 @@ def mkFreshUserName (n : Name) : CoreM Name :=
| Except.error (Exception.internal id _) => throw <| IO.userError <| "internal exception #" ++ toString id.idx
| Except.ok a => return a
@[inline] def CoreM.toIO' (x : CoreM α) (ctx : Context) (s : State) : IO α :=
(·.1) <$> x.toIO ctx s
-- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]`
protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α :=
controlAt CoreM fun runInBase => withIncRecDepth (runInBase x)
@@ -454,7 +455,6 @@ the exception has been thrown.
register_builtin_option debug.moduleNameAtTimeout : Bool := {
defValue := true
group := "debug"
descr := "include module name in deterministic timeout error messages.\nRemark: we set this option to false to increase the stability of our test suite"
}
@@ -522,7 +522,7 @@ instance : MonadLog CoreM where
if ( read).suppressElabErrors then
-- discard elaboration errors, except for a few important and unlikely misleading ones, on
-- parse error
unless msg.data.hasTag (· matches `Elab.synthPlaceholder | `Tactic.unsolvedGoals | `trace) do
unless msg.data.hasTag (· matches `Elab.synthPlaceholder | `Tactic.unsolvedGoals | `lean.inductionWithNoAlts._namedError | `trace) do
return
let ctx read
@@ -560,7 +560,6 @@ def wrapAsync {α : Type} (act : α → CoreM β) (cancelTk? : Option IO.CancelT
/-- Option for capturing output to stderr during elaboration. -/
register_builtin_option stderrAsMessages : Bool := {
defValue := true
group := "server"
descr := "(server) capture output to the Lean stderr channel (such as from `dbg_trace`) during elaboration of a command as a diagnostic message"
}

View File

@@ -110,7 +110,7 @@ def all (p : α → β → Bool) : AssocList α β → Bool
| ForInStep.yield d => loop d es
loop init as
instance : ForIn m (AssocList α β) (α × β) where
instance [Monad m] : ForIn m (AssocList α β) (α × β) where
forIn := AssocList.forIn
end Lean.AssocList

View File

@@ -32,11 +32,11 @@ public def levenshtein (str1 str2 : String) (cutoff : Nat) : Option Nat := Id.ru
for h : i in [0:v0.size] do
v0 := v0.set i i
let mut iter1 := str1.startValidPos
let mut iter1 := str1.startPos
let mut i := 0
while h1 : ¬iter1.IsAtEnd do
v1 := v1.set 0 (i+1)
let mut iter2 := str2.startValidPos
let mut iter2 := str2.startPos
let mut j : Fin (len2 + 1) := 0
while h2 : ¬iter2.IsAtEnd do
let j' : Fin _ := j + 1

View File

@@ -8,6 +8,7 @@ module
prelude
public import Lean.Data.Format
public import Lean.Data.Json.Basic
import Init.Data.String.Search
public section

View File

@@ -183,7 +183,7 @@ def updateSyntax (m : KVMap) (k : Name) (f : Syntax → Syntax) : KVMap :=
(kv : KVMap) (init : δ) (f : Name × DataValue δ m (ForInStep δ)) : m δ :=
forIn kv.entries init f
instance : ForIn m KVMap (Name × DataValue) where
instance [Monad m] : ForIn m KVMap (Name × DataValue) where
forIn := KVMap.forIn
def subsetAux : List (Name × DataValue) KVMap Bool

View File

@@ -188,7 +188,7 @@ instance : FromJson RefInfo where
@[expose] def ModuleRefs := Std.TreeMap RefIdent RefInfo
deriving EmptyCollection
instance : ForIn m ModuleRefs (RefIdent × RefInfo) where
instance [Monad m] : ForIn m ModuleRefs (RefIdent × RefInfo) where
forIn map init f :=
let map : Std.TreeMap RefIdent RefInfo := map
forIn map init f

View File

@@ -10,6 +10,7 @@ prelude
public import Lean.Data.Lsp.Basic
meta import Lean.Data.Json
public import Lean.Expr
public import Init.Data.String.Search
public section

View File

@@ -9,6 +9,7 @@ module
prelude
public import Lean.Data.Lsp.BasicAux
public import Lean.DeclarationRange
import Init.Data.String.Search
public section

View File

@@ -10,6 +10,7 @@ public import Init.Data.Ord.Basic
import Init.Data.String.TakeDrop
import Init.Data.Ord.String
import Init.Data.Ord.UInt
import Init.Data.String.Search
public section
namespace Lean

View File

@@ -35,7 +35,7 @@ def contains (m : NameMap α) (n : Name) : Bool := Std.TreeMap.contains m n
def find? (m : NameMap α) (n : Name) : Option α := Std.TreeMap.get? m n
instance : ForIn m (NameMap α) (Name × α) :=
instance [Monad m] : ForIn m (NameMap α) (Name × α) :=
inferInstanceAs (ForIn _ (Std.TreeMap _ _ _) ..)
/-- `filter f m` returns the `NameMap` consisting of all
@@ -52,7 +52,7 @@ instance : EmptyCollection NameSet := ⟨empty⟩
instance : Inhabited NameSet := empty
def insert (s : NameSet) (n : Name) : NameSet := Std.TreeSet.insert s n
def contains (s : NameSet) (n : Name) : Bool := Std.TreeSet.contains s n
instance : ForIn m NameSet Name :=
instance [Monad m] : ForIn m NameSet Name :=
inferInstanceAs (ForIn _ (Std.TreeSet _ _) ..)
/-- The union of two `NameSet`s. -/

View File

@@ -20,16 +20,27 @@ def Options.empty : Options := {}
instance : Inhabited Options where
default := {}
instance : ToString Options := inferInstanceAs (ToString KVMap)
instance : ForIn m Options (Name × DataValue) := inferInstanceAs (ForIn _ KVMap _)
instance [Monad m] : ForIn m Options (Name × DataValue) := inferInstanceAs (ForIn _ KVMap _)
instance : BEq Options := inferInstanceAs (BEq KVMap)
structure OptionDecl where
name : Name
declName : Name := by exact decl_name%
defValue : DataValue
group : String := ""
descr : String := ""
deriving Inhabited
def OptionDecl.fullDescr (self : OptionDecl) : String := Id.run do
let mut descr := self.descr
if (`backward).isPrefixOf self.name then
unless descr.isEmpty do
descr := descr ++ "\n\n"
descr := descr ++ "\
This is a backwards compatibility option, intended to help migrating to new Lean releases. \
It may be removed without further notice 6 months after their introduction. \
Please report an issue if you rely on this option."
pure descr
@[expose] def OptionDecls := NameMap OptionDecl
instance : Inhabited OptionDecls := ({} : NameMap OptionDecl)
@@ -112,7 +123,6 @@ namespace Option
protected structure Decl (α : Type) where
defValue : α
group : String := ""
descr : String := ""
protected def get? [KVMap.Value α] (opts : Options) (opt : Lean.Option α) : Option α :=
@@ -133,9 +143,9 @@ protected def setIfNotSet [KVMap.Value α] (opts : Options) (opt : Lean.Option
protected def register [KVMap.Value α] (name : Name) (decl : Lean.Option.Decl α) (ref : Name := by exact decl_name%) : IO (Lean.Option α) := do
registerOption name {
name
declName := ref
defValue := KVMap.Value.toDataValue decl.defValue
group := decl.group
descr := decl.descr
}
return { name := name, defValue := decl.defValue }

View File

@@ -252,7 +252,7 @@ partial def forInAux {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
| ForInStep.yield bNew => b := bNew
return b
instance : ForIn m (PersistentArray α) α where
instance [Monad m] : ForIn m (PersistentArray α) α where
forIn := PersistentArray.forIn
@[specialize] partial def findSomeMAux (f : α m (Option β)) : PersistentArrayNode α m (Option β)

View File

@@ -304,7 +304,7 @@ protected def forIn {_ : BEq α} {_ : Hashable α} [Monad m]
match result with
| .ok s | .error s => pure s
instance {_ : BEq α} {_ : Hashable α} : ForIn m (PersistentHashMap α β) (α × β) where
instance {_ : BEq α} {_ : Hashable α} [Monad m] : ForIn m (PersistentHashMap α β) (α × β) where
forIn := PersistentHashMap.forIn
end

View File

@@ -61,7 +61,7 @@ protected def forIn {_ : BEq α} {_ : Hashable α} [Monad m]
(s : PersistentHashSet α) (init : σ) (f : α σ m (ForInStep σ)) : m σ := do
PersistentHashMap.forIn s.set init fun p s => f p.1 s
instance {_ : BEq α} {_ : Hashable α} : ForIn m (PersistentHashSet α) α where
instance {_ : BEq α} {_ : Hashable α} [Monad m] : ForIn m (PersistentHashSet α) α where
forIn := PersistentHashSet.forIn
end PersistentHashSet

View File

@@ -295,7 +295,7 @@ def isSingleton (t : RBMap α β cmp) : Bool :=
@[inline] protected def forIn [Monad m] (t : RBMap α β cmp) (init : σ) (f : (α × β) σ m (ForInStep σ)) : m σ :=
t.val.forIn init (fun a b acc => f (a, b) acc)
instance : ForIn m (RBMap α β cmp) (α × β) where
instance [Monad m] : ForIn m (RBMap α β cmp) (α × β) where
forIn := RBMap.forIn
@[inline] def isEmpty : RBMap α β cmp Bool

View File

@@ -48,7 +48,7 @@ variable {α : Type u} {β : Type v} {cmp : αα → Ordering}
@[inline] protected def forIn [Monad m] (t : RBTree α cmp) (init : σ) (f : α σ m (ForInStep σ)) : m σ :=
t.val.forIn init (fun a _ acc => f a acc)
instance : ForIn m (RBTree α cmp) α where
instance [Monad m] : ForIn m (RBTree α cmp) α where
forIn := RBTree.forIn
@[inline] def isEmpty (t : RBTree α cmp) : Bool :=

View File

@@ -80,10 +80,10 @@ def forM [Monad m] (s : SMap α β) (f : α → β → m PUnit) : m PUnit := do
s.map₁.forM f
s.map₂.forM f
instance : ForM m (SMap α β) (α × β) where
instance [Monad m] : ForM m (SMap α β) (α × β) where
forM s f := forM s fun x y => f (x, y)
instance : ForIn m (SMap α β) (α × β) where
instance [Monad m] : ForIn m (SMap α β) (α × β) where
forIn := ForM.forIn
/-- Move from stage 1 into stage 2. -/

View File

@@ -8,6 +8,7 @@ module
prelude
public import Std.Internal.Parsec
public import Lean.Data.Xml.Basic
import Init.Data.String.Search
public section

View File

@@ -66,7 +66,6 @@ deriving Inhabited
register_builtin_option doc.verso : Bool := {
defValue := false,
descr := "whether to use Verso syntax in docstrings"
group := "doc"
}
private builtin_initialize builtinDocStrings : IO.Ref (NameMap String) IO.mkRef {}

View File

@@ -106,7 +106,7 @@ def rewriteManualLinksCore (s : String) : Id (Array (Lean.Syntax.Range × String
let scheme := "lean-manual://"
let mut out := ""
let mut errors := #[]
let mut iter := s.startValidPos
let mut iter := s.startPos
while h : ¬iter.IsAtEnd do
let c := iter.get h
let pre := iter
@@ -155,7 +155,7 @@ where
/--
Returns `true` if `goal` is a prefix of the string at the position pointed to by `iter`.
-/
lookingAt (goal : String) {s : String} (iter : s.ValidPos) : Bool :=
lookingAt (goal : String) {s : String} (iter : s.Pos) : Bool :=
String.Pos.Raw.substrEq s iter.offset goal 0 goal.rawEndPos.byteIdx
/--

View File

@@ -151,7 +151,7 @@ public instance : MarkdownBlock i Empty where
private def escape (s : String) : String := Id.run do
let mut s' := ""
let mut iter := s.startValidPos
let mut iter := s.startPos
while h : ¬iter.IsAtEnd do
let c := iter.get h
iter := iter.next h
@@ -165,7 +165,7 @@ where
private def quoteCode (str : String) : String := Id.run do
let mut longest := 0
let mut current := 0
let mut iter := str.startValidPos
let mut iter := str.startPos
while h : ¬iter.IsAtEnd do
let c := iter.get h
iter := iter.next h
@@ -183,7 +183,7 @@ where
go : List (Inline i) String × Inline i
| [] => ("", .empty)
| .text s :: more =>
if s.all (·.isWhitespace) then
if s.all Char.isWhitespace then
let (pre, post) := go more
(s ++ pre, post)
else
@@ -198,7 +198,7 @@ where
go : List (Inline i) Inline i × String
| [] => (.empty, "")
| .text s :: more =>
if s.all (·.isWhitespace) then
if s.all Char.isWhitespace then
let (pre, post) := go more
(pre, post ++ s)
else
@@ -273,7 +273,7 @@ public instance [MarkdownInline i] : ToMarkdown (Inline i) where
private def quoteCodeBlock (indent : Nat) (str : String) : String := Id.run do
let mut longest := 2
let mut current := 0
let mut iter := str.startValidPos
let mut iter := str.startPos
let mut out := ""
while h : ¬iter.IsAtEnd do
let c := iter.get h

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