Compare commits

..

127 Commits

Author SHA1 Message Date
Kim Morrison
d6fc6e6b45 perf: parallelize rw? tactic
Use `MetaM.parIterWithCancel` to try all candidate rewrites in parallel
while preserving deterministic result ordering. When an rfl-closeable
result is found (and `stopAtRfl` is true), or the maximum number of
results is reached, remaining tasks are cancelled.

This removes the old sequential `takeListAux` implementation along with
the heartbeat-based early termination and `RewriteResultConfig` structure.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 18:39:38 +11:00
Kim Morrison
272f0f5db3 feat: add chunked variants of parIterWithCancel
Add `parIterWithCancelChunked` functions for CoreM, MetaM, TermElabM, and TacticM that support chunking jobs into groups to reduce task creation overhead.

The original `parIterWithCancel` functions remain unchanged for backward compatibility. The new chunked variants accept `maxTasks` and `minChunkSize` parameters to control parallelism.

This enables PRs that use `parIterWithCancel` (like parallel library search and rewrites) to benefit from chunking by switching to the new `parIterWithCancelChunked` function with `maxTasks := 128`.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 18:35:46 +11:00
Kim Morrison
1d3fda4130 feat: add chunking support to par and par' in Lean.Elab.Parallel
This PR adds optional chunking support to the `par` and `par'` functions in
`Lean.Elab.Parallel` for CoreM, MetaM, TermElabM, and TacticM. This reduces
task creation overhead when there are many small jobs by grouping them into
chunks that run sequentially within each parallel task.

New optional parameters:
- `maxTasks : Nat := 0` - Maximum number of parallel tasks (0 = no limit)
- `minChunkSize : Nat := 1` - Minimum jobs per chunk

Example: With 1000 jobs and `maxTasks := 128, minChunkSize := 8`:
- Chunk size = max(8, ceil(1000/128)) = 8
- Creates ~125 parallel tasks instead of 1000

Default behavior (maxTasks = 0) is unchanged - one task per job.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 18:22:40 +11:00
Joachim Breitner
5bd331e85d perf: kernel-optimize Mon.mul (#11422)
This PR uses a kernel-reduction optimized variant of Mon.mul in grind.
2025-11-30 23:59:59 +00:00
Jon Eugster
856825a4d2 fix: typo in docstring of #guard_msgs (#11432)
This PR fixes a typo in the docstring of `#guard_mgs`.

Closes #11431
2025-11-30 14:44:35 +00:00
Leonardo de Moura
16508196e0 doc: add docstring for grind_pattern command (#11429)
This PR documents the `grind_pattern` command for manually selecting
theorem instantiation patterns, including multi-patterns and the
constraint system (`=/=`, `=?=`, `size`, `depth`, `is_ground`,
`is_value`, `is_strict_value`, `gen`, `max_insts`, `guard`, `check`).
2025-11-30 01:01:48 +00:00
Sebastian Ullrich
4eba5ea96d fix: shake: only record non-builtin simprocs (#11344) 2025-11-29 15:58:29 +00:00
Leonardo de Moura
075f1d66eb feat: guard and check in grind_pattern (#11428)
This PR implements support for **guards** in `grind_pattern`. The new
feature provides additional control over theorem instantiation. For
example, consider the following monotonicity theorem:

```lean
opaque f : Nat → Nat
theorem fMono : x ≤ y → f x ≤ f y := ...
```

We can use `grind_pattern` to instruct `grind` to instantiate the
theorem for every pair `f x` and `f y` occurring in the goal:

```lean
grind_pattern fMono => f x, f y
```

Then we can automatically prove the following simple example using
`grind`:

```lean
/--
trace: [grind.ematch.instance] fMono: f a ≤ b → f (f a) ≤ f b
[grind.ematch.instance] fMono: f a ≤ c → f (f a) ≤ f c
[grind.ematch.instance] fMono: f a ≤ a → f (f a) ≤ f a
[grind.ematch.instance] fMono: f a ≤ f (f a) → f (f a) ≤ f (f (f a))
[grind.ematch.instance] fMono: f a ≤ f a → f (f a) ≤ f (f a)
[grind.ematch.instance] fMono: f (f a) ≤ b → f (f (f a)) ≤ f b
[grind.ematch.instance] fMono: f (f a) ≤ c → f (f (f a)) ≤ f c
[grind.ematch.instance] fMono: f (f a) ≤ a → f (f (f a)) ≤ f a
[grind.ematch.instance] fMono: f (f a) ≤ f (f a) → f (f (f a)) ≤ f (f (f a))
[grind.ematch.instance] fMono: f (f a) ≤ f a → f (f (f a)) ≤ f (f a)
[grind.ematch.instance] fMono: a ≤ b → f a ≤ f b
[grind.ematch.instance] fMono: a ≤ c → f a ≤ f c
[grind.ematch.instance] fMono: a ≤ a → f a ≤ f a
[grind.ematch.instance] fMono: a ≤ f (f a) → f a ≤ f (f (f a))
[grind.ematch.instance] fMono: a ≤ f a → f a ≤ f (f a)
[grind.ematch.instance] fMono: c ≤ b → f c ≤ f b
[grind.ematch.instance] fMono: c ≤ c → f c ≤ f c
[grind.ematch.instance] fMono: c ≤ a → f c ≤ f a
[grind.ematch.instance] fMono: c ≤ f (f a) → f c ≤ f (f (f a))
[grind.ematch.instance] fMono: c ≤ f a → f c ≤ f (f a)
[grind.ematch.instance] fMono: b ≤ b → f b ≤ f b
[grind.ematch.instance] fMono: b ≤ c → f b ≤ f c
[grind.ematch.instance] fMono: b ≤ a → f b ≤ f a
[grind.ematch.instance] fMono: b ≤ f (f a) → f b ≤ f (f (f a))
[grind.ematch.instance] fMono: b ≤ f a → f b ≤ f (f a)
-/
#guard_msgs in
example : f b = f c → a ≤ f a → f (f a) ≤ f (f (f a)) := by
  set_option trace.grind.ematch.instance true in
  grind
```

However, many unnecessary theorem instantiations are generated.

With the new `guard` feature, we can instruct `grind` to instantiate the
theorem **only if** `x ≤ y` is already known to be true in the current
`grind` state:

```lean
grind_pattern fMono => f x, f y where
  guard x ≤ y
  x =/= y
```

If we run the example again, only three instances are generated:

```lean
/--
trace: [grind.ematch.instance] fMono: a ≤ f a → f a ≤ f (f a)
[grind.ematch.instance] fMono: f a ≤ f (f a) → f (f a) ≤ f (f (f a))
[grind.ematch.instance] fMono: a ≤ f (f a) → f a ≤ f (f (f a))
-/
#guard_msgs in
example : f b = f c → a ≤ f a → f (f a) ≤ f (f (f a)) := by
  set_option trace.grind.ematch.instance true in
  grind
```

Note that `guard` does **not** check whether the expression is
*implied*. It only checks whether the expression is *already known* to
be true in the current `grind` state. If this fact is eventually
learned, the theorem will be instantiated.

If you want `grind` to check whether the expression is implied, you
should use:

```lean
grind_pattern fMono => f x, f y where
  check x ≤ y
  x =/= y
```

Remark: we can use multiple `guard`/`check`s in a `grind_pattern`
command.
2025-11-29 03:56:53 +00:00
Sebastian Ullrich
3f05179fdb chore: CI: fix Linux release jobs (#11424) 2025-11-28 16:27:32 +00:00
Garmelon
a0d0abcdc5 chore: update and add benchmark metrics (#11420)
This PR adds per-module `.ilean` and `.olean` file size metrics, global
and per-module cycle counting, and adds back `lean --stat`-based
metrics. It also renames some `size/*` metrics to get rid of the name
`stdlib`.
2025-11-28 14:40:43 +00:00
Sebastian Ullrich
5ef1c8ddfc chore: cadical should never be built with fsanitize (#11423) 2025-11-28 14:36:39 +00:00
Kim Morrison
30d88c83b3 chore: restore set_library_suggestions tests after update-stage0 2025-11-29 01:08:47 +11:00
Kim Morrison
3e370600e5 chore: update stage0 2025-11-29 01:08:47 +11:00
Kim Morrison
bb04169674 feat: set_library_suggestions makes auxiliary def, rather than storing Syntax 2025-11-29 01:08:47 +11:00
Kim Morrison
eb8298432e doc: clarify how to trigger automatic stage0 updates (#11413)
This PR clarifies the bootstrap documentation to explain that to trigger
the automatic stage0 update mechanism, you should modify
`stage0/src/stdlib_flags.h` (not `src/stdlib_flags.h`). The existing
text was ambiguous about which file to modify.

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-28 12:56:59 +00:00
Sebastian Ullrich
dc5abb0500 chore: CI: disable additional fsanitize test 2025-11-28 13:04:30 +00:00
Lean stage0 autoupdater
8ff3adaa01 chore: update stage0 2025-11-28 11:52:39 +00:00
Kim Morrison
109ac9520c fix: revert "set_library_suggestions makes auxiliary def (#11396)" (#11417)
This PR reverts https://github.com/leanprover/lean4/pull/11396, which
changed `set_library_suggestions` to create an auxiliary definition
marked with `@[library_suggestions]`, rather than storing `Syntax`
directly in the environment extension.

It wasn't tested properly.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-28 11:03:17 +00:00
Sebastian Ullrich
b19468f81c chore: CI: disable problematic fsanitize tests (#11415) 2025-11-28 10:25:58 +00:00
Kim Morrison
958aa713fa fix: rename ring variable indices in grind cancel_var proofs (#11410)
This PR fixes a kernel type mismatch error in grind's denominator
cleanup feature. When generating proofs involving inverse numerals (like
`2⁻¹`), the proof context is compacted to only include variables
actually used. This involves renaming variable indices - e.g., if
original indices were `{0: r, 1: 2⁻¹}` and only `2⁻¹` is used, it gets
renamed to index 0.

The bug was that polynomials were correctly renamed via `varRename`, but
the variable index `x` stored in `cancelDen` constraints was passed
directly to the proof without renaming, causing a mismatch between the
polynomial's variable references and the theorem's variable argument.

Added `ringVarDecls` to track ring variable indices that need renaming,
similar to how `ringPolyDecls` tracks polynomials. The `mkRingContext`
function now also renames these variable indices.

See zulip discussion at [#nightly-testing > Mathlib status updates @
💬](https://leanprover.zulipchat.com/#narrow/channel/428973-nightly-testing/topic/Mathlib.20status.20updates/near/560575295).

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-28 04:43:46 +00:00
Lean stage0 autoupdater
fc36b1b796 chore: update stage0 2025-11-28 05:17:56 +00:00
Kim Morrison
157fbd08b4 feat: set_library_suggestions makes auxiliary def, rather than storing Syntax (#11396)
This PR changes `set_library_suggestions` to create an auxiliary
definition marked with `@[library_suggestions]`, rather than storing
`Syntax` directly in the environment extension. This enables better
persistence and consistency of library suggestions across modules.

The change requires a stage0 update before tests can be restored. After
CI updates stage0, a follow-up PR will restore the test cases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-28 04:36:31 +00:00
Kim Morrison
6a900dc9d6 fix: strip nested mdata in grind preprocessing (#11412)
This PR fixes an issue where `grind` would fail after multiple
`norm_cast`
calls with the error "unexpected metadata found during internalization".

The `norm_cast` tactic adds mdata nodes to expressions, and when called
multiple times it creates nested mdata. The `eraseIrrelevantMData`
preprocessing function was using `.continue e` when stripping mdata,
which causes `Core.transform` to reconstruct the mdata node around the
visited children. By changing to `.visit e`, the inner expression is
passed back to `pre` for another round of processing, allowing all
nested mdata layers to be stripped.

Closes #11411

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-28 04:36:26 +00:00
Henrik Böving
b21cef37e4 perf: sort before elim dead branches (#11366)
This PR sorts the declarations fed into ElimDeadBranches in increasing
size. This can improve performance when we are dealing with a lot of
iterations.

The motivation for this change is as follows. Currently the algorithm
for doing one step of abstract interpretation is:
```
for decl in scc do
  interpDecl
  if summaryChanged decl then
    return true
return false
```
whenever we return true we run another step. Now suppose we are in a
situation where we have an SCC with one big decl in the front and then
`n` small ones afterwards. For each time that the small ones change
their summary, we will re-run analysis of the big one in the front.
Currently the ordering is basically at "random" based on how other
compilers inject things into the SCC. This change ensures the behavior
is consistent and at least somewhat intelligent. By putting the small
declarations first, whenever we trigger a rerun of the loop we bias
analyzing the small declarations first, thus decreasing run time.

Note that this change does not have much effect on the current pipeline
because: We usually construct the SCCs in a way such that small ones
happen to be in front anyways. However, with upcomping changes on
specialization this is about to change.
2025-11-27 22:21:06 +00:00
Leonardo de Moura
9a5a9c2709 feat: add is_value and is_strict_value grind_pattern constraints (#11409)
This PR implements support for the `grind_pattern` constraints
`is_value` and `is_strict_value`.
2025-11-27 21:02:49 +00:00
Sebastian Ullrich
6eeb215e8f chore: CI: enable leak sanitizer again (#11339) 2025-11-27 18:32:35 +00:00
Leonardo de Moura
16740a1540 feat: some grind_pattern constraints (#11405)
This PR implements the following `grind_pattern` constraints:
```lean
grind_pattern fax => f x  where
  depth x < 2

grind_pattern fax => f x where
  is_ground x

grind_pattern fax => f x where
  size x < 5

grind_pattern fax => f x where
  gen < 2

grind_pattern fax => f x where
  max_insts < 4

grind_pattern gax => g as where
  as =?= _ :: _
```
2025-11-27 18:05:47 +00:00
Wojciech Różowski
799d594400 feat: add difference on DHashMap/HashMap/HashSet (#11212)
This PR adds support for difference operation for
`DHashMap`/`HashMap`/`HashSet` and proves several lemmas about it.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-27 13:08:30 +00:00
Henrik Böving
586ea55c0d fix: enforce choice invariant in ElimDeadBranches (#11398)
This PR fixes a broken invariant in the choice nodes of
ElimDeadBranches.

Closes: #11389 and #11393
2025-11-27 11:41:43 +00:00
Leonardo de Moura
a4f9a793d9 feat: new constraints in grind_pattern (#11391)
This PR implements new kinds of constraints for the `grind_pattern`
command. These constraints allow users to control theorem instantiation
in `grind`.
It requires a manual `update-stage0` because the change affects the
`.olean` format, and the PR fails without it.
2025-11-26 21:13:14 -08:00
Kim Morrison
490d714486 chore: run Mathlib's verify_version_tags.py in release_checklist.py (#11392)
Not tested carefully: I will shake out any problems during the next
release. This script would have detected the mistakes I made in recent
releases of `v4.24.1` / `v4.25.1` and `v4.25.2`. (And #11374 would have
prevented these mistakes.)
2025-11-27 04:10:43 +00:00
Kim Morrison
9220ee3b2d chore: CI validates release tag against CMakeLists.txt (#11374)
I just made this mistake again (twice!) and had to redo `v4.24.1` and
`v4.25.2`. Let's prevent it from happening.
2025-11-27 03:57:49 +00:00
Lean stage0 autoupdater
130d3cbb57 chore: update stage0 2025-11-27 03:22:23 +00:00
Lean stage0 autoupdater
ae5db72cbe chore: update stage0 2025-11-27 02:33:54 +00:00
MJ141592
3b43156650 doc: correct grammar error in array indexing panic message (#11368)
This PR corrects a grammar error in a docstring in the GetElem file for
array indexing.
2025-11-26 23:09:38 +00:00
Théophile Wallez
644a217e60 fix: typo in documentation of leOfOrd (#11387)
This PR fixes a typo in the documentation of `leOfOrd`.
2025-11-26 23:08:36 +00:00
Sebastian Ullrich
17e8765bdc fix: miscompilation resulting in minor memory leak on extern projections with unboxed arguments (#11383)
This PR fixes the compilation of structure projections with unboxed
arguments marked `extern`, adding missing `dec` instructions. It led to
leaking single allocations when such functions were used as closures or
in the interpreter.

This is the minimal working fix; `extern` should not replicate parts of
the compilation pipeline, which will be possible via #10291.
2025-11-26 19:27:43 +00:00
Henrik Böving
5dde403ec0 fix: toposort declarations to ensure proper constant initialization (#11388)
This PR is a followup of #11381 and enforces the invariants on ordering
of closed terms and constants required by the EmitC pass properly by
toposorting before saving the declarations into the Environment.
2025-11-26 18:17:17 +00:00
Joachim Breitner
8639afacf8 fix: when constructing instance names, avoid private names (#11385)
This PR lets implicit instance names avoid name clashes with private
declarations. This fixes #10329.
2025-11-26 18:16:44 +00:00
Wojciech Różowski
fea55533d9 feat: add ofArray to DHashMap/HashMap/HashSet (#11243)
This PR adds `ofArray` to `DHashMap`/`HashMap`/`HashSet` and proves a
simp lemma allowing to rewrite `ofArray` to `ofList`.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-26 17:24:40 +00:00
David Thrane Christiansen
70b4943506 chore: add release draft for the module system (#11359)
This PR adds a release note draft for the next major release, where the
module system will cease being experimental.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-11-26 15:01:07 +00:00
David Thrane Christiansen
34adc4d941 doc: add missing docstrings (#11364)
This PR adds missing docstrings for constants that occur in the
reference manual.

---------

Co-authored-by: Johannes Tantow <44068763+jt0202@users.noreply.github.com>
2025-11-26 15:00:50 +00:00
Markus Himmel
5fb25fff06 feat: grind instances for String.Pos and variants (#11384)
This PR adds the necessary instances for `grind` to reason about
`String.Pos.Raw`, `String.Pos` and `String.Slice.Pos`.
2025-11-26 13:59:01 +00:00
Henrik Böving
e8da78adda fix: enforce implicit invariants in EmitC stronger (#11381)
This PR fixes a bug where the closed term extraction does not respect
the implicit invariant of the
c emitter to have closed term decls first, other decls second, within an
SCC. This bug has not yet
been triggered in the wild but was unearthed during work on upcoming
modifications of the
specializer.
2025-11-26 12:24:03 +00:00
Markus Himmel
d8913f88dc feat: move String positions between slices (#11380)
This PR renames `String.Slice.Pos.ofSlice` to `String.Pos.ofToSlice` to
adhere with the (yet-to-be documented) naming convention for mapping
positions to positions. It then adds several new functions so that for
every way to construct a slice from a string and slice, there are now
functions for mapping positions forwards and backwards along this
construction.
2025-11-26 11:48:59 +00:00
Joachim Breitner
9ce8a062ba perf: macro_inline ctorIdx for single constructor inductives (#11379)
This PR sets `@[macro_inline]` on the (trivial) `.ctorIdx` for inductive
types with one constructor, to reduce the number of symbols generated by
the compiler.
2025-11-26 11:23:00 +00:00
Sebastian Ullrich
3772bb8685 chore: revert "refactor: port shell option processing to Lean" (#11378)
Needs a fix to unbreak the Windows build first.

Reverts leanprover/lean4#11345
2025-11-26 09:28:48 +00:00
Markus Himmel
5a5f8c4c2e perf: unbundle needle from char/pred pattern (#11376)
This PR aims to improve the performance of `String.contains`,
`String.find`, etc. when using patterns of type `Char` or `Char -> Bool`
by moving the needle out of the iterator state and thus working around
missing unboxing in the compiler.
2025-11-26 07:30:29 +00:00
Kim Morrison
e8d35a1d77 fix: make library suggestions available in module files (#11373)
This PR makes the library suggestions extension state available when
importing from `module` files.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-26 05:39:27 +00:00
Leonardo de Moura
5ac0931c8f feat: cleanup denominators in grind linarith (#11375)
This PR adds support for cleaning up denominators in `grind linarith`
when the type is a `Field`.

Examples:
```lean
open Std Lean.Grind
section
variable {α : Type} [Field α] [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] [OrderedRing α]

example (a b : α) (h : a < b / 2) : 2 * a < b := by grind
example (a b : α) (_ : 0 ≤ a) (h : a ≤ b) : a / 7 ≤ b / 2 := by grind
example (a b : α) (_ : b < 0) (h : a < b) : (3/2) * a < (5/4) * b := by grind
example (a b : α) (h : a = b * (3⁻¹)^2) : 9 * a ≤ b := by grind
example (a b : α) (h : a / 2 ≠ b / 9) : 9 * a < 2 * b ∨ 9 * a > 2 * b := by grind
example (a b : α) (h : a < b / (2^2 - 3/2 + -1 + 1/2)) : 2 * a < b := by grind

end

example (a b : Rat) (h : a < b / 2) : a + a < b := by grind
example (a b : Rat) (h : a < b / 2) : a + a ≤ b := by grind
example (a b : Rat) (h : a ≠ b * (3⁻¹)^2) : 9 * a < b ∨ 9 * a > b := by grind
example (a b : Rat) (h : a / 2 ≠ b / 9) : 9 * a < 2 * b ∨ 9 * a > 2 * b := by grind
```
2025-11-26 05:21:55 +00:00
Kim Morrison
6f4bee8421 perf: avoid re-exporting Std.Time from grind_annotated (#11372)
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:05:08 +00: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
Leonardo de Moura
5306a3469d fix: bug ite/dite propagator used in grind (#11295)
This PR fixes a bug in the propagation rules for `ite` and `dite` used
in `grind`. The bug prevented equalities from being propagated to the
satellite solvers. Here is an example affected by this issue.

```lean
example
    [LE α] [LT α] [Std.IsLinearOrder α] [Std.LawfulOrderLT α]
    [Lean.Grind.CommRing α] [DecidableLE α] [Lean.Grind.OrderedRing α]
    (a b c : α) :
  (if a - b ≤ -(a - b) then -(a - b) else a - b) ≤
  ((if a - c ≤ -(a - c) then -(a - c) else a - c) + if c - d ≤ -(c - d) then -(c - d) else c - d) +
    if b - d ≤ -(b - d) then -(b - d) else b - d := by
  grind
```
2025-11-20 23:54:28 +00:00
Marc Huisinga
2f1e258a5e test: re-enable re-elab benchmarks and add watchdog re-elab benchmark (#11284) 2025-11-20 22:53:08 +00:00
Sebastian Ullrich
e97c1505f0 fix: shake: register attribute rev use independent of initialize kind (#11293) 2025-11-20 20:39:27 +00:00
Robert J. Simmons
b6399e18c3 feat: allow decidable equality for empty lists and empty arrays (#11269)
This PR adds support for decidable equality of empty lists and empty
arrays. Decidable equality for lists and arrays is suitably modified so
that all diamonds are definitionally equal.

Following #9302, the strong condition of definitionally equal under
`with_reducible_and_instances` is tested. This also moves some of the
comments added in #9302 out of docstrings.

---------

Co-authored-by: Aaron Liu <aaronliu2008@outlook.com>
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
2025-11-20 20:19:31 +00:00
Markus Himmel
51b67385cc refactor: better name for String.replaceStart and variants (#11290)
This PR renames `String.replaceStartEnd` to `String.slice`,
`String.replaceStart` to `String.sliceFrom`, and `String.replaceEnd` to
`String.sliceTo`, and similar for the corresponding functions on
`String.Slice`.
2025-11-20 16:42:27 +00:00
Wojciech Różowski
556e96088e feat: add lemmas relating getMin/getMin?/getMin!/getMinD and insertion to the empty (D)TreeMap/TreeSet (#11231)
This PR adds several lemmas that relate
`getMin`/`getMin?`/`getMin!`/`getMinD` and insertion to the empty
(D)TreeMap/TreeSet and their extensional variants.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-20 16:35:07 +00:00
Paul Reichert
649d0b4eb5 refactor: remove duplicated internal lemmas (#11260)
This PR removes some duplicated internal lemmas of the hash map and tree
map infrastructure.
2025-11-20 16:29:27 +00:00
Sebastian Ullrich
e5e7a89fdc fix: shake: only record used simp theorems as dependencies, plus simprocs (#11287) 2025-11-20 15:43:25 +00:00
Sebastian Ullrich
7ef229d03d chore: shake: re-add attribute rev use (#11288)
Global `attribute` commands on non-local declarations are impossible to
track granularly a priori and so should be preserved by `shake` by
default. A new `shake` option could be added to ignore these
dependencies for evaluation.
2025-11-20 15:39:38 +00:00
Markus Himmel
7267ed707a feat: string patterns for decidable predicates on Char (#11285)
This PR adds `Std.Slice.Pattern` instances for `p : Char -> Prop` as
long as `DecidablePred p`, to allow things like `"hello".dropWhile (· =
'h')`.

To achieve this, we refactor `ForwardPattern` and friends to be
"non-uniform", i.e., the class is now `ForwardPattern pat`, not
`ForwardPattern ρ` (where `pat : ρ`).
2025-11-20 15:30:37 +00:00
Wojciech Różowski
89d4e9bd4c feat: add intersection for ExtDHashMap/ExtHashMap/ExtHashSet (#11241)
This PR provides intersection operation for
`ExtDHashMap`/`ExtHashMap`/`ExtHashSet` and proves several lemmas about
it.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-20 15:24:28 +00:00
Wojciech Różowski
108a3d1b44 feat: add intersection on DTreeMap/TreeMap/TreeSet (#11165)
This PR provides intersection on `DTreeMap`/`TreeMap`/`TreeSet`and
provides several lemmas about it.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-20 15:08:30 +00:00
Markus Himmel
f7ed158002 chore: introduce and immediately deprecate String.Slice.length (#11286)
This PR adds a function `String.Slice.length`, with the following
deprecation string: There is no constant-time length function on slices.
Use `s.positions.count` instead, or `isEmpty` if you only need to know
whether the slice is empty.
2025-11-20 14:31:46 +00:00
Markus Himmel
cf0e4441e8 chore: create alias String.Slice.any for String.Slice.contains (#11282)
This PR adds the alias `String.Slice.any` for `String.Slice.contains`.

It would probably be even better to only have one, but we don't have a
good mechanism for pointing people looking for one towards the other, so
an alias it is for now.
2025-11-20 13:21:30 +00:00
Markus Himmel
2c12bc9fdf chore: more deprecations for string migration (#11281)
This PR adds a few deprecations for functions that never existed but
that are still helpful for people migrating their code post-#11180.
2025-11-20 13:09:52 +00:00
Paul Reichert
fc6e0454c7 feat: add more lemmas about Array and List slices, support subslices (#11178)
This PR provides more lemmas about `Subarray` and `ListSlice` and it
also adds support for subslices of these two types of slices.
2025-11-20 10:46:17 +00:00
2002 changed files with 20572 additions and 4447 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

@@ -106,9 +106,54 @@ jobs:
TAG_NAME="${GITHUB_REF##*/}"
echo "RELEASE_TAG=$TAG_NAME" >> "$GITHUB_OUTPUT"
- name: Validate CMakeLists.txt version matches tag
if: steps.set-release.outputs.RELEASE_TAG != ''
run: |
echo "Validating CMakeLists.txt version matches tag ${{ steps.set-release.outputs.RELEASE_TAG }}"
# Extract version values from CMakeLists.txt
CMAKE_MAJOR=$(grep -E "^set\(LEAN_VERSION_MAJOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_MINOR=$(grep -E "^set\(LEAN_VERSION_MINOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_PATCH=$(grep -E "^set\(LEAN_VERSION_PATCH " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_IS_RELEASE=$(grep -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | grep -oE '[0-9]+')
# Expected values from tag parsing
TAG_MAJOR="${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}"
TAG_MINOR="${{ steps.set-release.outputs.LEAN_VERSION_MINOR }}"
TAG_PATCH="${{ steps.set-release.outputs.LEAN_VERSION_PATCH }}"
ERRORS=""
if [[ "$CMAKE_MAJOR" != "$TAG_MAJOR" ]]; then
ERRORS+="LEAN_VERSION_MAJOR: expected $TAG_MAJOR, found $CMAKE_MAJOR\n"
fi
if [[ "$CMAKE_MINOR" != "$TAG_MINOR" ]]; then
ERRORS+="LEAN_VERSION_MINOR: expected $TAG_MINOR, found $CMAKE_MINOR\n"
fi
if [[ "$CMAKE_PATCH" != "$TAG_PATCH" ]]; then
ERRORS+="LEAN_VERSION_PATCH: expected $TAG_PATCH, found $CMAKE_PATCH\n"
fi
if [[ "$CMAKE_IS_RELEASE" != "1" ]]; then
ERRORS+="LEAN_VERSION_IS_RELEASE: expected 1, found $CMAKE_IS_RELEASE\n"
fi
if [[ -n "$ERRORS" ]]; then
echo "::error::Version mismatch between tag and src/CMakeLists.txt"
echo ""
echo "Tag ${{ steps.set-release.outputs.RELEASE_TAG }} expects version $TAG_MAJOR.$TAG_MINOR.$TAG_PATCH"
echo "But src/CMakeLists.txt has mismatched values:"
echo -e "$ERRORS"
echo ""
echo "Fix src/CMakeLists.txt, delete the tag, and re-tag."
exit 1
fi
echo "Version validation passed: $TAG_MAJOR.$TAG_MINOR.$TAG_PATCH"
# 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
# 3: 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 +163,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 +257,22 @@ jobs:
"test": true,
"CMAKE_PRESET": "reldebug",
},
// TODO: suddenly started failing in CI
/*{
{
"name": "Linux fsanitize",
"os": "ubuntu-latest",
// Always run on large if available, more reliable regarding timeouts
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"enabled": level >= 2,
// do not fail nightlies on this for now
"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'"
},*/
// `StackOverflow*` correctly triggers ubsan
// `reverse-ffi` fails to link in sanitizers
// `interactive` and `async_select_channel` fail nondeterministically, would need to
// be investigated.
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel'"
},
{
"name": "macOS",
"os": "macos-15-intel",
@@ -252,7 +304,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 LSAN_OPTIONS=max_leaks=10"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/sanitize"

View File

@@ -72,6 +72,9 @@ update the archived C source code of the stage 0 compiler in `stage0/src`.
The github repository will automatically update stage0 on `master` once
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
To trigger this, modify `stage0/src/stdlib_flags.h` (e.g., by adding or changing
a comment). When `update-stage0` runs, it will overwrite `stage0/src/stdlib_flags.h`
with the contents of `src/stdlib_flags.h`, bringing them back in sync.
NOTE: A full rebuild of stage 1 will only be triggered when the *committed* contents of `stage0/` are changed.
Thus if you change files in it manually instead of through `update-stage0-commit` (see below) or fetching updates from git, you either need to commit those changes first or run `make -C build/release clean-stdlib`.

View File

@@ -0,0 +1,54 @@
This release introduces the Lean module system, which allows files to
control the visibility of their contents for other files. In previous
releases, this feature was available as a preview when the option
`experimental.module` was set to `true`; it is now a fully supported
feature of Lean.
# Benefits
Because modules reduce the amount of information exposed to other
code, they speed up rebuilds because irrelevant changes can be
ignored, they make it possible to be deliberate about API evolution by
hiding details that may change from clients, they help proofs be
checked faster by avoiding accidentally unfolding definitions, and
they lead to smaller executable files through improved dead code
elimination.
# Visibility
A source file is a module if it begins with the `module` keyword. By
default, declarations in a module are private; the `public` modifier
exports them. Proofs of theorems and bodies of definitions are private
by default even when their signatures are public; the bodies of
definitions can be made public by adding the `@[expose]`
attribute. Theorems and opaque constants never expose their bodies.
`public section` and `@[expose] section` change the default visibility
of declarations in the section.
# Imports
Modules may only import other modules. By default, `import` adds the
public information of the imported module to the private scope of the
current module. Adding the `public` modifier to an import places the
imported modules's public information in the public scope of the
current module, exposing it in turn to the current module's clients.
Within a package, `import all` can be used to import another module's
private scope into the current module; this can be used to separate
lemmas or tests from definition modules without exposing details to
downstream clients.
# Meta Code
Code used in metaprograms must be marked `meta`. This ensures that the
code is compiled and available for execution when it is needed during
elaboration. Meta code may only reference other meta code. A whole
module can be made available in the meta phase using `meta import`;
this allows code to be shared across phases by importing the module in
each phase. Code that is reachable from public metaprograms must be
imported via `public meta import`, while local metaprograms can use
plain `meta import` for their dependencies.
The module system is described in detail in [the Lean language reference](https://lean-reference-manual-review.netlify.app/find/?domain=Verso.Genre.Manual.section&name=files).

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

@@ -10,6 +10,16 @@ Tests language server memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
-/
def determineRSS (pid : UInt32) : IO Nat := do
let status IO.FS.readFile s!"/proc/{pid}/smaps_rollup"
let some rssLine := status.splitOn "\n" |>.find? (·.startsWith "Rss:")
| throw <| IO.userError "No RSS in proc status"
let rssLine := rssLine.dropPrefix "Rss:"
let rssLine := rssLine.dropWhile Char.isWhitespace
let some rssInKB := rssLine.takeWhile Char.isDigit |>.toNat?
| throw <| IO.userError "Cannot parse RSS"
return rssInKB
def main (args : List String) : IO Unit := do
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
let file IO.FS.realPath file
@@ -34,11 +44,14 @@ def main (args : List String) : IO Unit := do
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let n := iters.toNat!
let mut lastRSS? : Option Nat := none
let mut totalRSSDelta : Int := 0
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
textDocument := { uri := uri, languageId := "lean", version := 1, text := text } : DidOpenTextDocumentParams }
for i in [0:iters.toNat!] do
for i in [0:n] do
if i > 0 then
versionNo := versionNo + 1
let params : DidChangeTextDocumentParams := {
@@ -61,9 +74,16 @@ def main (args : List String) : IO Unit := do
IO.eprintln diag.message
requestNo := requestNo + 1
let status IO.FS.readFile s!"/proc/{(← read).pid}/status"
for line in status.splitOn "\n" |>.filter (·.startsWith "RssAnon") do
IO.eprintln line
let rss determineRSS ( read).pid
-- The first `didChange` usually results in a significantly higher RSS increase than
-- the others, so we ignore it.
if i > 1 then
if let some lastRSS := lastRSS? then
totalRSSDelta := totalRSSDelta + ((rss : Int) - (lastRSS : Int))
lastRSS? := some rss
let avgRSSDelta := totalRSSDelta / (n - 2)
IO.println s!"avg-reelab-rss-delta: {avgRSSDelta}"
let _ Ipc.collectDiagnostics requestNo uri versionNo
( Ipc.stdin).writeLspMessage (Message.notification "exit" none)

View File

@@ -0,0 +1,89 @@
import Lean.Data.Lsp
import Lean.Elab.Import
open Lean
open Lean.Lsp
open Lean.JsonRpc
/-!
Tests watchdog memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
-/
def determineRSS (pid : UInt32) : IO Nat := do
let status IO.FS.readFile s!"/proc/{pid}/smaps_rollup"
let some rssLine := status.splitOn "\n" |>.find? (·.startsWith "Rss:")
| throw <| IO.userError "No RSS in proc status"
let rssLine := rssLine.dropPrefix "Rss:"
let rssLine := rssLine.dropWhile Char.isWhitespace
let some rssInKB := rssLine.takeWhile Char.isDigit |>.toNat?
| throw <| IO.userError "Cannot parse RSS"
return rssInKB
def main (args : List String) : IO Unit := do
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
let file IO.FS.realPath file
let uri := s!"file://{file}"
Ipc.runWith leanCmd (#["--server", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
let capabilities := {
textDocument? := some {
completion? := some {
completionItem? := some {
insertReplaceSupport? := true
}
}
}
}
Ipc.writeRequest 0, "initialize", { capabilities : InitializeParams }
discard <| Ipc.readResponseAs 0 InitializeResult
Ipc.writeNotification "initialized", InitializedParams.mk
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let n := iters.toNat!
let mut lastRSS? : Option Nat := none
let mut totalRSSDelta : Int := 0
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
textDocument := { uri := uri, languageId := "lean", version := 1, text := text } : DidOpenTextDocumentParams }
for i in [0:iters.toNat!] do
if i > 0 then
versionNo := versionNo + 1
let params : DidChangeTextDocumentParams := {
textDocument := {
uri := uri
version? := versionNo
}
contentChanges := #[TextDocumentContentChangeEvent.rangeChange {
start := headerEndPos
«end» := headerEndPos
} " "]
}
let params := toJson params
Ipc.writeNotification "textDocument/didChange", params
requestNo := requestNo + 1
let diags Ipc.collectDiagnostics requestNo uri versionNo
if let some diags := diags then
for diag in diags.param.diagnostics do
IO.eprintln diag.message
requestNo := requestNo + 1
Ipc.waitForILeans requestNo uri versionNo
let rss determineRSS ( read).pid
-- The first `didChange` usually results in a significantly higher RSS increase than
-- the others, so we ignore it.
if i > 1 then
if let some lastRSS := lastRSS? then
totalRSSDelta := totalRSSDelta + ((rss : Int) - (lastRSS : Int))
lastRSS? := some rss
let avgRSSDelta := totalRSSDelta / (n - 2)
IO.println s!"avg-reelab-rss-delta: {avgRSSDelta}"
let _ Ipc.collectDiagnostics requestNo uri versionNo
Ipc.shutdown requestNo
discard <| Ipc.waitForExit

View File

@@ -58,7 +58,11 @@ OPTIONS=()
# We build cadical using the custom toolchain on Linux to avoid glibc versioning issues
echo -n " -DLEAN_STANDALONE=ON -DCADICAL_USE_CUSTOM_CXX=ON"
echo -n " -DCMAKE_CXX_COMPILER=$PWD/llvm-host/bin/clang++ -DLEAN_CXX_STDLIB='-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic'"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# these should also be used for cadical, so do not use `LEAN_EXTRA_CXX_FLAGS` here
echo -n " -DCMAKE_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# the above does not include linker flags which will be added below based on context, so skip the
# generic check by cmake
echo -n " -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER_WORKS=1"
# use target compiler directly when not cross-compiling
if [[ -L llvm-host ]]; then
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang"

View File

@@ -31,6 +31,8 @@ What this script does:
- Ensures tags are merged into stable branches (for non-RC releases)
- Verifies bump branches exist and are configured correctly
- Special handling for ProofWidgets4 release tags
- For mathlib4: runs verify_version_tags.py to validate the release tag
(checks git/GitHub consistency, toolchain, elan, cache, and build)
3. Optionally automates missing steps (when not in --dry-run mode):
- Creates missing release tags using push_repo_release_tag.py
@@ -499,6 +501,57 @@ def check_proofwidgets4_release(repo_url, target_toolchain, github_token):
print(f" You will need to create and push a tag v0.0.{next_version}")
return False
def run_mathlib_verify_version_tags(toolchain, verbose=False):
"""Run mathlib4's verify_version_tags.py script to validate the release tag.
This clones mathlib4 to a temp directory and runs the verification script.
Returns True if verification passes, False otherwise.
"""
import tempfile
print(f" ... Running mathlib4 verify_version_tags.py {toolchain}")
with tempfile.TemporaryDirectory() as tmpdir:
# Clone mathlib4 (shallow clone is sufficient for running the script)
clone_result = subprocess.run(
['git', 'clone', '--depth', '1', 'https://github.com/leanprover-community/mathlib4.git', tmpdir],
capture_output=True,
text=True
)
if clone_result.returncode != 0:
print(f" ❌ Failed to clone mathlib4: {clone_result.stderr.strip()[:200]}")
return False
# Run the verification script
script_path = os.path.join(tmpdir, 'scripts', 'verify_version_tags.py')
if not os.path.exists(script_path):
print(f" ❌ verify_version_tags.py not found in mathlib4 (expected at scripts/verify_version_tags.py)")
return False
# Run from the mathlib4 directory so git operations work
result = subprocess.run(
['python3', script_path, toolchain],
cwd=tmpdir,
capture_output=True,
text=True,
timeout=900 # 15 minutes timeout for cache download etc.
)
# Print output with indentation
if result.stdout:
for line in result.stdout.strip().split('\n'):
print(f" {line}")
if result.stderr:
for line in result.stderr.strip().split('\n'):
print(f" {line}")
if result.returncode != 0:
print(f" ❌ mathlib4 verify_version_tags.py failed")
return False
print(f" ✅ mathlib4 verify_version_tags.py passed")
return True
def main():
parser = argparse.ArgumentParser(description="Check release status of Lean4 repositories")
parser.add_argument("toolchain", help="The toolchain version to check (e.g., v4.6.0)")
@@ -763,6 +816,12 @@ def main():
repo_status[name] = False
continue
# For mathlib4, run verify_version_tags.py to validate the release tag
if name == "mathlib4":
if not run_mathlib_verify_version_tags(toolchain, verbose):
repo_status[name] = False
continue
repo_status[name] = success
# Final check for lean4 master branch

View File

@@ -42,7 +42,7 @@ if(LLD_PATH)
endif()
set(LEAN_EXTRA_LINKER_FLAGS ${LEAN_EXTRA_LINKER_FLAGS_DEFAULT} CACHE STRING "Additional flags used by the linker")
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler")
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler. Unlike `CMAKE_CXX_FLAGS`, these will not be used to build e.g. cadical.")
set(LEAN_TEST_VARS "LEAN_CC=${CMAKE_C_COMPILER}" CACHE STRING "Additional environment variables used when running tests")
if (NOT CMAKE_BUILD_TYPE)
@@ -191,7 +191,7 @@ endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
# Initialize CXXFLAGS.
set(CMAKE_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS_DEBUG "-DLEAN_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG")

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

@@ -89,11 +89,41 @@ theorem isEqv_self_beq [BEq α] [ReflBEq α] (xs : Array α) : Array.isEqv xs xs
theorem isEqv_self [DecidableEq α] (xs : Array α) : Array.isEqv xs xs (· = ·) = true := by
simp [isEqv, isEqvAux_self]
instance [DecidableEq α] : DecidableEq (Array α) :=
fun xs ys =>
match h:isEqv xs ys (fun a b => a = b) with
| true => isTrue (eq_of_isEqv xs ys h)
| false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction
def instDecidableEqImpl [DecidableEq α] : DecidableEq (Array α) := fun xs ys =>
match h:isEqv xs ys (fun a b => a = b) with
| true => isTrue (eq_of_isEqv xs ys h)
| false => isFalse (by subst ·; rw [isEqv_self] at h; contradiction)
instance instDecidableEq [DecidableEq α] : DecidableEq (Array α) := fun xs ys =>
match xs with
| [] =>
match ys with
| [] => isTrue rfl
| _ :: _ => isFalse (Array.noConfusion · (List.noConfusion ·))
| a :: as =>
match ys with
| [] => isFalse (Array.noConfusion · (List.noConfusion ·))
| b :: bs => instDecidableEqImpl a :: as b :: bs
@[csimp]
theorem instDecidableEq_csimp : @instDecidableEq = @instDecidableEqImpl :=
Subsingleton.allEq _ _
/--
Equality with `#[]` is decidable even if the underlying type does not have decidable equality.
-/
instance instDecidableEqEmp (xs : Array α) : Decidable (xs = #[]) :=
match xs with
| [] => isTrue rfl
| _ :: _ => isFalse (Array.noConfusion · (List.noConfusion ·))
/--
Equality with `#[]` is decidable even if the underlying type does not have decidable equality.
-/
instance instDecidableEmpEq (ys : Array α) : Decidable (#[] = ys) :=
match ys with
| [] => isTrue rfl
| _ :: _ => isFalse (Array.noConfusion · (List.noConfusion ·))
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
(xs == ys) = if h : xs.size = ys.size then

View File

@@ -409,8 +409,6 @@ theorem popWhile_append {xs ys : Array α} :
rcases ys with ys
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, List.dropWhile_append,
List.isEmpty_iff, List.isEmpty_toArray, List.isEmpty_reverse]
-- Why do these not fire with `simp`?
rw [List.popWhile_toArray, List.isEmpty_toArray, List.isEmpty_reverse]
split
· rfl
· simp

View File

@@ -3555,11 +3555,6 @@ theorem mem_of_back? {xs : Array α} {a : α} (h : xs.back? = some a) : a ∈ xs
rcases ys with ys
simp only [List.append_toArray, List.back_toArray, List.getLast_append, List.isEmpty_iff,
List.isEmpty_toArray]
split
· rw [dif_pos]
simpa only [List.isEmpty_toArray]
· rw [dif_neg]
simpa only [List.isEmpty_toArray]
theorem back_append_right {xs ys : Array α} (h : 0 < ys.size) :
(xs ++ ys).back (by simp; omega) = ys.back h := by
@@ -3971,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
@@ -4013,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 -/
@@ -4276,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

@@ -132,6 +132,11 @@ Copies the bytes with indices {name}`b` (inclusive) to {name}`e` (exclusive) to
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
a.copySlice b empty 0 (e - b)
/--
Appends two byte arrays using fast array primitives instead of converting them into lists and back.
In compiled code, this function replaces calls to {name}`ByteArray.append`.
-/
@[inline]
protected def fastAppend (a : ByteArray) (b : ByteArray) : ByteArray :=
-- we assume that `append`s may be repeated, so use asymptotic growing; use `copySlice` directly to customize
@@ -243,7 +248,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

@@ -678,6 +678,7 @@ Given this typeclass, termination proofs for well-founded recursion over an iter
`it.finitelyManySteps` as a termination measure.
-/
class Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
/-- The relation of plausible successors is well-founded. -/
wf : WellFounded (IterM.IsPlausibleSuccessorOf (α := α) (m := m))
theorem Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
@@ -797,6 +798,7 @@ Given this typeclass, termination proofs for well-founded recursion over an iter
`it.finitelyManySkips` as a termination measure.
-/
class Productive (α m) {β} [Iterator α m β] : Prop where
/-- The relation of plausible successors during skips is well-founded. -/
wf : WellFounded (IterM.IsPlausibleSkipSuccessorOf (α := α) (m := m))
/--

View File

@@ -9,6 +9,8 @@ prelude
public import Init.Data.Iterators.Consumers.Collect
public import Init.Data.Iterators.Consumers.Monadic.Loop
set_option linter.missingDocs true
public section
/-!
@@ -46,6 +48,9 @@ instance (α : Type w) (β : Type w) (n : Type x → Type x') [Monad n]
haveI : ForIn' n (Iter (α := α) β) β _ := Iter.instForIn'
instForInOfForIn'
/--
An implementation of `for h : ... in ... do ...` notation for partial iterators.
-/
@[always_inline, inline]
def Iter.Partial.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoopPartial α Id n] :
@@ -63,12 +68,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)
@@ -202,6 +207,11 @@ def Iter.all {α β : Type w}
(p : β Bool) (it : Iter (α := α) β) : Bool :=
(it.allM (fun x => pure (f := Id) (p x))).run
/--
Steps through the iterator until the monadic function `f` returns `some` for an element, at which
point iteration stops and the result of `f` is returned. If the iterator is completely consumed
without `f` returning `some`, then the result is `none`.
-/
@[inline]
def Iter.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (Option γ)) :
@@ -211,7 +221,7 @@ def Iter.findSomeM? {α β : Type w} {γ : Type x} {m : Type x → Type w'} [Mon
| none => return .yield none
| some fx => return .done (some fx))
@[inline]
@[inline, inherit_doc Iter.findSomeM?]
def Iter.Partial.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β)
(f : β m (Option γ)) :
@@ -221,36 +231,50 @@ def Iter.Partial.findSomeM? {α β : Type w} {γ : Type x} {m : Type x → Type
| none => return .yield none
| some fx => return .done (some fx))
/--
Steps through the iterator until `f` returns `some` for an element, at which point iteration stops
and the result of `f` is returned. If the iterator is completely consumed without `f` returning
`some`, then the result is `none`.
-/
@[inline]
def Iter.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] (it : Iter (α := α) β) (f : β Option γ) :
Option γ :=
Id.run (it.findSomeM? (pure <| f ·))
@[inline]
@[inline, inherit_doc Iter.findSome?]
def Iter.Partial.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoopPartial α Id Id] (it : Iter.Partial (α := α) β) (f : β Option γ) :
Option γ :=
Id.run (it.findSomeM? (pure <| f ·))
/--
Steps through the iterator until an element satisfies the monadic predicate `f`, at which point
iteration stops and the element is returned. If no element satisfies `f`, then the result is
`none`.
-/
@[inline]
def Iter.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
@[inline]
@[inline, inherit_doc Iter.findM?]
def Iter.Partial.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
/--
Steps through the iterator until an element satisfies `f`, at which point iteration stops and the
element is returned. If no element satisfies `f`, then the result is `none`.
-/
@[inline]
def Iter.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
[Finite α Id] (it : Iter (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))
@[inline]
@[inline, inherit_doc Iter.find?]
def Iter.Partial.find? {α β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))

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
@@ -298,6 +305,12 @@ theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
have h₁ := Nat.le_of_not_lt h₁
rw [getElem?_eq_none h₁, getElem?_eq_none]; rwa [ hl]
theorem ext_getElem_iff {l₁ l₂ : List α} :
l₁ = l₂ l₁.length = l₂.length (i : Nat) (h₁ : i < l₁.length) (h₂ : i < l₂.length), l₁[i]'h₁ = l₂[i]'h₂ := by
constructor
· simp +contextual
· exact fun h => ext_getElem h.1 h.2
@[simp] theorem getElem_concat_length {l : List α} {a : α} {i : Nat} (h : i = l.length) (w) :
(l ++ [a])[i]'w = a := by
subst h; simp

View File

@@ -374,6 +374,22 @@ theorem drop_take : ∀ {i j : Nat} {l : List α}, drop i (take j l) = take (j -
rw [drop_take]
simp
@[simp]
theorem drop_eq_drop_iff :
{l : List α} {i j : Nat}, l.drop i = l.drop j min i l.length = min j l.length
| [], i, j => by simp
| _ :: xs, 0, 0 => by simp
| x :: xs, i + 1, 0 => by
rw [List.ext_getElem_iff]
simp [succ_min_succ, show ¬ xs.length - i = xs.length + 1 by omega]
| x :: xs, 0, j + 1 => by
rw [List.ext_getElem_iff]
simp [succ_min_succ, show ¬ xs.length + 1 = xs.length - j by omega]
| x :: xs, i + 1, j + 1 => by simp [succ_min_succ, drop_eq_drop_iff]
theorem drop_eq_drop_min {l : List α} {i : Nat} : l.drop i = l.drop (min i l.length) := by
simp
theorem take_reverse {α} {xs : List α} {i : Nat} :
xs.reverse.take i = (xs.drop (xs.length - i)).reverse := by
by_cases h : i xs.length

View File

@@ -15,18 +15,19 @@ public section
namespace Option
/- We write the instance manually so that it is coherent with `decidableEqNone` and
`decidableNoneEq`.
TODO: adjust the `deriving instance DecidableEq` handler to generate something coherent. -/
instance instDecidableEq {α} [DecidableEq α] : DecidableEq (Option α) := fun a b =>
instance instDecidableEq {α} [inst : DecidableEq α] : DecidableEq (Option α) := fun a b =>
/-
Structured for compatibility with the decidable-equality-with-none instances.
-/
match a with
| none => match b with
| none => .isTrue rfl
| some _ => .isFalse Option.noConfusion
| some a => match b with
| none => .isFalse Option.noConfusion
| some b => decidable_of_decidable_of_eq (Option.some.injEq a b).symm
| some b => match inst a b with
| .isTrue h => .isTrue (h rfl)
| .isFalse n => .isFalse (Option.noConfusion · n)
/--
Equality with `none` is decidable even if the wrapped type does not have decidable equality.

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

@@ -631,7 +631,7 @@ instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) := fun a b =>
decidable_of_bool (compare a b).isLT Ordering.isLT_iff_eq_lt
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare`
Constructs an `LE` instance from an `Ord` instance that asserts that the result of `compare`
satisfies `Ordering.isLE`.
-/
@[expose] def leOfOrd [Ord α] : LE α where

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

@@ -2846,6 +2846,7 @@ public theorem size_eq_if_rcc [LE α] [DecidableLE α] [UpwardEnumerable α]
· split <;> simp [*]
· rfl
@[simp]
public theorem length_toList [LE α] [DecidableLE α] [UpwardEnumerable α]
[Rxc.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[Rxc.IsAlwaysFinite α] [Rxc.LawfulHasSize α] :
@@ -2864,6 +2865,7 @@ public theorem length_toList [LE α] [DecidableLE α] [UpwardEnumerable α]
simp [h, ih _ h]
· simp
@[simp]
public theorem size_toArray [LE α] [DecidableLE α] [UpwardEnumerable α]
[Rxc.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[Rxc.IsAlwaysFinite α] [Rxc.LawfulHasSize α] :
@@ -2988,6 +2990,7 @@ public theorem size_eq_match_roc [LE α] [DecidableLE α] [UpwardEnumerable α]
rw [size_eq_match_rcc]
simp [Rcc.size_eq_if_roc]
@[simp]
public theorem length_toList [LE α] [DecidableLE α] [UpwardEnumerable α]
[Rxc.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[Rxc.IsAlwaysFinite α] [Rxc.LawfulHasSize α] :
@@ -2995,6 +2998,7 @@ public theorem length_toList [LE α] [DecidableLE α] [UpwardEnumerable α]
simp only [toList_eq_match_rcc, size_eq_match_rcc]
split <;> simp [Rcc.length_toList]
@[simp]
public theorem size_toArray [LE α] [DecidableLE α] [UpwardEnumerable α]
[Rxc.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[Rxc.IsAlwaysFinite α] [Rxc.LawfulHasSize α] :
@@ -3094,6 +3098,7 @@ public theorem size_eq_match_roc [Least? α] [LE α] [DecidableLE α] [UpwardEnu
rw [size_eq_match_rcc]
simp [Rcc.size_eq_if_roc]
@[simp]
public theorem length_toList [Least? α] [LE α] [DecidableLE α] [UpwardEnumerable α]
[Rxc.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[Rxc.IsAlwaysFinite α] [Rxc.LawfulHasSize α] :
@@ -3101,6 +3106,7 @@ public theorem length_toList [Least? α] [LE α] [DecidableLE α] [UpwardEnumera
rw [toList_eq_match_rcc, size_eq_match_rcc]
split <;> simp [Rcc.length_toList]
@[simp]
public theorem size_toArray [Least? α] [LE α] [DecidableLE α] [UpwardEnumerable α]
[Rxc.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLE α]
[Rxc.IsAlwaysFinite α] [Rxc.LawfulHasSize α] :
@@ -3223,6 +3229,7 @@ public theorem size_eq_if_rcc [LT α] [DecidableLT α] [UpwardEnumerable α]
· split <;> simp [*]
· rfl
@[simp]
public theorem length_toList [LT α] [DecidableLT α] [UpwardEnumerable α]
[Rxo.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
[Rxo.IsAlwaysFinite α] [Rxo.LawfulHasSize α] :
@@ -3241,6 +3248,7 @@ public theorem length_toList [LT α] [DecidableLT α] [UpwardEnumerable α]
simp [h, ih _ h]
· simp
@[simp]
public theorem size_toArray [LT α] [DecidableLT α] [UpwardEnumerable α]
[Rxo.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
[Rxo.IsAlwaysFinite α] [Rxo.LawfulHasSize α] :
@@ -3366,6 +3374,7 @@ public theorem size_eq_match_roc [LT α] [DecidableLT α] [UpwardEnumerable α]
rw [size_eq_match_rcc]
simp [Rco.size_eq_if_roo]
@[simp]
public theorem length_toList [LT α] [DecidableLT α] [UpwardEnumerable α]
[Rxo.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
[Rxo.IsAlwaysFinite α] [Rxo.LawfulHasSize α] :
@@ -3373,6 +3382,7 @@ public theorem length_toList [LT α] [DecidableLT α] [UpwardEnumerable α]
simp only [toList_eq_match_rco, size_eq_match_rcc]
split <;> simp [Rco.length_toList]
@[simp]
public theorem size_toArray [LT α] [DecidableLT α] [UpwardEnumerable α]
[Rxo.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
[Rxo.IsAlwaysFinite α] [Rxo.LawfulHasSize α] :
@@ -3472,6 +3482,7 @@ public theorem size_eq_match_roc [Least? α] [LT α] [DecidableLT α] [UpwardEnu
rw [size_eq_match_rcc]
simp [Rco.size_eq_if_roo]
@[simp]
public theorem length_toList [Least? α] [LT α] [DecidableLT α] [UpwardEnumerable α]
[Rxo.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
[Rxo.IsAlwaysFinite α] [Rxo.LawfulHasSize α] :
@@ -3479,6 +3490,7 @@ public theorem length_toList [Least? α] [LT α] [DecidableLT α] [UpwardEnumera
rw [toList_eq_match_rco, size_eq_match_rcc]
split <;> simp [Rco.length_toList]
@[simp]
public theorem size_toArray [Least? α] [LT α] [DecidableLT α] [UpwardEnumerable α]
[Rxo.HasSize α] [LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
[Rxo.IsAlwaysFinite α] [Rxo.LawfulHasSize α] :
@@ -3590,6 +3602,7 @@ public theorem size_eq_match_rci [UpwardEnumerable α] [Rxi.HasSize α] [LawfulU
simp only [Rci.size]
split <;> simp [*]
@[simp]
public theorem length_toList [UpwardEnumerable α] [Rxi.HasSize α] [LawfulUpwardEnumerable α]
[Rxi.IsAlwaysFinite α] [Rxi.LawfulHasSize α] :
r.toList.length = r.size := by
@@ -3605,6 +3618,7 @@ public theorem length_toList [UpwardEnumerable α] [Rxi.HasSize α] [LawfulUpwar
· simp only [Nat.add_right_cancel_iff, *] at h
simp [ih _ h, h]
@[simp]
public theorem size_toArray [UpwardEnumerable α] [Rxi.HasSize α] [LawfulUpwardEnumerable α]
[Rxi.IsAlwaysFinite α] [Rxi.LawfulHasSize α] :
r.toArray.size = r.size := by
@@ -3711,12 +3725,14 @@ public theorem size_eq_match_roi [UpwardEnumerable α] [Rxi.HasSize α] [LawfulU
rw [size_eq_match_rci]
simp [Rci.size_eq_size_roi]
@[simp]
public theorem length_toList [UpwardEnumerable α] [Rxi.HasSize α] [LawfulUpwardEnumerable α]
[Rxi.IsAlwaysFinite α] [Rxi.LawfulHasSize α] :
r.toList.length = r.size := by
simp only [toList_eq_match_rci, size_eq_match_rci]
split <;> simp [Rci.length_toList]
@[simp]
public theorem size_toArray [UpwardEnumerable α] [Rxi.HasSize α] [LawfulUpwardEnumerable α]
[Rxi.IsAlwaysFinite α] [Rxi.LawfulHasSize α] :
r.toArray.size = r.size := by
@@ -3809,6 +3825,7 @@ public theorem size_eq_match_roi [Least? α] [UpwardEnumerable α] [Rxi.HasSize
rw [size_eq_match_rci]
simp [Rci.size_eq_size_roi]
@[simp]
public theorem length_toList [Least? α] [UpwardEnumerable α]
[Rxi.HasSize α] [LawfulUpwardEnumerable α]
[Rxi.IsAlwaysFinite α] [Rxi.LawfulHasSize α] :
@@ -3816,6 +3833,7 @@ public theorem length_toList [Least? α] [UpwardEnumerable α]
rw [toList_eq_match_rci, size_eq_match_rci]
split <;> simp [Rci.length_toList]
@[simp]
public theorem size_toArray [Least? α] [UpwardEnumerable α]
[Rxi.HasSize α] [LawfulUpwardEnumerable α]
[Rxi.IsAlwaysFinite α] [Rxi.LawfulHasSize α] :

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.Range.Polymorphic.UpwardEnumerable
set_option doc.verso true
set_option linter.missingDocs true
public section
@@ -23,7 +24,13 @@ A range of elements of {given}`α` with closed lower and upper bounds.
equal to {given}`b : α`. This is notation for {lean}`Rcc.mk a b`.
-/
structure Rcc (α : Type u) where
/--
The lower bound of the range. {name (full := Rcc.lower)}`lower` is included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Rcc.upper)}`upper` is included in the range.
-/
upper : α
/--
@@ -33,7 +40,13 @@ A range of elements of {given}`α` with a closed lower bound and an open upper b
less than {given}`b : α`. This is notation for {lean}`Rco.mk a b`.
-/
structure Rco (α : Type u) where
/--
The lower bound of the range. {name (full := Rco.lower)}`lower` is included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Rco.upper)}`upper` is not included in the range.
-/
upper : α
/--
@@ -43,6 +56,9 @@ An upward-unbounded range of elements of {given}`α` with a closed lower bound.
This is notation for {lean}`Rci.mk a`.
-/
structure Rci (α : Type u) where
/--
The lower bound of the range. {name (full := Rci.lower)}`lower` is included in the range.
-/
lower : α
/--
@@ -52,7 +68,13 @@ A range of elements of {given}`α` with an open lower bound and a closed upper b
{given}`b : α`. This is notation for {lean}`Roc.mk a b`.
-/
structure Roc (α : Type u) where
/--
The lower bound of the range. {name (full := Roc.lower)}`lower` is not included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Roc.upper)}`upper` is included in the range.
-/
upper : α
/--
@@ -62,7 +84,13 @@ A range of elements of {given}`α` with an open lower and upper bounds.
{given}`b : α`. This is notation for {lean}`Roo.mk a b`.
-/
structure Roo (α : Type u) where
/--
The lower bound of the range. {name (full := Roo.lower)}`lower` is not included in the range.
-/
lower : α
/--
The upper bound of the range. {name (full := Roo.upper)}`upper` is not included in the range.
-/
upper : α
/--
@@ -72,6 +100,9 @@ An upward-unbounded range of elements of {given}`α` with an open lower bound.
This is notation for {lean}`Roi.mk a`.
-/
structure Roi (α : Type u) where
/--
The lower bound of the range. {name (full := Roi.lower)}`lower` is not included in the range.
-/
lower : α
/--
@@ -81,6 +112,9 @@ A downward-unbounded range of elements of {given}`α` with a closed upper bound.
This is notation for {lean}`Ric.mk b`.
-/
structure Ric (α : Type u) where
/--
The upper bound of the range. {name (full := Ric.upper)}`upper` is included in the range.
-/
upper : α
/--
@@ -90,6 +124,9 @@ A downward-unbounded range of elements of {given}`α` with an open upper bound.
This is notation for {lean}`Rio.mk b`.
-/
structure Rio (α : Type u) where
/--
The upper bound of the range. {name (full := Rio.upper)}`upper` is not included in the range.
-/
upper : α
/--
@@ -162,6 +199,10 @@ This is a prerequisite for many functions and instances, such as
{name (scope := "Init.Data.Range.Polymorphic.Iterators")}`Rcc.toList` or {name}`ForIn'`.
-/
class Rxc.IsAlwaysFinite (α : Type u) [UpwardEnumerable α] [LE α] : Prop where
/--
For every pair of elements {name}`init` and {name}`hi`, there exists a chain of successors that
results in an element that either has no successors or is greater than {name}`hi`.
-/
finite (init : α) (hi : α) :
n, (UpwardEnumerable.succMany? n init).elim True (¬ · hi)
@@ -172,6 +213,10 @@ This is a prerequisite for many functions and instances, such as
{name (scope := "Init.Data.Range.Polymorphic.Iterators")}`Rco.toList` or {name}`ForIn'`.
-/
class Rxo.IsAlwaysFinite (α : Type u) [UpwardEnumerable α] [LT α] : Prop where
/--
For every pair of elements {name}`init` and {name}`hi`, there exists a chain of successors that
results in an element that either has no successors or is greater than {name}`hi`.
-/
finite (init : α) (hi : α) :
n, (UpwardEnumerable.succMany? n init).elim True (¬ · < hi)
@@ -182,6 +227,10 @@ This is a prerequisite for many functions and instances, such as
{name (scope := "Init.Data.Range.Polymorphic.Iterators")}`Rci.toList` or {name}`ForIn'`.
-/
class Rxi.IsAlwaysFinite (α : Type u) [UpwardEnumerable α] : Prop where
/--
For every elements {name}`init`, there exists a chain of successors that
results in an element that has no successors.
-/
finite (init : α) : n, UpwardEnumerable.succMany? n init = none
namespace Rcc
@@ -291,6 +340,7 @@ This type class allows taking the intersection of a closed range with a
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Rcc.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rcc α Rco α Rco α
/--
@@ -299,6 +349,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rcc.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rcc α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -307,6 +360,7 @@ This type class allows taking the intersection of two left-closed right-open ran
another left-closed right-open range.
-/
class Rco.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rco α Rco α Rco α
/--
@@ -315,6 +369,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rco.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rco α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -323,6 +380,7 @@ This type class allows taking the intersection of a left-closed right-unbounded
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Rci.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rci α Rco α Rco α
/--
@@ -331,6 +389,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rci.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rci α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -339,6 +400,7 @@ This type class allows taking the intersection of a left-open right-closed range
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Roc.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Roc α Rco α Rco α
/--
@@ -347,6 +409,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Roc.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Roc α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -355,6 +420,7 @@ This type class allows taking the intersection of an open range with a
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Roo.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Roo α Rco α Rco α
/--
@@ -363,6 +429,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Roo.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Roo α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -371,6 +440,7 @@ This type class allows taking the intersection of a left-open right-unbounded ra
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Roi.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Roi α Rco α Rco α
/--
@@ -379,6 +449,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Roi.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Roi α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -387,6 +460,7 @@ This type class allows taking the intersection of a left-unbounded right-closed
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Ric.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Ric α Rco α Rco α
/--
@@ -395,6 +469,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Ric.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Ric α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s
@@ -403,6 +480,7 @@ This type class allows taking the intersection of a left-unbounded right-open ra
left-closed right-open range, resulting in another left-closed right-open range.
-/
class Rio.HasRcoIntersection (α : Type w) where
/-- The intersection operator. -/
intersection : Rio α Rco α Rco α
/--
@@ -411,6 +489,9 @@ of two ranges contains exactly those elements that are contained in both ranges.
-/
class Rio.LawfulRcoIntersection (α : Type w) [LT α] [LE α]
[HasRcoIntersection α] where
/--
Every element of the intersection is an element of both original ranges.
-/
mem_intersection_iff {a : α} {r : Rio α} {s : Rco α} :
a HasRcoIntersection.intersection r s a r a s

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

@@ -23,45 +23,82 @@ variable {α : Type u}
instance : Rcc.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rcc.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray range.lower (range.upper + 1)
instance : Rco.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rco.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray range.lower range.upper
instance : Rci.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rci.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray halfOpenRange.lower halfOpenRange.upper
instance : Roc.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Roc.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray (range.lower + 1) (range.upper + 1)
instance : Roo.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Roo.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray (range.lower + 1) range.upper
instance : Roi.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Roi.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray halfOpenRange.lower halfOpenRange.upper
instance : Ric.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Ric.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray 0 (range.upper + 1)
instance : Rio.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rio.HasRcoIntersection.intersection range 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray 0 range.upper
instance : Rii.Sliceable (Array α) Nat (Subarray α) where
mkSlice xs _ :=
let halfOpenRange := 0...<xs.size
(xs.toSubarray halfOpenRange.lower halfOpenRange.upper)
xs.toSubarray 0 xs.size
instance : Rcc.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rcc.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Rco.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rco.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Rci.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rci.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Roc.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Roc.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Roo.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Roo.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Roi.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Roi.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Ric.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Ric.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Rio.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs range :=
let halfOpenRange := Rio.HasRcoIntersection.intersection range 0...<xs.size
xs.array[(halfOpenRange.lower + xs.start)...(halfOpenRange.upper + xs.start)]
instance : Rii.Sliceable (Subarray α) Nat (Subarray α) where
mkSlice xs _ :=
xs

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

@@ -13,43 +13,100 @@ public import Init.Data.Slice.Array.Iterator
import all Init.Data.Slice.Array.Iterator
import all Init.Data.Slice.Operations
import all Init.Data.Range.Polymorphic.Iterators
public import Init.Data.Range.Polymorphic.Lemmas
import all Init.Data.Range.Polymorphic.Lemmas
public import Init.Data.Slice.Lemmas
public import Init.Data.Iterators.Lemmas
import Init.Data.Slice.List.Lemmas
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
|>.attachWith (· < s.array.size)
(fun out h => h
|> Rco.mem_toList_iff_mem.mp
|> Rco.lt_upper_of_mem
|> (Nat.lt_of_lt_of_le · s.stop_le_array_size))
|>.map fun i => s.array[i.1]) := 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
@@ -70,3 +127,631 @@ public theorem forIn_toArray {α : Type u} {s : Subarray α}
Slice.forIn_toArray
end Subarray
public theorem Array.toSubarray_eq_toSubarray_of_min_eq_min {xs : Array α}
{start stop stop' : Nat} (h : min stop xs.size = min stop' xs.size) :
xs.toSubarray start stop = xs.toSubarray start stop' := by
simp only [Array.toSubarray]
split
· split
· have h₁ : start xs.size := by omega
have h₂ : start stop' := by omega
simp only [dif_pos h₁, dif_pos h₂]
split
· simp_all
· simp_all [Nat.min_eq_right (Nat.le_of_lt _)]
· simp only [Nat.min_eq_left, *] at h
split
· simp only [Nat.min_eq_left, *] at 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 [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 [*]; omega
· simp
public theorem Array.toSubarray_eq_min {xs : Array α} {lo hi : Nat} :
xs.toSubarray lo hi = xs, min lo (min hi xs.size), min hi xs.size, Nat.min_le_right _ _,
Nat.min_le_right _ _ := by
simp only [Array.toSubarray]
split <;> split <;> simp [Nat.min_eq_right (Nat.le_of_not_ge _), *]
@[simp]
public theorem Array.array_toSubarray {xs : Array α} {lo hi : Nat} :
(xs.toSubarray lo hi).array = xs := by
simp [toSubarray_eq_min, Subarray.array]
@[simp]
public theorem Array.start_toSubarray {xs : Array α} {lo hi : Nat} :
(xs.toSubarray lo hi).start = min lo (min hi xs.size) := by
simp [toSubarray_eq_min, Subarray.start]
@[simp]
public theorem Array.stop_toSubarray {xs : Array α} {lo hi : Nat} :
(xs.toSubarray lo hi).stop = min hi xs.size := by
simp [toSubarray_eq_min, Subarray.stop]
theorem Subarray.toList_eq {xs : Subarray α} :
xs.toList = (xs.array.extract xs.start xs.stop).toList := by
let aslice := xs
obtain array, start, stop, h₁, h₂ := xs
let lslice : ListSlice α := array.toList.drop start, some (stop - start)
simp only [Subarray.start, Subarray.stop, Subarray.array]
change aslice.toList = _
have : aslice.toList = lslice.toList := by
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, *, Subarray.array]
· intros
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
simp [Std.Slice.toList, Subarray.toArray, Array.ofSubarray, Std.Slice.toArray]
@[simp]
public theorem Subarray.toList_toArray {xs : Subarray α} :
xs.toArray.toList = xs.toList := by
simp [Std.Slice.toList, Subarray.toArray, Array.ofSubarray, Std.Slice.toArray]
@[simp]
public theorem Subarray.length_toList {xs : Subarray α} :
xs.toList.length = xs.size := by
have : xs.start xs.stop := xs.internalRepresentation.start_le_stop
have : xs.stop xs.array.size := xs.internalRepresentation.stop_le_array_size
simp [Subarray.toList_eq, Subarray.size]; omega
@[simp]
public theorem Subarray.size_toArray {xs : Subarray α} :
xs.toArray.size = xs.size := by
simp [ Subarray.toArray_toList, Subarray.size, Slice.size, SliceSize.size, start, stop]
namespace Array
@[simp]
public theorem array_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo...hi].array = xs := by
simp [Std.Rco.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo...hi].start = min lo (min hi xs.size) := by
simp [Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem stop_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo...hi].stop = min hi xs.size := by
simp [Std.Rco.Sliceable.mkSlice]
public theorem mkSlice_rco_eq_mkSlice_rco_min {xs : Array α} {lo hi : Nat} :
xs[lo...hi] = xs[(min lo (min hi xs.size))...(min hi xs.size)] := by
simp [Std.Rco.Sliceable.mkSlice, Array.toSubarray_eq_min]
@[simp]
public theorem toList_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo...hi].toList = (xs.toList.take hi).drop lo := by
rw [List.take_eq_take_min, List.drop_eq_drop_min]
simp [Std.Rco.Sliceable.mkSlice, Subarray.toList_eq, List.take_drop,
Nat.add_sub_of_le (Nat.min_le_right _ _)]
@[simp]
public theorem toArray_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo...hi].toArray = xs.extract lo hi := by
simp only [ Subarray.toArray_toList, toList_mkSlice_rco]
rw [show xs = xs.toList.toArray by simp, List.extract_toArray, List.extract_eq_drop_take]
simp only [List.take_drop, mk.injEq]
by_cases h : lo hi
· congr 1
rw [List.take_eq_take_iff, Nat.add_sub_cancel' h]
· rw [List.drop_eq_nil_of_le, List.drop_eq_nil_of_le]
· simp; omega
· simp; omega
@[simp]
public theorem size_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo...hi].size = min hi xs.size - lo := by
simp [ Subarray.length_toList]
@[simp]
public theorem mkSlice_rcc_eq_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo...=hi] = xs[lo...(hi + 1)] := by
simp [Std.Rcc.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
public theorem mkSlice_rcc_eq_mkSlice_rco_min {xs : Array α} {lo hi : Nat} :
xs[lo...=hi] = xs[(min lo (min (hi + 1) xs.size))...(min (hi + 1) xs.size)] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem array_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].array = xs := by
simp [Std.Rcc.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].start = min lo (min (hi + 1) xs.size) := by
simp [Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem stop_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].stop = min (hi + 1) xs.size := by
simp [Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].toArray = xs.extract lo (hi + 1) := by
simp
@[simp]
public theorem size_mkSlice_rcc {xs : Array α} {lo hi : Nat} :
xs[lo...=hi].size = min (hi + 1) xs.size - lo := by
simp [ Subarray.length_toList]
@[simp]
public theorem array_mkSlice_rci {xs : Array α} {lo : Nat} :
xs[lo...*].array = xs := by
simp [Std.Rci.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_rci {xs : Array α} {lo : Nat} :
xs[lo...*].start = min lo xs.size := by
simp [Std.Rci.Sliceable.mkSlice, Std.Rci.HasRcoIntersection.intersection]
@[simp]
public theorem stop_mkSlice_rci {xs : Array α} {lo : Nat} :
xs[lo...*].stop = xs.size := by
simp [Std.Rci.Sliceable.mkSlice, Std.Rci.HasRcoIntersection.intersection]
@[simp]
public theorem mkSlice_rci_eq_mkSlice_rco {xs : Array α} {lo : Nat} :
xs[lo...*] = xs[lo...xs.size] := by
simp [Std.Rci.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice, Std.Rci.HasRcoIntersection.intersection]
public theorem mkSlice_rci_eq_mkSlice_rco_min {xs : Array α} {lo : Nat} :
xs[lo...*] = xs[(min lo xs.size)...xs.size] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem toList_mkSlice_rci {xs : Array α} {lo : Nat} :
xs[lo...*].toList = xs.toList.drop lo := by
rw [mkSlice_rci_eq_mkSlice_rco, toList_mkSlice_rco, Array.length_toList, List.take_length]
@[simp]
public theorem toArray_mkSlice_rci {xs : Array α} {lo : Nat} :
xs[lo...*].toArray = xs.extract lo := by
simp
@[simp]
public theorem size_mkSlice_rci {xs : Array α} {lo : Nat} :
xs[lo...*].size = xs.size - lo := by
simp [ Subarray.length_toList]
@[simp]
public theorem array_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].array = xs := by
simp [Std.Roo.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].start = min (lo + 1) (min hi xs.size) := by
simp [Std.Roo.Sliceable.mkSlice]
@[simp]
public theorem stop_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].stop = min hi xs.size := by
simp [Std.Roo.Sliceable.mkSlice]
@[simp]
public theorem mkSlice_roo_eq_mkSlice_rco {xs : Array α} {lo hi : Nat} :
xs[lo<...hi] = xs[(lo + 1)...hi] := by
simp [Std.Roo.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
public theorem mkSlice_roo_eq_mkSlice_roo_min {xs : Array α} {lo hi : Nat} :
xs[lo<...hi] = xs[(min (lo + 1) (min hi xs.size))...(min hi xs.size)] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem toList_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].toArray = xs.extract (lo + 1) hi := by
simp
@[simp]
public theorem size_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...hi].size = min hi xs.size - (lo + 1) := by
simp [ Subarray.length_toList]
@[simp]
public theorem array_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].array = xs := by
simp [Std.Roc.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].start = min (lo + 1) (min (hi + 1) xs.size) := by
simp [Std.Roc.Sliceable.mkSlice]
@[simp]
public theorem stop_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].stop = min (hi + 1) xs.size := by
simp [Std.Roc.Sliceable.mkSlice]
@[simp]
public theorem mkSlice_roc_eq_mkSlice_roo {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi] = xs[lo<...(hi + 1)] := by
simp [Std.Roc.Sliceable.mkSlice, Std.Roo.Sliceable.mkSlice]
public theorem mkSlice_roc_eq_mkSlice_roo_min {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi] = xs[(min (lo + 1) (min (hi + 1) xs.size))...(min (hi + 1) xs.size)] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem toList_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].toArray = xs.extract (lo + 1) (hi + 1) := by
simp
@[simp]
public theorem size_mkSlice_roc {xs : Array α} {lo hi : Nat} :
xs[lo<...=hi].size = min (hi + 1) xs.size - (lo + 1) := by
simp [ Subarray.length_toList]
@[simp]
public theorem array_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo<...*].array = xs := by
simp [Std.Roi.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo<...*].start = min (lo + 1) xs.size := by
simp [Std.Roi.Sliceable.mkSlice, Std.Roi.HasRcoIntersection.intersection]
@[simp]
public theorem stop_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo...*].stop = xs.size := by
simp [Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem mkSlice_roi_eq_mkSlice_rci {xs : Array α} {lo : Nat} :
xs[lo<...*] = xs[(lo + 1)...*] := by
simp [Std.Roi.Sliceable.mkSlice, Std.Roi.HasRcoIntersection.intersection,
Std.Rci.Sliceable.mkSlice, Std.Rci.HasRcoIntersection.intersection]
public theorem mkSlice_roi_eq_mkSlice_roo {xs : Array α} {lo : Nat} :
xs[lo<...*] = xs[lo<...xs.size] := by
simp [mkSlice_rci_eq_mkSlice_rco]
public theorem mkSlice_roi_eq_mkSlice_roo_min {xs : Array α} {lo : Nat} :
xs[lo<...*] = xs[(min (lo + 1) xs.size)...xs.size] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem toList_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo<...*].toList = xs.toList.drop (lo + 1) := by
rw [mkSlice_roi_eq_mkSlice_rci, toList_mkSlice_rci]
@[simp]
public theorem toArray_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo<...*].toArray = xs.drop (lo + 1) := by
simp
@[simp]
public theorem size_mkSlice_roi {xs : Array α} {lo : Nat} :
xs[lo<...*].size = xs.size - (lo + 1) := by
simp [ Subarray.length_toList]
@[simp]
public theorem array_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].array = xs := by
simp [Std.Rio.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].start = 0 := by
simp [Std.Rio.Sliceable.mkSlice]
@[simp]
public theorem stop_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].stop = min hi xs.size := by
simp [Std.Rio.Sliceable.mkSlice]
@[simp]
public theorem mkSlice_rio_eq_mkSlice_rco {xs : Array α} {hi : Nat} :
xs[*...hi] = xs[0...hi] := by
simp [Std.Rio.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
public theorem mkSlice_rio_eq_mkSlice_rio_min {xs : Array α} {hi : Nat} :
xs[*...hi] = xs[*...(min hi xs.size)] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem toList_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].toArray = xs.extract 0 hi := by
simp
@[simp]
public theorem size_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...hi].size = min hi xs.size := by
simp [ Subarray.length_toList]
@[simp]
public theorem array_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].array = xs := by
simp [Std.Ric.Sliceable.mkSlice, Array.toSubarray, apply_dite, Subarray.array]
@[simp]
public theorem start_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].start = 0 := by
simp [Std.Ric.Sliceable.mkSlice]
@[simp]
public theorem stop_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].stop = min (hi + 1) xs.size := by
simp [Std.Ric.Sliceable.mkSlice]
@[simp]
public theorem mkSlice_ric_eq_mkSlice_rio {xs : Array α} {hi : Nat} :
xs[*...=hi] = xs[*...(hi + 1)] := by
simp [Std.Ric.Sliceable.mkSlice, Std.Rio.Sliceable.mkSlice]
public theorem mkSlice_ric_eq_mkSlice_rio_min {xs : Array α} {hi : Nat} :
xs[*...=hi] = xs[*...(min (hi + 1) xs.size)] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem toList_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].toArray = xs.extract 0 (hi + 1) := by
simp
@[simp]
public theorem size_mkSlice_ric {xs : Array α} {hi : Nat} :
xs[*...=hi].size = min (hi + 1) xs.size := by
simp [ Subarray.length_toList]
@[simp]
public theorem mkSlice_rii_eq_mkSlice_rci {xs : Array α} :
xs[*...*] = xs[0...*] := by
simp [Std.Rii.Sliceable.mkSlice, Std.Rci.Sliceable.mkSlice,
Std.Rci.HasRcoIntersection.intersection]
public theorem mkSlice_rii_eq_mkSlice_rio {xs : Array α} :
xs[*...*] = xs[*...xs.size] := by
simp [mkSlice_rci_eq_mkSlice_rco]
public theorem mkSlice_rii_eq_mkSlice_rio_min {xs : Array α} :
xs[*...*] = xs[*...xs.size] := by
simp [mkSlice_rco_eq_mkSlice_rco_min]
@[simp]
public theorem toList_mkSlice_rii {xs : Array α} :
xs[*...*].toList = xs.toList := by
rw [mkSlice_rii_eq_mkSlice_rci, toList_mkSlice_rci, List.drop_zero]
@[simp]
public theorem toArray_mkSlice_rii {xs : Array α} :
xs[*...*].toArray = xs := by
simp
@[simp]
public theorem size_mkSlice_rii {xs : Array α} :
xs[*...*].size = xs.size := by
simp [ Subarray.length_toList]
@[simp]
public theorem array_mkSlice_rii {xs : Array α} :
xs[*...*].array = xs := by
simp
@[simp]
public theorem start_mkSlice_rii {xs : Array α} :
xs[*...*].start = 0 := by
simp
@[simp]
public theorem stop_mkSlice_rii {xs : Array α} :
xs[*...*].stop = xs.size := by
simp [Std.Rii.Sliceable.mkSlice]
end Array
section SubarraySlices
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 [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]
@[simp]
public theorem toArray_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
xs[lo...hi].toArray = xs.toArray.extract lo hi := by
simp [ Subarray.toArray_toList, List.drop_take]
@[simp]
public theorem mkSlice_rcc_eq_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
xs[lo...=hi] = xs[lo...(hi + 1)] := by
simp [Std.Rcc.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice,
Std.Rcc.HasRcoIntersection.intersection, Std.Rco.HasRcoIntersection.intersection]
@[simp]
public theorem toList_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
xs[lo...=hi].toArray = xs.toArray.extract lo (hi + 1) := by
simp
@[simp]
public theorem mkSlice_rci_eq_mkSlice_rco {xs : Subarray α} {lo : Nat} :
xs[lo...*] = xs[lo...xs.size] := by
simp [Std.Rci.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice,
Std.Rci.HasRcoIntersection.intersection, Std.Rco.HasRcoIntersection.intersection]
@[simp]
public theorem toList_mkSlice_rci {xs : Subarray α} {lo : Nat} :
xs[lo...*].toList = xs.toList.drop lo := by
rw [mkSlice_rci_eq_mkSlice_rco, toList_mkSlice_rco, Subarray.length_toList, List.take_length]
@[simp]
public theorem toArray_mkSlice_rci {xs : Subarray α} {lo : Nat} :
xs[lo...*].toArray = xs.toArray.extract lo := by
simp
@[simp]
public theorem mkSlice_roc_eq_mkSlice_roo {xs : Subarray α} {lo hi : Nat} :
xs[lo<...=hi] = xs[lo<...(hi + 1)] := by
simp [Std.Roc.Sliceable.mkSlice, Std.Roo.Sliceable.mkSlice,
Std.Roc.HasRcoIntersection.intersection, Std.Roo.HasRcoIntersection.intersection]
@[simp]
public theorem mkSlice_roo_eq_mkSlice_rco {xs : Subarray α} {lo hi : Nat} :
xs[lo<...hi] = xs[(lo + 1)...hi] := by
simp [Std.Roo.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice,
Std.Roo.HasRcoIntersection.intersection, Std.Rco.HasRcoIntersection.intersection]
@[simp]
public theorem toList_mkSlice_roo {xs : Subarray α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : Subarray α} {lo hi : Nat} :
xs[lo<...hi].toArray = xs.toArray.extract (lo + 1) hi := by
simp
@[simp]
public theorem mkSlice_roc_eq_mkSlice_rcc {xs : Subarray α} {lo hi : Nat} :
xs[lo<...=hi] = xs[(lo + 1)...=hi] := by
simp [Std.Roc.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice,
Std.Roc.HasRcoIntersection.intersection, Std.Rco.HasRcoIntersection.intersection]
@[simp]
public theorem toList_mkSlice_roc {xs : Subarray α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : Subarray α} {lo hi : Nat} :
xs[lo<...=hi].toArray = xs.toArray.extract (lo + 1) (hi + 1) := by
simp
@[simp]
public theorem mkSlice_roi_eq_mkSlice_rci {xs : Subarray α} {lo : Nat} :
xs[lo<...*] = xs[(lo + 1)...*] := by
simp [Std.Roi.Sliceable.mkSlice, Std.Rci.Sliceable.mkSlice,
Std.Roi.HasRcoIntersection.intersection, Std.Rci.HasRcoIntersection.intersection]
@[simp]
public theorem toList_mkSlice_roi {xs : Subarray α} {lo : Nat} :
xs[lo<...*].toList = xs.toList.drop (lo + 1) := by
rw [mkSlice_roi_eq_mkSlice_rci, toList_mkSlice_rci]
@[simp]
public theorem toArray_mkSlice_roi {xs : Subarray α} {lo : Nat} :
xs[lo<...*].toArray = xs.toArray.extract (lo + 1) := by
simp
@[simp]
public theorem mkSlice_ric_eq_mkSlice_rio {xs : Subarray α} {hi : Nat} :
xs[*...=hi] = xs[*...(hi + 1)] := by
simp [Std.Ric.Sliceable.mkSlice, Std.Rio.Sliceable.mkSlice,
Std.Ric.HasRcoIntersection.intersection, Std.Rio.HasRcoIntersection.intersection]
@[simp]
public theorem mkSlice_rio_eq_mkSlice_rco {xs : Subarray α} {hi : Nat} :
xs[*...hi] = xs[0...hi] := by
simp [Std.Rio.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice,
Std.Rio.HasRcoIntersection.intersection, Std.Rco.HasRcoIntersection.intersection]
@[simp]
public theorem toList_mkSlice_rio {xs : Subarray α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : Subarray α} {hi : Nat} :
xs[*...hi].toArray = xs.toArray.extract 0 hi := by
simp
@[simp]
public theorem mkSlice_ric_eq_mkSlice_rcc {xs : Subarray α} {hi : Nat} :
xs[*...=hi] = xs[0...=hi] := by
simp [Std.Ric.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice,
Std.Ric.HasRcoIntersection.intersection, Std.Rco.HasRcoIntersection.intersection]
@[simp]
public theorem toList_mkSlice_ric {xs : Subarray α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : Subarray α} {hi : Nat} :
xs[*...=hi].toArray = xs.toArray.extract 0 (hi + 1) := by
simp
@[simp]
public theorem mkSlice_rii {xs : Subarray α} :
xs[*...*] = xs := by
simp [Std.Rii.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rii {xs : Subarray α} :
xs[*...*].toList = xs.toList := by
rw [mkSlice_rii]
@[simp]
public theorem toArray_mkSlice_rii {xs : Subarray α} :
xs[*...*].toArray = xs.toArray := by
rw [mkSlice_rii]
end Subarray
end SubarraySlices

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

@@ -49,7 +49,7 @@ Additionally, the starting index is clamped to the ending index.
This function is linear in `start` because it stores `as.drop start` in the slice.
-/
public def List.toSlice (as : List α) (start : Nat) (stop : Nat) : ListSlice α :=
if start stop then
if start < stop then
{ list := as.drop start, stop := some (stop - start) }
else
{ list := [], stop := some 0 }
@@ -66,36 +66,94 @@ public def List.toUnboundedSlice (as : List α) (start : Nat) : ListSlice α :=
public instance : Rcc.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toSlice range.lower (range.upper + 1))
xs.toSlice range.lower (range.upper + 1)
public instance : Rco.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toSlice range.lower range.upper)
xs.toSlice range.lower range.upper
public instance : Rci.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toUnboundedSlice range.lower)
xs.toUnboundedSlice range.lower
public instance : Roc.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toSlice (range.lower + 1) (range.upper + 1))
xs.toSlice (range.lower + 1) (range.upper + 1)
public instance : Roo.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toSlice (range.lower + 1) range.upper)
xs.toSlice (range.lower + 1) range.upper
public instance : Roi.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toUnboundedSlice (range.lower + 1))
xs.toUnboundedSlice (range.lower + 1)
public instance : Ric.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toSlice 0 (range.upper + 1))
xs.toSlice 0 (range.upper + 1)
public instance : Rio.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs range :=
(xs.toSlice 0 range.upper)
xs.toSlice 0 range.upper
public instance : Rii.Sliceable (List α) Nat (ListSlice α) where
mkSlice xs _ :=
(xs.toUnboundedSlice 0)
xs.toUnboundedSlice 0
public instance : Rcc.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
let stop := match xs.internalRepresentation.stop with
| none => range.upper + 1
| some stop => min stop (range.upper + 1)
xs.internalRepresentation.list[range.lower...stop]
public instance : Rco.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
let stop := match xs.internalRepresentation.stop with
| none => range.upper
| some stop => min stop range.upper
xs.internalRepresentation.list[range.lower...stop]
public instance : Rci.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
match xs.internalRepresentation.stop with
| none => xs.internalRepresentation.list[range.lower...*]
| some stop => xs.internalRepresentation.list[range.lower...stop]
public instance : Roc.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
let stop := match xs.internalRepresentation.stop with
| none => range.upper + 1
| some stop => min stop (range.upper + 1)
xs.internalRepresentation.list[range.lower<...stop]
public instance : Roo.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
let stop := match xs.internalRepresentation.stop with
| none => range.upper
| some stop => min stop range.upper
xs.internalRepresentation.list[range.lower<...stop]
public instance : Roi.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
match xs.internalRepresentation.stop with
| none => xs.internalRepresentation.list[range.lower<...*]
| some stop => xs.internalRepresentation.list[range.lower<...stop]
public instance : Ric.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
let stop := match xs.internalRepresentation.stop with
| none => range.upper + 1
| some stop => min stop (range.upper + 1)
xs.internalRepresentation.list[*...stop]
public instance : Rio.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs range :=
let stop := match xs.internalRepresentation.stop with
| none => range.upper
| some stop => min stop range.upper
xs.internalRepresentation.list[*...stop]
public instance : Rii.Sliceable (ListSlice α) Nat (ListSlice α) where
mkSlice xs _ :=
xs

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,4 +42,355 @@ public instance : LawfulSliceSize (Internal.ListSliceData α) where
lawful s := by
simp [ internalIter_eq_toIteratorIter, SliceSize.size]
end Std.Slice.List
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 [Std.Slice.toList, toList_internalIter]
rfl
public theorem toArray_toList {xs : ListSlice α} :
xs.toList.toArray = xs.toArray := by
simp [Std.Slice.toArray, Std.Slice.toList]
public theorem toList_toArray {xs : ListSlice α} :
xs.toArray.toList = xs.toList := by
simp [Std.Slice.toArray, Std.Slice.toList]
@[simp]
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,
toList_internalIter]; rfl
@[simp]
public theorem size_toArray {xs : ListSlice α} :
xs.toArray.size = xs.size := by
simp [ ListSlice.toArray_toList]
end ListSlice
namespace List
@[simp]
public theorem toList_mkSlice_rco {xs : List α} {lo hi : Nat} :
xs[lo...hi].toList = (xs.take hi).drop lo := by
rw [List.take_eq_take_min, List.drop_eq_drop_min]
simp only [Std.Rco.Sliceable.mkSlice, toSlice, ListSlice.toList_eq]
by_cases h : lo < hi
· have : lo hi := by omega
simp [h, List.take_drop, Nat.add_sub_cancel' _, List.take_eq_take_min]
· have : min hi xs.length lo := by omega
simp [h, Nat.min_eq_right this]
@[simp]
public theorem toArray_mkSlice_rco {xs : List α} {lo hi : Nat} :
xs[lo...hi].toArray = ((xs.take hi).drop lo).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_rco {xs : List α} {lo hi : Nat} :
xs[lo...hi].size = min hi xs.length - lo := by
simp [ ListSlice.length_toList]
@[simp]
public theorem mkSlice_rcc_eq_mkSlice_rco {xs : List α} {lo hi : Nat} :
xs[lo...=hi] = xs[lo...(hi + 1)] := by
simp [Std.Rcc.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rcc {xs : List α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.take (hi + 1)).drop lo := by
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : List α} {lo hi : Nat} :
xs[lo...=hi].toArray = ((xs.take (hi + 1)).drop lo).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_rcc {xs : List α} {lo hi : Nat} :
xs[lo...=hi].size = min (hi + 1) xs.length - lo := by
simp [ ListSlice.length_toList]
@[simp]
public theorem toList_mkSlice_rci {xs : List α} {lo : Nat} :
xs[lo...*].toList = xs.drop lo := by
rw [List.drop_eq_drop_min]
simp [ListSlice.toList_eq, Std.Rci.Sliceable.mkSlice, List.toUnboundedSlice]
@[simp]
public theorem toArray_mkSlice_rci {xs : List α} {lo : Nat} :
xs[lo...*].toArray = (xs.drop lo).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_rci {xs : List α} {lo : Nat} :
xs[lo...*].size = xs.length - lo := by
simp [ ListSlice.length_toList]
@[simp]
public theorem mkSlice_roo_eq_mkSlice_rco {xs : List α} {lo hi : Nat} :
xs[lo<...hi] = xs[(lo + 1)...hi] := by
simp [Std.Roo.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_roo {xs : List α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.take hi).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : List α} {lo hi : Nat} :
xs[lo<...hi].toArray = ((xs.take hi).drop (lo + 1)).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_roo {xs : List α} {lo hi : Nat} :
xs[lo<...hi].size = min hi xs.length - (lo + 1) := by
simp [ ListSlice.length_toList]
@[simp]
public theorem mkSlice_roc_eq_mkSlice_roo {xs : List α} {lo hi : Nat} :
xs[lo<...=hi] = xs[lo<...(hi + 1)] := by
simp [Std.Roc.Sliceable.mkSlice, Std.Roo.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_roc {xs : List α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.take (hi + 1)).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : List α} {lo hi : Nat} :
xs[lo<...=hi].toArray = ((xs.take (hi + 1)).drop (lo + 1)).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_roc {xs : List α} {lo hi : Nat} :
xs[lo<...=hi].size = min (hi + 1) xs.length - (lo + 1) := by
simp [ ListSlice.length_toList]
@[simp]
public theorem mkSlice_roi_eq_mkSlice_rci {xs : List α} {lo : Nat} :
xs[lo<...*] = xs[(lo + 1)...*] := by
simp [Std.Roi.Sliceable.mkSlice, Std.Rci.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_roi {xs : List α} {lo : Nat} :
xs[lo<...*].toList = xs.drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roi {xs : List α} {lo : Nat} :
xs[lo<...*].toArray = (xs.drop (lo + 1)).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_roi {xs : List α} {lo : Nat} :
xs[lo<...*].size = xs.length - (lo + 1) := by
simp [ ListSlice.length_toList]
@[simp]
public theorem mkSlice_rio_eq_mkSlice_rco {xs : List α} {hi : Nat} :
xs[*...hi] = xs[0...hi] := by
simp [Std.Rio.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rio {xs : List α} {hi : Nat} :
xs[*...hi].toList = xs.take hi := by
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : List α} {hi : Nat} :
xs[*...hi].toArray = (xs.take hi).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_rio {xs : List α} {hi : Nat} :
xs[*...hi].size = min hi xs.length := by
simp [ ListSlice.length_toList]
@[simp]
public theorem mkSlice_ric_eq_mkSlice_rio {xs : List α} {hi : Nat} :
xs[*...=hi] = xs[*...(hi + 1)] := by
simp [Std.Ric.Sliceable.mkSlice, Std.Rio.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_ric {xs : List α} {hi : Nat} :
xs[*...=hi].toList = xs.take (hi + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : List α} {hi : Nat} :
xs[*...=hi].toArray = (xs.take (hi + 1)).toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_ric {xs : List α} {hi : Nat} :
xs[*...=hi].size = min (hi + 1) xs.length := by
simp [ ListSlice.length_toList]
@[simp]
public theorem mkSlice_rii_eq_mkSlice_rci {xs : List α} :
xs[*...*] = xs[0...*] := by
simp [Std.Rii.Sliceable.mkSlice, Std.Rci.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rii {xs : List α} :
xs[*...*].toList = xs := by
simp
@[simp]
public theorem toArray_mkSlice_rii {xs : List α} :
xs[*...*].toArray = xs.toArray := by
simp [ ListSlice.toArray_toList]
@[simp]
public theorem size_mkSlice_rii {xs : List α} :
xs[*...*].size = xs.length := by
simp [ ListSlice.length_toList]
end List
section ListSubslices
namespace ListSlice
@[simp]
public theorem toList_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
xs[lo...hi].toList = (xs.toList.take hi).drop lo := by
simp only [instSliceableListSliceNat_1, List.toList_mkSlice_rco, ListSlice.toList_eq (xs := xs)]
obtain xs, stop := xs
cases stop
· simp
· simp [List.take_take, Nat.min_comm]
@[simp]
public theorem toArray_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
xs[lo...hi].toArray = xs.toArray.extract lo hi := by
simp [ toArray_toList, List.drop_take]
@[simp]
public theorem mkSlice_rcc_eq_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
xs[lo...=hi] = xs[lo...(hi + 1)] := by
simp [Std.Rcc.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
xs[lo...=hi].toList = (xs.toList.take (hi + 1)).drop lo := by
simp
@[simp]
public theorem toArray_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
xs[lo...=hi].toArray = xs.toArray.extract lo (hi + 1) := by
simp [ ListSlice.toArray_toList, List.drop_take]
@[simp]
public theorem toList_mkSlice_rci {xs : ListSlice α} {lo : Nat} :
xs[lo...*].toList = xs.toList.drop lo := by
simp only [instSliceableListSliceNat_2, ListSlice.toList_eq (xs := xs)]
obtain xs, stop := xs
simp only
split <;> simp
@[simp]
public theorem toArray_mkSlice_rci {xs : ListSlice α} {lo : Nat} :
xs[lo...*].toArray = xs.toArray.extract lo := by
simp only [ toArray_toList, toList_mkSlice_rci]
rw (occs := [1]) [ List.take_length (l := List.drop lo xs.toList)]
simp
@[simp]
public theorem mkSlice_roo_eq_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...hi] = xs[(lo + 1)...hi] := by
simp [Std.Roo.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_roo {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...hi].toList = (xs.toList.take hi).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roo {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...hi].toArray = xs.toArray.extract (lo + 1) hi := by
simp [ toArray_toList, List.drop_take]
@[simp]
public theorem mkSlice_roc_eq_mkSlice_roo {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...=hi] = xs[lo<...(hi + 1)] := by
simp [Std.Roc.Sliceable.mkSlice, Std.Roo.Sliceable.mkSlice]
@[simp]
public theorem mkSlice_roc_eq_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...=hi] = xs[(lo + 1)...=hi] := by
simp [Std.Roc.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_roc {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...=hi].toList = (xs.toList.take (hi + 1)).drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roc {xs : ListSlice α} {lo hi : Nat} :
xs[lo<...=hi].toArray = xs.toArray.extract (lo + 1) (hi + 1) := by
simp [ toArray_toList, List.drop_take]
@[simp]
public theorem mkSlice_roi_eq_mkSlice_rci {xs : ListSlice α} {lo : Nat} :
xs[lo<...*] = xs[(lo + 1)...*] := by
simp [Std.Roi.Sliceable.mkSlice, Std.Rci.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_roi {xs : ListSlice α} {lo : Nat} :
xs[lo<...*].toList = xs.toList.drop (lo + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_roi {xs : ListSlice α} {lo : Nat} :
xs[lo<...*].toArray = xs.toArray.extract (lo + 1) := by
simp only [ toArray_toList, toList_mkSlice_roi]
rw (occs := [1]) [ List.take_length (l := List.drop (lo + 1) xs.toList)]
simp
@[simp]
public theorem mkSlice_rio_eq_mkSlice_rco {xs : ListSlice α} {hi : Nat} :
xs[*...hi] = xs[0...hi] := by
simp [Std.Rio.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_rio {xs : ListSlice α} {hi : Nat} :
xs[*...hi].toList = xs.toList.take hi := by
simp
@[simp]
public theorem toArray_mkSlice_rio {xs : ListSlice α} {hi : Nat} :
xs[*...hi].toArray = xs.toArray.extract 0 hi := by
simp [ toArray_toList]
@[simp]
public theorem mkSlice_ric_eq_mkSlice_rio {xs : ListSlice α} {hi : Nat} :
xs[*...=hi] = xs[*...(hi + 1)] := by
simp [Std.Ric.Sliceable.mkSlice, Std.Rio.Sliceable.mkSlice]
@[simp]
public theorem mkSlice_ric_eq_mkSlice_rcc {xs : ListSlice α} {hi : Nat} :
xs[*...=hi] = xs[0...=hi] := by
simp [Std.Ric.Sliceable.mkSlice, Std.Rco.Sliceable.mkSlice]
@[simp]
public theorem toList_mkSlice_ric {xs : ListSlice α} {hi : Nat} :
xs[*...=hi].toList = xs.toList.take (hi + 1) := by
simp
@[simp]
public theorem toArray_mkSlice_ric {xs : ListSlice α} {hi : Nat} :
xs[*...=hi].toArray = xs.toArray.extract 0 (hi + 1) := by
simp [ toArray_toList]
@[simp]
public theorem mkSlice_rii {xs : ListSlice α} :
xs[*...*] = xs := by
simp [Std.Rii.Sliceable.mkSlice]
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
@@ -26,3 +25,4 @@ public import Init.Data.String.Termination
public import Init.Data.String.ToSlice
public import Init.Data.String.Search
public import Init.Data.String.Legacy
public import Init.Data.String.Grind

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

@@ -0,0 +1,112 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.String.Defs
public import Init.Grind.ToInt
public section
/-!
# Register string positions with `grind`.
-/
namespace String
namespace Internal
scoped macro "order" : tactic => `(tactic| {
simp [Pos.Raw.lt_iff, Pos.Raw.le_iff, String.Pos.lt_iff, String.Pos.le_iff, Slice.Pos.lt_iff,
Slice.Pos.le_iff, Pos.Raw.ext_iff, String.Pos.ext_iff, Slice.Pos.ext_iff] at *;
try omega })
end Internal
open Internal
namespace Pos.Raw
instance : Lean.Grind.ToInt String.Pos.Raw (.ci 0) where
toInt p := p.byteIdx
toInt_inj p q := by simp [Pos.Raw.ext_iff, Int.ofNat_inj]
toInt_mem := by simp
@[simp]
theorem toInt_eq {p : Pos.Raw} : Lean.Grind.ToInt.toInt p = p.byteIdx := rfl
instance : Lean.Grind.ToInt.LE String.Pos.Raw (.ci 0) where
le_iff := by simp [Pos.Raw.le_iff]
instance : Lean.Grind.ToInt.LT String.Pos.Raw (.ci 0) where
lt_iff := by simp [Pos.Raw.lt_iff]
instance : Std.LawfulOrderLT String.Pos.Raw where
lt_iff := by order
instance : Std.IsLinearOrder String.Pos.Raw where
le_refl := by order
le_trans := by order
le_antisymm := by order
le_total := by order
end Pos.Raw
namespace Pos
instance {s : String} : Lean.Grind.ToInt s.Pos (.co 0 (s.utf8ByteSize + 1)) where
toInt p := p.offset.byteIdx
toInt_inj p q := by simp [Pos.ext_iff, Pos.Raw.ext_iff, Int.ofNat_inj]
toInt_mem p := by have := p.isValid.le_utf8ByteSize; simp; omega
@[simp]
theorem toInt_eq {s : String} {p : s.Pos} : Lean.Grind.ToInt.toInt p = p.offset.byteIdx := rfl
instance {s : String} : Lean.Grind.ToInt.LE s.Pos (.co 0 (s.utf8ByteSize + 1)) where
le_iff := by simp [Pos.le_iff, Pos.Raw.le_iff]
instance {s : String} : Lean.Grind.ToInt.LT s.Pos (.co 0 (s.utf8ByteSize + 1)) where
lt_iff := by simp [Pos.lt_iff, Pos.Raw.lt_iff]
instance {s : String} : Std.LawfulOrderLT s.Pos where
lt_iff := by order
instance {s : String} : Std.IsLinearOrder s.Pos where
le_refl := by order
le_trans := by order
le_antisymm := by order
le_total := by order
end Pos
namespace Slice.Pos
instance {s : Slice} : Lean.Grind.ToInt s.Pos (.co 0 (s.utf8ByteSize + 1)) where
toInt p := p.offset.byteIdx
toInt_inj p q := by simp [Pos.ext_iff, Pos.Raw.ext_iff, Int.ofNat_inj]
toInt_mem p := by have := p.isValidForSlice.le_utf8ByteSize; simp; omega
@[simp]
theorem toInt_eq {s : Slice} {p : s.Pos} : Lean.Grind.ToInt.toInt p = p.offset.byteIdx := rfl
instance {s : Slice} : Lean.Grind.ToInt.LE s.Pos (.co 0 (s.utf8ByteSize + 1)) where
le_iff := by simp [Pos.le_iff, Pos.Raw.le_iff]
instance {s : Slice} : Lean.Grind.ToInt.LT s.Pos (.co 0 (s.utf8ByteSize + 1)) where
lt_iff := by simp [Pos.lt_iff, Pos.Raw.lt_iff]
instance {s : Slice} : Std.LawfulOrderLT s.Pos where
lt_iff := by order
instance {s : Slice} : Std.IsLinearOrder s.Pos where
le_refl := by order
le_trans := by order
le_antisymm := by order
le_total := by order
end Slice.Pos
end String

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,41 +72,41 @@ 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) :
p.Splits (s.replaceEnd p).copy (s.replaceStart p).copy where
eq_append := by simp [ bytes_inj, Slice.bytes_copy, size_bytes]
theorem Pos.splits {s : String} (p : s.Pos) :
p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where
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) :
p.Splits (s.replaceEnd p).copy (s.replaceStart p).copy where
eq_append := copy_eq_copy_replaceEnd
p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where
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,55 +175,55 @@ 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) :
p.Splits (s.replaceEnd p).copy (singleton (p.get hp) ++ (s.replaceStart (p.next hp)).copy) where
eq_append := by simpa [ append_assoc] using p.eq_copy_replaceEnd_append_get hp
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) :
(p.next hp).Splits ((s.replaceEnd p).copy ++ singleton (p.get hp)) (s.replaceStart (p.next hp)).copy where
eq_append := p.eq_copy_replaceEnd_append_get hp
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
theorem Slice.Pos.splits_next_right {s : Slice} (p : s.Pos) (hp : p s.endPos) :
p.Splits (s.replaceEnd p).copy (singleton (p.get hp) ++ (s.replaceStart (p.next hp)).copy) where
eq_append := by simpa [ append_assoc] using p.copy_eq_copy_replaceEnd_append_get hp
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.copy_eq_copy_sliceTo_append_get hp
offset_eq_rawEndPos := by simp
theorem Slice.Pos.splits_next {s : Slice} (p : s.Pos) (hp : p s.endPos) :
(p.next hp).Splits ((s.replaceEnd p).copy ++ singleton (p.get hp)) (s.replaceStart (p.next hp)).copy where
eq_append := p.copy_eq_copy_replaceEnd_append_get hp
(p.next hp).Splits ((s.sliceTo p).copy ++ singleton (p.get hp)) (s.sliceFrom (p.next hp)).copy where
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₂' :=
(s.replaceStart (p.next hp)).copy, h.eq_right (p.splits_next_right hp)
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) :=
(s.replaceEnd p).copy, h.eq_left (p.splits_next 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}
(hp : p s.endPos) (h : p.Splits t₁ t₂) : t₂', t₂ = singleton (p.get hp) ++ t₂' :=
(s.replaceStart (p.next hp)).copy, h.eq_right (p.splits_next_right hp)
(s.sliceFrom (p.next hp)).copy, h.eq_right (p.splits_next_right hp)
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.replaceEnd p).copy, h.eq_left (p.splits_next 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,69 +33,69 @@ 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.replaceEnd p).copy ++ singleton c ++ (s.replaceStart (p.next hp)).copy
(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} :
p.set c hp = (s.replaceEnd p).copy ++ singleton c ++ (s.replaceStart (p.next hp)).copy := by
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_stringReplaceEnd]
rw [isValid_copy_iff, isValidForSlice_stringSliceTo]
exact hpq, hq
/-- 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} :
(p.pastSet c hp).Splits ((s.replaceEnd p).copy ++ singleton c) (s.replaceStart (p.next hp)).copy where
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

@@ -44,13 +44,13 @@ deriving Inhabited, BEq
Provides a conversion from a pattern to an iterator of {name}`SearchStep` that searches for matches
of the pattern from the start towards the end of a {name}`Slice`.
-/
class ToForwardSearcher (ρ : Type) (σ : outParam (Slice Type)) where
class ToForwardSearcher {ρ : Type} (pat : ρ) (σ : outParam (Slice Type)) where
/--
Builds an iterator of {name}`SearchStep` corresponding to matches of {name}`pat` along the slice
{name}`s`. The {name}`SearchStep`s returned by this iterator must contain ranges that are
adjacent, non-overlapping and cover all of {name}`s`.
-/
toSearcher : (s : Slice) (pat : ρ) Std.Iter (α := σ s) (SearchStep s)
toSearcher : (s : Slice) Std.Iter (α := σ s) (SearchStep s)
/--
Provides simple pattern matching capabilities from the start of a {name}`Slice`.
@@ -61,16 +61,16 @@ need to specialize in this fashion, then
{name (scope := "Init.Data.String.Pattern.Basic")}`ForwardPattern.defaultImplementation` can be used
to automatically derive an instance.
-/
class ForwardPattern (ρ : Type) where
class ForwardPattern {ρ : Type} (pat : ρ) where
/--
Checks whether the slice starts with the pattern.
-/
startsWith : Slice ρ Bool
startsWith : Slice Bool
/--
Checks whether the slice starts with the pattern. If it does, the slice is returned with the
prefix removed; otherwise the result is {name}`none`.
-/
dropPrefix? : (s : Slice) ρ Option s.Pos
dropPrefix? : (s : Slice) Option s.Pos
namespace Internal
@@ -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
@@ -126,26 +126,26 @@ namespace ForwardPattern
variable {ρ : Type} {σ : Slice Type}
variable [ s, Std.Iterators.Iterator (σ s) Id (SearchStep s)]
variable [ToForwardSearcher ρ σ]
variable (pat : ρ) [ToForwardSearcher pat σ]
@[specialize pat]
def defaultStartsWith (s : Slice) (pat : ρ) : Bool :=
let searcher := ToForwardSearcher.toSearcher s pat
def defaultStartsWith (s : Slice) : Bool :=
let searcher := ToForwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched start ..) _ => s.startPos = start
| _ => false
@[specialize pat]
def defaultDropPrefix? (s : Slice) (pat : ρ) : Option s.Pos :=
let searcher := ToForwardSearcher.toSearcher s pat
def defaultDropPrefix? (s : Slice) : Option s.Pos :=
let searcher := ToForwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched _ endPos) _ => some endPos
| _ => none
@[always_inline, inline]
def defaultImplementation : ForwardPattern ρ where
startsWith := defaultStartsWith
dropPrefix? := defaultDropPrefix?
def defaultImplementation {pat : ρ} [ToForwardSearcher pat σ] : ForwardPattern pat where
startsWith := defaultStartsWith pat
dropPrefix? := defaultDropPrefix? pat
end ForwardPattern
@@ -153,13 +153,13 @@ end ForwardPattern
Provides a conversion from a pattern to an iterator of {name}`SearchStep` searching for matches of
the pattern from the end towards the start of a {name}`Slice`.
-/
class ToBackwardSearcher (ρ : Type) (σ : outParam (Slice Type)) where
class ToBackwardSearcher {ρ : Type} (pat : ρ) (σ : outParam (Slice Type)) where
/--
Build an iterator of {name}`SearchStep` corresponding to matches of {lean}`pat` along the slice
{name}`s`. The {name}`SearchStep`s returned by this iterator must contain ranges that are
adjacent, non-overlapping and cover all of {name}`s`.
-/
toSearcher : (s : Slice) (pat : ρ) Std.Iter (α := σ s) (SearchStep s)
toSearcher : (s : Slice) Std.Iter (α := σ s) (SearchStep s)
/--
Provides simple pattern matching capabilities from the end of a {name}`Slice`.
@@ -170,41 +170,41 @@ need to specialize in this fashion, then
{name (scope := "Init.Data.String.Pattern.Basic")}`BackwardPattern.defaultImplementation` can be
used to automatically derive an instance.
-/
class BackwardPattern (ρ : Type) where
class BackwardPattern {ρ : Type} (pat : ρ) where
/--
Checks whether the slice ends with the pattern.
-/
endsWith : Slice ρ Bool
endsWith : Slice Bool
/--
Checks whether the slice ends with the pattern. If it does, the slice is returned with the
suffix removed; otherwise the result is {name}`none`.
-/
dropSuffix? : (s : Slice) ρ Option s.Pos
dropSuffix? : (s : Slice) Option s.Pos
namespace ToBackwardSearcher
variable {ρ : Type} {σ : Slice Type}
variable [ s, Std.Iterators.Iterator (σ s) Id (SearchStep s)]
variable [ToBackwardSearcher ρ σ]
variable (pat : ρ) [ToBackwardSearcher pat σ]
@[specialize pat]
def defaultEndsWith (s : Slice) (pat : ρ) : Bool :=
let searcher := ToBackwardSearcher.toSearcher s pat
def defaultEndsWith (s : Slice) : Bool :=
let searcher := ToBackwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched _ endPos) _ => s.endPos = endPos
| _ => false
@[specialize pat]
def defaultDropSuffix? (s : Slice) (pat : ρ) : Option s.Pos :=
let searcher := ToBackwardSearcher.toSearcher s pat
def defaultDropSuffix? (s : Slice) : Option s.Pos :=
let searcher := ToBackwardSearcher.toSearcher pat s
match searcher.step with
| .yield _ (.matched startPos _) _ => some startPos
| _ => none
@[always_inline, inline]
def defaultImplementation : BackwardPattern ρ where
endsWith := defaultEndsWith
dropSuffix? := defaultDropSuffix?
def defaultImplementation {pat : ρ} [ToBackwardSearcher pat σ] : BackwardPattern pat where
endsWith := defaultEndsWith pat
dropSuffix? := defaultDropSuffix? pat
end ToBackwardSearcher

View File

@@ -21,46 +21,44 @@ public section
namespace String.Slice.Pattern
structure ForwardCharSearcher (s : Slice) where
structure ForwardCharSearcher (needle : Char) (s : Slice) where
currPos : s.Pos
needle : Char
deriving Inhabited
namespace ForwardCharSearcher
@[inline]
def iter (s : Slice) (c : Char) : Std.Iter (α := ForwardCharSearcher s) (SearchStep s) :=
{ internalState := { currPos := s.startPos, needle := c }}
def iter (c : Char) (s : Slice) : Std.Iter (α := ForwardCharSearcher c s) (SearchStep s) :=
{ internalState := { currPos := s.startPos }}
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharSearcher s) Id (SearchStep s) where
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharSearcher c s) Id (SearchStep s) where
IsPlausibleStep it
| .yield it' out =>
it.internalState.needle = it'.internalState.needle
h1 : it.internalState.currPos s.endPos,
it'.internalState.currPos = it.internalState.currPos.next h1
match out with
| .matched startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
it.internalState.currPos.get h1 = it.internalState.needle
it.internalState.currPos.get h1 = c
| .rejected startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
it.internalState.currPos.get h1 it.internalState.needle
it.internalState.currPos.get h1 c
| .skip _ => False
| .done => it.internalState.currPos = s.endPos
step := fun currPos, needle =>
step := fun currPos =>
if h1 : currPos = s.endPos then
pure (.deflate .done, by simp [h1])
else
let nextPos := currPos.next h1
let nextIt := nextPos, needle
if h2 : currPos.get h1 = needle then
let nextIt := nextPos
if h2 : currPos.get h1 = c then
pure (.deflate .yield nextIt (.matched currPos nextPos), by simp [h1, h2, nextIt, nextPos])
else
pure (.deflate .yield nextIt (.rejected currPos nextPos), by simp [h1, h2, nextIt, nextPos])
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharSearcher s) Id where
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharSearcher s c) Id where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.currPos)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
@@ -68,21 +66,21 @@ def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharSearcher s
obtain step, h, h' := h
cases step
· cases h
obtain _, h1, h2, _ := h'
obtain _, h2, _ := h'
simp [h2]
· cases h'
· cases h
instance : Std.Iterators.Finite (ForwardCharSearcher s) Id :=
instance : Std.Iterators.Finite (ForwardCharSearcher s c) Id :=
.of_finitenessRelation finitenessRelation
instance : Std.Iterators.IteratorLoop (ForwardCharSearcher s) Id Id :=
instance : Std.Iterators.IteratorLoop (ForwardCharSearcher s c) Id Id :=
.defaultImplementation
instance : ToForwardSearcher Char ForwardCharSearcher where
toSearcher := iter
instance {c : Char} : ToForwardSearcher c (ForwardCharSearcher c) where
toSearcher := iter c
instance : ForwardPattern Char := .defaultImplementation
instance {c : Char} : ForwardPattern c := .defaultImplementation
end ForwardCharSearcher
@@ -94,7 +92,7 @@ deriving Inhabited
namespace BackwardCharSearcher
@[inline]
def iter (s : Slice) (c : Char) : Std.Iter (α := BackwardCharSearcher s) (SearchStep s) :=
def iter (c : Char) (s : Slice) : Std.Iter (α := BackwardCharSearcher s) (SearchStep s) :=
{ internalState := { currPos := s.endPos, needle := c }}
instance (s : Slice) : Std.Iterators.Iterator (BackwardCharSearcher s) Id (SearchStep s) where
@@ -144,10 +142,10 @@ instance : Std.Iterators.Finite (BackwardCharSearcher s) Id :=
instance : Std.Iterators.IteratorLoop (BackwardCharSearcher s) Id Id :=
.defaultImplementation
instance : ToBackwardSearcher Char BackwardCharSearcher where
toSearcher := iter
instance {c : Char} : ToBackwardSearcher c BackwardCharSearcher where
toSearcher := iter c
instance : BackwardPattern Char := ToBackwardSearcher.defaultImplementation
instance {c : Char} : BackwardPattern c := ToBackwardSearcher.defaultImplementation
end BackwardCharSearcher

View File

@@ -22,47 +22,45 @@ public section
namespace String.Slice.Pattern
structure ForwardCharPredSearcher (s : Slice) where
structure ForwardCharPredSearcher (p : Char Bool) (s : Slice) where
currPos : s.Pos
needle : Char Bool
deriving Inhabited
namespace ForwardCharPredSearcher
@[inline]
def iter (s : Slice) (p : Char Bool) : Std.Iter (α := ForwardCharPredSearcher s) (SearchStep s) :=
{ internalState := { currPos := s.startPos, needle := p }}
def iter (p : Char Bool) (s : Slice) : Std.Iter (α := ForwardCharPredSearcher p s) (SearchStep s) :=
{ internalState := { currPos := s.startPos }}
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharPredSearcher s) Id (SearchStep s) where
instance (s : Slice) : Std.Iterators.Iterator (ForwardCharPredSearcher p s) Id (SearchStep s) where
IsPlausibleStep it
| .yield it' out =>
it.internalState.needle = it'.internalState.needle
h1 : it.internalState.currPos s.endPos,
it'.internalState.currPos = it.internalState.currPos.next h1
match out with
| .matched startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
it.internalState.needle (it.internalState.currPos.get h1)
p (it.internalState.currPos.get h1)
| .rejected startPos endPos =>
it.internalState.currPos = startPos
it'.internalState.currPos = endPos
¬ it.internalState.needle (it.internalState.currPos.get h1)
¬ p (it.internalState.currPos.get h1)
| .skip _ => False
| .done => it.internalState.currPos = s.endPos
step := fun currPos, needle =>
step := fun currPos =>
if h1 : currPos = s.endPos then
pure (.deflate .done, by simp [h1])
else
let nextPos := currPos.next h1
let nextIt := nextPos, needle
if h2 : needle <| currPos.get h1 then
let nextIt := nextPos
if h2 : p <| currPos.get h1 then
pure (.deflate .yield nextIt (.matched currPos nextPos), by simp [h1, h2, nextPos, nextIt])
else
pure (.deflate .yield nextIt (.rejected currPos nextPos), by simp [h1, h2, nextPos, nextIt])
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharPredSearcher s) Id where
def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharPredSearcher p s) Id where
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.currPos)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
@@ -70,21 +68,27 @@ def finitenessRelation : Std.Iterators.FinitenessRelation (ForwardCharPredSearch
obtain step, h, h' := h
cases step
· cases h
obtain _, h1, h2, _ := h'
obtain _, h2, _ := h'
simp [h2]
· cases h'
· cases h
instance : Std.Iterators.Finite (ForwardCharPredSearcher s) Id :=
instance : Std.Iterators.Finite (ForwardCharPredSearcher p s) Id :=
.of_finitenessRelation finitenessRelation
instance : Std.Iterators.IteratorLoop (ForwardCharPredSearcher s) Id Id :=
instance : Std.Iterators.IteratorLoop (ForwardCharPredSearcher p s) Id Id :=
.defaultImplementation
instance : ToForwardSearcher (Char Bool) ForwardCharPredSearcher where
toSearcher := iter
instance {p : Char Bool} : ToForwardSearcher p (ForwardCharPredSearcher p) where
toSearcher := iter p
instance : ForwardPattern (Char Bool) := .defaultImplementation
instance {p : Char Bool} : ForwardPattern p := .defaultImplementation
instance {p : Char Prop} [DecidablePred p] : ToForwardSearcher p (ForwardCharPredSearcher p) where
toSearcher := iter (decide <| p ·)
instance {p : Char Prop} [DecidablePred p] : ForwardPattern p :=
.defaultImplementation
end ForwardCharPredSearcher
@@ -96,7 +100,7 @@ deriving Inhabited
namespace BackwardCharPredSearcher
@[inline]
def iter (s : Slice) (c : Char Bool) : Std.Iter (α := BackwardCharPredSearcher s) (SearchStep s) :=
def iter (c : Char Bool) (s : Slice) : Std.Iter (α := BackwardCharPredSearcher s) (SearchStep s) :=
{ internalState := { currPos := s.endPos, needle := c }}
instance (s : Slice) : Std.Iterators.Iterator (BackwardCharPredSearcher s) Id (SearchStep s) where
@@ -149,10 +153,16 @@ instance : Std.Iterators.Finite (BackwardCharPredSearcher s) Id :=
instance : Std.Iterators.IteratorLoop (BackwardCharPredSearcher s) Id Id :=
.defaultImplementation
instance : ToBackwardSearcher (Char Bool) BackwardCharPredSearcher where
toSearcher := iter
instance {p : Char Bool} : ToBackwardSearcher p BackwardCharPredSearcher where
toSearcher := iter p
instance : BackwardPattern (Char Bool) := ToBackwardSearcher.defaultImplementation
instance {p : Char Bool} : BackwardPattern p := ToBackwardSearcher.defaultImplementation
instance {p : Char Prop} [DecidablePred p] : ToBackwardSearcher p BackwardCharPredSearcher where
toSearcher := iter (decide <| p ·)
instance {p : Char Prop} [DecidablePred p] : BackwardPattern p :=
ToBackwardSearcher.defaultImplementation
end BackwardCharPredSearcher

View File

@@ -84,7 +84,7 @@ inductive _root_.String.Slice.Pattern.ForwardSliceSearcher (s : Slice) where
deriving Inhabited
@[inline]
def iter (s : Slice) (pat : Slice) : Std.Iter (α := ForwardSliceSearcher s) (SearchStep s) :=
def iter (pat : Slice) (s : Slice) : Std.Iter (α := ForwardSliceSearcher s) (SearchStep s) :=
if h : pat.utf8ByteSize = 0 then
{ internalState := .emptyBefore s.startPos }
else
@@ -259,11 +259,11 @@ instance : Std.Iterators.IteratorCollect (ForwardSliceSearcher s) Id Id :=
instance : Std.Iterators.IteratorLoop (ForwardSliceSearcher s) Id Id :=
.defaultImplementation
instance : ToForwardSearcher Slice ForwardSliceSearcher where
toSearcher := iter
instance {pat : Slice} : ToForwardSearcher pat ForwardSliceSearcher where
toSearcher := iter pat
@[inline]
def startsWith (s : Slice) (pat : Slice) : Bool :=
def startsWith (pat : Slice) (s : Slice) : Bool :=
if h : pat.utf8ByteSize s.utf8ByteSize then
have hs := by
simp [Pos.Raw.le_iff] at h
@@ -275,29 +275,29 @@ def startsWith (s : Slice) (pat : Slice) : Bool :=
false
@[inline]
def dropPrefix? (s : Slice) (pat : Slice) : Option s.Pos :=
if startsWith s pat then
def dropPrefix? (pat : Slice) (s : Slice) : Option s.Pos :=
if startsWith pat s then
some <| s.pos! <| pat.rawEndPos.offsetBy s.startPos.offset
else
none
instance : ForwardPattern Slice where
startsWith := startsWith
dropPrefix? := dropPrefix?
instance {pat : Slice} : ForwardPattern pat where
startsWith := startsWith pat
dropPrefix? := dropPrefix? pat
instance : ToForwardSearcher String ForwardSliceSearcher where
toSearcher slice pat := iter slice pat.toSlice
instance {pat : String} : ToForwardSearcher pat ForwardSliceSearcher where
toSearcher := iter pat.toSlice
instance : ForwardPattern String where
startsWith s pat := startsWith s pat.toSlice
dropPrefix? s pat := dropPrefix? s pat.toSlice
instance {pat : String} : ForwardPattern pat where
startsWith := startsWith pat.toSlice
dropPrefix? := dropPrefix? pat.toSlice
end ForwardSliceSearcher
namespace BackwardSliceSearcher
@[inline]
def endsWith (s : Slice) (pat : Slice) : Bool :=
def endsWith (pat : Slice) (s : Slice) : Bool :=
if h : pat.utf8ByteSize s.utf8ByteSize then
let sStart := s.endPos.offset.unoffsetBy pat.rawEndPos
let patStart := pat.startPos.offset
@@ -311,19 +311,19 @@ def endsWith (s : Slice) (pat : Slice) : Bool :=
false
@[inline]
def dropSuffix? (s : Slice) (pat : Slice) : Option s.Pos :=
if endsWith s pat then
def dropSuffix? (pat : Slice) (s : Slice) : Option s.Pos :=
if endsWith pat s then
some <| s.pos! <| s.endPos.offset.unoffsetBy pat.rawEndPos
else
none
instance : BackwardPattern Slice where
endsWith := endsWith
dropSuffix? := dropSuffix?
instance {pat : Slice} : BackwardPattern pat where
endsWith := endsWith pat
dropSuffix? := dropSuffix? pat
instance : BackwardPattern String where
endsWith s pat := endsWith s pat.toSlice
dropSuffix? s pat := dropSuffix? s pat.toSlice
instance {pat : String} : BackwardPattern pat where
endsWith := endsWith pat.toSlice
dropSuffix? := dropSuffix? pat.toSlice
end BackwardSliceSearcher

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

@@ -47,7 +47,7 @@ Examples:
* {lean}`"abc".replace "" "k" = "kakbkck"`
-/
@[inline]
def replace [ToForwardSearcher ρ σ] [ToSlice α] (s : String) (pattern : ρ)
def replace [ToSlice α] (s : String) (pattern : ρ) [ToForwardSearcher pattern σ]
(replacement : α) : String :=
s.toSlice.replace pattern replacement
@@ -62,9 +62,24 @@ Examples:
* {lean}`("tea".toSlice.pos ⟨1⟩ (by decide)).find? (fun (c : Char) => c == 't') == none`
-/
@[inline]
def Slice.Pos.find? [ToForwardSearcher ρ σ] {s : Slice} (pos : s.Pos) (pattern : ρ) :
def Slice.Pos.find? {s : Slice} (pos : s.Pos) (pattern : ρ) [ToForwardSearcher pattern σ] :
Option s.Pos :=
((s.replaceStart pos).find? pattern).map ofReplaceStart
((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
@@ -73,13 +88,28 @@ 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? [ToForwardSearcher ρ σ] {s : String} (pos : s.ValidPos)
(pattern : ρ) : Option s.ValidPos :=
(pos.toSlice.find? pattern).map (·.ofSlice)
def Pos.find? {s : String} (pos : s.Pos) (pattern : ρ)
[ToForwardSearcher pattern σ] : Option s.Pos :=
(pos.toSlice.find? pattern).map Pos.ofToSlice
/--
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 :=
ofToSlice (pos.toSlice.find pattern)
/--
Finds the position of the first match of the pattern {name}`pattern` in a string {name}`s`. If
@@ -93,8 +123,120 @@ Examples:
* {lean}`("coffee tea water".find? "tea").map (·.get!) == some 't'`
-/
@[inline]
def find? [ToForwardSearcher ρ σ] (s : String) (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 Pos.ofToSlice
/--
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 [ToForwardSearcher ρ σ] (s : String) (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 Pos.ofToSlice_ne_endPos {s : String} {p : s.toSlice.Pos}
(h : p s.toSlice.endPos) : ofToSlice p s.endPos := by
rwa [ne_eq, Pos.toSlice_inj, Slice.Pos.toSlice_ofToSlice, 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 => Pos.ofToSlice p, Pos.ofToSlice_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

@@ -117,21 +117,21 @@ Examples:
* {lean}`"red green blue".toSlice.startsWith Char.isLower = true`
-/
@[inline]
def startsWith [ForwardPattern ρ] (s : Slice) (pat : ρ) : Bool :=
ForwardPattern.startsWith s pat
def startsWith (s : Slice) (pat : ρ) [ForwardPattern pat] : Bool :=
ForwardPattern.startsWith pat s
inductive SplitIterator (ρ : Type) (s : Slice) [ToForwardSearcher ρ σ] where
inductive SplitIterator {ρ : Type} (pat : ρ) (s : Slice) [ToForwardSearcher pat σ] where
| operating (currPos : s.Pos) (searcher : Std.Iter (α := σ s) (SearchStep s))
| atEnd
deriving Inhabited
namespace SplitIterator
variable [ToForwardSearcher ρ σ]
variable {pat : ρ} [ToForwardSearcher pat σ]
inductive PlausibleStep
instance : Std.Iterators.Iterator (SplitIterator ρ s) Id Slice where
instance : Std.Iterators.Iterator (SplitIterator pat s) Id Slice where
IsPlausibleStep
| .operating _ s, .yield .operating _ s' _ => s'.IsPlausibleSuccessorOf s
| .operating _ s, .yield .atEnd .. _ => True
@@ -145,7 +145,7 @@ instance : Std.Iterators.Iterator (SplitIterator ρ s) Id Slice where
| .operating currPos searcher =>
match h : searcher.step with
| .yield searcher' (.matched startPos endPos), hps =>
let slice := s.replaceStartEnd! currPos startPos
let slice := s.slice! currPos startPos
let nextIt := .operating endPos searcher'
pure (.deflate .yield nextIt slice, by simp [nextIt, hps.isPlausibleSuccessor_of_yield])
| .yield searcher' (.rejected ..), hps =>
@@ -155,16 +155,16 @@ instance : Std.Iterators.Iterator (SplitIterator ρ s) Id Slice where
pure (.deflate .skip .operating currPos searcher',
by simp [hps.isPlausibleSuccessor_of_skip])
| .done, _ =>
let slice := s.replaceStart currPos
let slice := s.sliceFrom currPos
pure (.deflate .yield .atEnd slice, by simp)
| .atEnd => pure (.deflate .done, by simp)
private def toOption : SplitIterator ρ s Option (Std.Iter (α := σ s) (SearchStep s))
private def toOption : SplitIterator pat s Option (Std.Iter (α := σ s) (SearchStep s))
| .operating _ s => some s
| .atEnd => none
private def finitenessRelation [Std.Iterators.Finite (σ s) Id] :
Std.Iterators.FinitenessRelation (SplitIterator ρ s) Id where
Std.Iterators.FinitenessRelation (SplitIterator pat s) Id where
rel := InvImage (Option.lt Std.Iterators.Iter.IsPlausibleSuccessorOf)
(SplitIterator.toOption Std.Iterators.IterM.internalState)
wf := InvImage.wf _ (Option.wellFounded_lt Std.Iterators.Finite.wf_of_id)
@@ -182,19 +182,19 @@ private def finitenessRelation [Std.Iterators.Finite (σ s) Id] :
| .atEnd, _ => simp
@[no_expose]
instance [Std.Iterators.Finite (σ s) Id] : Std.Iterators.Finite (SplitIterator ρ s) Id :=
instance [Std.Iterators.Finite (σ s) Id] : Std.Iterators.Finite (SplitIterator pat s) Id :=
.of_finitenessRelation finitenessRelation
instance [Monad n] : Std.Iterators.IteratorCollect (SplitIterator ρ s) Id n :=
instance [Monad n] : Std.Iterators.IteratorCollect (SplitIterator pat s) Id n :=
.defaultImplementation
instance [Monad n] : Std.Iterators.IteratorCollectPartial (SplitIterator ρ s) Id n :=
instance [Monad n] : Std.Iterators.IteratorCollectPartial (SplitIterator pat s) Id n :=
.defaultImplementation
instance [Monad n] : Std.Iterators.IteratorLoop (SplitIterator ρ s) Id n :=
instance [Monad n] : Std.Iterators.IteratorLoop (SplitIterator pat s) Id n :=
.defaultImplementation
instance [Monad n] : Std.Iterators.IteratorLoopPartial (SplitIterator ρ s) Id n :=
instance [Monad n] : Std.Iterators.IteratorLoopPartial (SplitIterator pat s) Id n :=
.defaultImplementation
end SplitIterator
@@ -215,19 +215,19 @@ Examples:
* {lean}`("baaab".toSlice.split "aa").toList == ["b".toSlice, "ab".toSlice]`
-/
@[specialize pat]
def split [ToForwardSearcher ρ σ] (s : Slice) (pat : ρ) : Std.Iter (α := SplitIterator ρ s) Slice :=
{ internalState := .operating s.startPos (ToForwardSearcher.toSearcher s pat) }
def split (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : Std.Iter (α := SplitIterator pat s) Slice :=
{ internalState := .operating s.startPos (ToForwardSearcher.toSearcher pat s) }
inductive SplitInclusiveIterator (ρ : Type) (s : Slice) [ToForwardSearcher ρ σ] where
inductive SplitInclusiveIterator {ρ : Type} (pat : ρ) (s : Slice) [ToForwardSearcher pat σ] where
| operating (currPos : s.Pos) (searcher : Std.Iter (α := σ s) (SearchStep s))
| atEnd
deriving Inhabited
namespace SplitInclusiveIterator
variable [ToForwardSearcher ρ σ]
variable {pat : ρ} [ToForwardSearcher pat σ]
instance : Std.Iterators.Iterator (SplitInclusiveIterator ρ s) Id Slice where
instance : Std.Iterators.Iterator (SplitInclusiveIterator pat s) Id Slice where
IsPlausibleStep
| .operating _ s, .yield .operating _ s' _ => s'.IsPlausibleSuccessorOf s
| .operating _ s, .yield .atEnd .. _ => True
@@ -241,7 +241,7 @@ instance : Std.Iterators.Iterator (SplitInclusiveIterator ρ s) Id Slice where
| .operating currPos searcher =>
match h : searcher.step with
| .yield searcher' (.matched _ endPos), hps =>
let slice := s.replaceStartEnd! currPos endPos
let slice := s.slice! currPos endPos
let nextIt := .operating endPos searcher'
pure (.deflate .yield nextIt slice,
by simp [nextIt, hps.isPlausibleSuccessor_of_yield])
@@ -253,18 +253,18 @@ instance : Std.Iterators.Iterator (SplitInclusiveIterator ρ s) Id Slice where
by simp [hps.isPlausibleSuccessor_of_skip])
| .done, _ =>
if currPos != s.endPos then
let slice := s.replaceStart currPos
let slice := s.sliceFrom currPos
pure (.deflate .yield .atEnd slice, by simp)
else
pure (.deflate .done, by simp)
| .atEnd => pure (.deflate .done, by simp)
private def toOption : SplitInclusiveIterator ρ s Option (Std.Iter (α := σ s) (SearchStep s))
private def toOption : SplitInclusiveIterator pat s Option (Std.Iter (α := σ s) (SearchStep s))
| .operating _ s => some s
| .atEnd => none
private def finitenessRelation [Std.Iterators.Finite (σ s) Id] :
Std.Iterators.FinitenessRelation (SplitInclusiveIterator ρ s) Id where
Std.Iterators.FinitenessRelation (SplitInclusiveIterator pat s) Id where
rel := InvImage (Option.lt Std.Iterators.Iter.IsPlausibleSuccessorOf)
(SplitInclusiveIterator.toOption Std.Iterators.IterM.internalState)
wf := InvImage.wf _ (Option.wellFounded_lt Std.Iterators.Finite.wf_of_id)
@@ -283,23 +283,23 @@ private def finitenessRelation [Std.Iterators.Finite (σ s) Id] :
@[no_expose]
instance [Std.Iterators.Finite (σ s) Id] :
Std.Iterators.Finite (SplitInclusiveIterator ρ s) Id :=
Std.Iterators.Finite (SplitInclusiveIterator pat s) Id :=
.of_finitenessRelation finitenessRelation
instance [Monad n] {s} :
Std.Iterators.IteratorCollect (SplitInclusiveIterator ρ s) Id n :=
Std.Iterators.IteratorCollect (SplitInclusiveIterator pat s) Id n :=
.defaultImplementation
instance [Monad n] {s} :
Std.Iterators.IteratorCollectPartial (SplitInclusiveIterator ρ s) Id n :=
Std.Iterators.IteratorCollectPartial (SplitInclusiveIterator pat s) Id n :=
.defaultImplementation
instance [Monad n] {s} :
Std.Iterators.IteratorLoop (SplitInclusiveIterator ρ s) Id n :=
Std.Iterators.IteratorLoop (SplitInclusiveIterator pat s) Id n :=
.defaultImplementation
instance [Monad n] {s} :
Std.Iterators.IteratorLoopPartial (SplitInclusiveIterator ρ s) Id n :=
Std.Iterators.IteratorLoopPartial (SplitInclusiveIterator pat s) Id n :=
.defaultImplementation
end SplitInclusiveIterator
@@ -317,9 +317,9 @@ Examples:
* {lean}`("baaab".toSlice.splitInclusive "aa").toList == ["baa".toSlice, "ab".toSlice]`
-/
@[specialize pat]
def splitInclusive [ToForwardSearcher ρ σ] (s : Slice) (pat : ρ) :
Std.Iter (α := SplitInclusiveIterator ρ s) Slice :=
{ internalState := .operating s.startPos (ToForwardSearcher.toSearcher s pat) }
def splitInclusive (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] :
Std.Iter (α := SplitInclusiveIterator pat s) Slice :=
{ internalState := .operating s.startPos (ToForwardSearcher.toSearcher pat s) }
/--
If {name}`pat` matches a prefix of {name}`s`, returns the remainder. Returns {name}`none` otherwise.
@@ -336,8 +336,8 @@ Examples:
* {lean}`"red green blue".toSlice.dropPrefix? Char.isLower == some "ed green blue".toSlice`
-/
@[inline]
def dropPrefix? [ForwardPattern ρ] (s : Slice) (pat : ρ) : Option Slice :=
(ForwardPattern.dropPrefix? s pat).map s.replaceStart
def dropPrefix? (s : Slice) (pat : ρ) [ForwardPattern pat] : Option Slice :=
(ForwardPattern.dropPrefix? pat s).map s.sliceFrom
/--
If {name}`pat` matches a prefix of {name}`s`, returns the remainder. Returns {name}`s` unmodified
@@ -354,7 +354,7 @@ Examples:
* {lean}`"red green blue".toSlice.dropPrefix Char.isLower == "ed green blue".toSlice`
-/
@[specialize pat]
def dropPrefix [ForwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
def dropPrefix (s : Slice) (pat : ρ) [ForwardPattern pat] : Slice :=
dropPrefix? s pat |>.getD s
/--
@@ -373,11 +373,11 @@ Examples:
* {lean}`"aaaaa".toSlice.replace "aa" "b" = "bba"`
* {lean}`"abc".toSlice.replace "" "k" = "kakbkck"`
-/
def replace [ToForwardSearcher ρ σ] [ToSlice α] (s : Slice) (pattern : ρ) (replacement : α) :
def replace [ToSlice α] (s : Slice) (pattern : ρ) [ToForwardSearcher pattern σ] (replacement : α) :
String :=
(ToForwardSearcher.toSearcher s pattern).fold (init := "") (fun
(ToForwardSearcher.toSearcher pattern s).fold (init := "") (fun
| sofar, .matched .. => sofar ++ ToSlice.toSlice replacement
| sofar, .rejected start stop => sofar ++ s.replaceStartEnd! start stop)
| sofar, .rejected start stop => sofar ++ s.slice! start stop)
/--
Removes the specified number of characters (Unicode code points) from the start of the slice.
@@ -391,7 +391,7 @@ Examples:
-/
@[inline]
def drop (s : Slice) (n : Nat) : Slice :=
s.replaceStart (s.startPos.nextn n)
s.sliceFrom (s.startPos.nextn n)
/--
Creates a new slice that contains the longest prefix of {name}`s` for which {name}`pat` matched
@@ -404,18 +404,18 @@ Examples:
* {lean}`"red green blue".toSlice.dropWhile (fun (_ : Char) => true) == "".toSlice`
-/
@[inline]
def dropWhile [ForwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
def dropWhile (s : Slice) (pat : ρ) [ForwardPattern pat] : Slice :=
go s.startPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := ForwardPattern.dropPrefix? (s.replaceStart curr) pat then
if curr < Pos.ofReplaceStart nextCurr then
go (Pos.ofReplaceStart nextCurr)
if let some nextCurr := ForwardPattern.dropPrefix? pat (s.sliceFrom curr) then
if curr < Pos.ofSliceFrom nextCurr then
go (Pos.ofSliceFrom nextCurr)
else
s.replaceStart curr
s.sliceFrom curr
else
s.replaceStart curr
s.sliceFrom curr
termination_by curr
/--
@@ -449,7 +449,7 @@ Examples:
-/
@[inline]
def take (s : Slice) (n : Nat) : Slice :=
s.replaceEnd (s.startPos.nextn n)
s.sliceTo (s.startPos.nextn n)
/--
Creates a new slice that contains the longest prefix of {name}`s` for which {name}`pat` matched
@@ -464,18 +464,18 @@ Examples:
* {lean}`"red green blue".toSlice.takeWhile (fun (_ : Char) => true) == "red green blue".toSlice`
-/
@[inline]
def takeWhile [ForwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
def takeWhile (s : Slice) (pat : ρ) [ForwardPattern pat] : Slice :=
go s.startPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := ForwardPattern.dropPrefix? (s.replaceStart curr) pat then
if curr < Pos.ofReplaceStart nextCurr then
go (Pos.ofReplaceStart nextCurr)
if let some nextCurr := ForwardPattern.dropPrefix? pat (s.sliceFrom curr) then
if curr < Pos.ofSliceFrom nextCurr then
go (Pos.ofSliceFrom nextCurr)
else
s.replaceEnd curr
s.sliceTo curr
else
s.replaceEnd curr
s.sliceTo curr
termination_by curr
/--
@@ -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]
def find? [ToForwardSearcher ρ σ] (s : Slice) (pat : ρ) : Option s.Pos :=
let searcher := ToForwardSearcher.toSearcher s 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.
@@ -505,12 +520,16 @@ Examples:
* {lean}`"coffee tea water".toSlice.contains "tea" = true`
-/
@[specialize pat]
def contains [ToForwardSearcher ρ σ] (s : Slice) (pat : ρ) : Bool :=
let searcher := ToForwardSearcher.toSearcher s pat
def contains (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
let searcher := ToForwardSearcher.toSearcher pat s
searcher.any (· matches .matched ..)
@[inline, inherit_doc contains]
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.
@@ -524,7 +543,7 @@ Examples:
* {lean}`"aaaaaaa".toSlice.all "aa" = false`
-/
@[inline]
def all [ForwardPattern ρ] (s : Slice) (pat : ρ) : Bool :=
def all (s : Slice) (pat : ρ) [ForwardPattern pat] : Bool :=
s.dropWhile pat |>.isEmpty
end ForwardPatternUsers
@@ -549,10 +568,10 @@ Examples:
* {lean}`"red green blue".toSlice.endsWith Char.isLower = true`
-/
@[inline]
def endsWith [BackwardPattern ρ] (s : Slice) (pat : ρ) : Bool :=
BackwardPattern.endsWith s pat
def endsWith (s : Slice) (pat : ρ) [BackwardPattern pat] : Bool :=
BackwardPattern.endsWith pat s
inductive RevSplitIterator (ρ : Type) (s : Slice) [ToBackwardSearcher ρ σ] where
inductive RevSplitIterator {ρ : Type} (pat : ρ) (s : Slice) [ToBackwardSearcher pat σ] where
| operating (currPos : s.Pos) (searcher : Std.Iter (α := σ s) (SearchStep s))
| atEnd
deriving Inhabited
@@ -575,7 +594,7 @@ instance [Pure m] : Std.Iterators.Iterator (RevSplitIterator ρ s) m Slice where
| .operating currPos searcher =>
match h : searcher.step with
| .yield searcher' (.matched startPos endPos), hps =>
let slice := s.replaceStartEnd! endPos currPos
let slice := s.slice! endPos currPos
let nextIt := .operating startPos searcher'
pure (.deflate .yield nextIt slice, by simp [nextIt, hps.isPlausibleSuccessor_of_yield])
| .yield searcher' (.rejected ..), hps =>
@@ -586,7 +605,7 @@ instance [Pure m] : Std.Iterators.Iterator (RevSplitIterator ρ s) m Slice where
by simp [hps.isPlausibleSuccessor_of_skip])
| .done, _ =>
if currPos s.startPos then
let slice := s.replaceEnd currPos
let slice := s.sliceTo currPos
pure (.deflate .yield .atEnd slice, by simp)
else
pure (.deflate .done, by simp)
@@ -648,9 +667,9 @@ Examples:
* {lean}`("coffee tea water".toSlice.revSplit ' ').toList == ["water".toSlice, "tea".toSlice, "coffee".toSlice]`
-/
@[specialize pat]
def revSplit [ToBackwardSearcher ρ σ] (s : Slice) (pat : ρ) :
Std.Iter (α := RevSplitIterator ρ s) Slice :=
{ internalState := .operating s.endPos (ToBackwardSearcher.toSearcher s pat) }
def revSplit (s : Slice) (pat : ρ) [ToBackwardSearcher pat σ] :
Std.Iter (α := RevSplitIterator pat s) Slice :=
{ internalState := .operating s.endPos (ToBackwardSearcher.toSearcher pat s) }
/--
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`none` otherwise.
@@ -667,8 +686,8 @@ Examples:
* {lean}`"red green blue".toSlice.dropSuffix? Char.isLower == some "red green blu".toSlice`
-/
@[inline]
def dropSuffix? [BackwardPattern ρ] (s : Slice) (pat : ρ) : Option Slice :=
(BackwardPattern.dropSuffix? s pat).map s.replaceEnd
def dropSuffix? (s : Slice) (pat : ρ) [BackwardPattern pat] : Option Slice :=
(BackwardPattern.dropSuffix? pat s).map s.sliceTo
/--
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`s` unmodified
@@ -686,7 +705,7 @@ Examples:
* {lean}`"red green blue".toSlice.dropSuffix Char.isLower == "red green blu".toSlice`
-/
@[specialize pat]
def dropSuffix [BackwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
def dropSuffix (s : Slice) (pat : ρ) [BackwardPattern pat] : Slice :=
dropSuffix? s pat |>.getD s
/--
@@ -701,7 +720,7 @@ Examples:
-/
@[inline]
def dropEnd (s : Slice) (n : Nat) : Slice :=
s.replaceEnd (s.endPos.prevn n)
s.sliceTo (s.endPos.prevn n)
/--
Creates a new slice that contains the longest suffix of {name}`s` for which {name}`pat` matched
@@ -713,18 +732,18 @@ Examples:
* {lean}`"red green blue".toSlice.dropEndWhile (fun (_ : Char) => true) == "".toSlice`
-/
@[inline]
def dropEndWhile [BackwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
def dropEndWhile (s : Slice) (pat : ρ) [BackwardPattern pat] : Slice :=
go s.endPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := BackwardPattern.dropSuffix? (s.replaceEnd curr) pat then
if Pos.ofReplaceEnd nextCurr < curr then
go (Pos.ofReplaceEnd nextCurr)
if let some nextCurr := BackwardPattern.dropSuffix? pat (s.sliceTo curr) then
if Pos.ofSliceTo nextCurr < curr then
go (Pos.ofSliceTo nextCurr)
else
s.replaceEnd curr
s.sliceTo curr
else
s.replaceEnd curr
s.sliceTo curr
termination_by curr.down
/--
@@ -758,7 +777,7 @@ Examples:
-/
@[inline]
def takeEnd (s : Slice) (n : Nat) : Slice :=
s.replaceStart (s.endPos.prevn n)
s.sliceFrom (s.endPos.prevn n)
/--
Creates a new slice that contains the suffix prefix of {name}`s` for which {name}`pat` matched
@@ -772,22 +791,22 @@ Examples:
* {lean}`"red green blue".toSlice.takeEndWhile (fun (_ : Char) => true) == "red green blue".toSlice`
-/
@[inline]
def takeEndWhile [BackwardPattern ρ] (s : Slice) (pat : ρ) : Slice :=
def takeEndWhile (s : Slice) (pat : ρ) [BackwardPattern pat] : Slice :=
go s.endPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := BackwardPattern.dropSuffix? (s.replaceEnd curr) pat then
if Pos.ofReplaceEnd nextCurr < curr then
go (Pos.ofReplaceEnd nextCurr)
if let some nextCurr := BackwardPattern.dropSuffix? pat (s.sliceTo curr) then
if Pos.ofSliceTo nextCurr < curr then
go (Pos.ofSliceTo nextCurr)
else
s.replaceStart curr
s.sliceFrom curr
else
s.replaceStart curr
s.sliceFrom curr
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.
@@ -799,8 +818,8 @@ Examples:
* {lean}`"tea".toSlice.revFind? (fun (c : Char) => c == 'X') == none`
-/
@[specialize pat]
def revFind? [ToBackwardSearcher ρ σ] (s : Slice) (pat : ρ) : Option s.Pos :=
let searcher := ToBackwardSearcher.toSearcher s pat
def revFind? (s : Slice) (pat : ρ) [ToBackwardSearcher pat σ] : Option s.Pos :=
let searcher := ToBackwardSearcher.toSearcher pat s
searcher.findSome? (fun | .matched startPos _ => some startPos | .rejected .. => none)
end BackwardPatternUsers
@@ -926,6 +945,10 @@ Examples:
def chars (s : Slice) :=
Std.Iterators.Iter.map (fun pos, h => pos.get h) (positions s)
@[deprecated "There is no constant-time length function on slices. Use `s.positions.count` instead, or `isEmpty` if you only need to know whether the slice is empty." (since := "2025-11-20")]
def length (s : Slice) : Nat :=
s.positions.count
structure RevPosIterator (s : Slice) where
currPos : s.Pos
deriving Inhabited
@@ -1295,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?
@@ -1312,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`.
@@ -1323,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?)
@@ -1334,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

@@ -69,6 +69,10 @@ Examples:
def dropRight (s : String) (n : Nat) : String :=
(s.dropEnd n).copy
@[deprecated Slice.dropEnd (since := "2025-11-20")]
def Slice.dropRight (s : Slice) (n : Nat) : Slice :=
s.dropEnd n
@[export lean_string_dropright]
def Internal.dropRightImpl (s : String) (n : Nat) : String :=
(String.dropEnd s n).copy
@@ -115,6 +119,10 @@ Examples:
def takeRight (s : String) (n : Nat) : String :=
(s.takeEnd n).toString
@[deprecated Slice.takeEnd (since := "2025-11-20")]
def Slice.takeRight (s : Slice) (n : Nat) : Slice :=
s.takeEnd n
/--
Creates a string slice that contains the longest prefix of {name}`s` in which {name}`pat` matched
(potentially repeatedly).
@@ -130,7 +138,7 @@ Examples:
* {lean}`"red red green blue".takeWhile "red " == "red red ".toSlice`
* {lean}`"red green blue".takeWhile (fun (_ : Char) => true) == "red green blue".toSlice`
-/
@[inline] def takeWhile [ForwardPattern ρ] (s : String) (pat : ρ) : String.Slice :=
@[inline] def takeWhile (s : String) (pat : ρ) [ForwardPattern pat] : String.Slice :=
s.toSlice.takeWhile pat
/--
@@ -148,7 +156,7 @@ Examples:
* {lean}`"red red green blue".dropWhile "red " == "green blue".toSlice`
* {lean}`"red green blue".dropWhile (fun (_ : Char) => true) == "".toSlice`
-/
@[inline] def dropWhile [ForwardPattern ρ] (s : String) (pat : ρ) : String.Slice :=
@[inline] def dropWhile (s : String) (pat : ρ) [ForwardPattern pat] : String.Slice :=
s.toSlice.dropWhile pat
/--
@@ -165,13 +173,17 @@ Examples:
* {lean}`"red green blue".takeEndWhile 'e' == "e".toSlice`
* {lean}`"red green blue".takeEndWhile (fun (_ : Char) => true) == "red green blue".toSlice`
-/
@[inline] def takeEndWhile [BackwardPattern ρ] (s : String) (pat : ρ) : String.Slice :=
@[inline] def takeEndWhile (s : String) (pat : ρ) [BackwardPattern pat] : String.Slice :=
s.toSlice.takeEndWhile pat
@[deprecated String.takeEndWhile (since := "2025-11-17")]
def takeRightWhile (s : String) (p : Char Bool) : String :=
(s.takeEndWhile p).toString
@[deprecated Slice.takeEndWhile (since := "2025-11-20")]
def Slice.takeRightWhile (s : Slice) (p : Char Bool) : Slice :=
s.takeEndWhile p
/--
Creates a new string by removing the longest suffix from {name}`s` in which {name}`pat` matches
(potentially repeatedly).
@@ -186,13 +198,17 @@ Examples:
* {lean}`"red green blue".dropEndWhile 'e' == "red green blu".toSlice`
* {lean}`"red green blue".dropEndWhile (fun (_ : Char) => true) == "".toSlice`
-/
@[inline] def dropEndWhile [BackwardPattern ρ] (s : String) (pat : ρ) : String.Slice :=
@[inline] def dropEndWhile (s : String) (pat : ρ) [BackwardPattern pat] : String.Slice :=
s.toSlice.dropEndWhile pat
@[deprecated String.dropEndWhile (since := "2025-11-17")]
def dropRightWhile (s : String) (p : Char Bool) : String :=
(s.dropEndWhile p).toString
@[deprecated Slice.dropEndWhile (since := "2025-11-20")]
def Slice.dropRightWhile (s : Slice) (p : Char Bool) : Slice :=
s.dropEndWhile p
/--
Checks whether the first string ({name}`s`) begins with the pattern ({name}`pat`).
@@ -206,7 +222,7 @@ Examples:
* {lean}`"red green blue".startsWith 'r' = true`
* {lean}`"red green blue".startsWith Char.isLower = true`
-/
@[inline] def startsWith [ForwardPattern ρ] (s : String) (pat : ρ) : Bool :=
@[inline] def startsWith (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
s.toSlice.startsWith pat
/--
@@ -240,7 +256,7 @@ Examples:
* {lean}`"red green blue".endsWith 'e' = true`
* {lean}`"red green blue".endsWith Char.isLower = true`
-/
@[inline] def endsWith [BackwardPattern ρ] (s : String) (pat : ρ) : Bool :=
@[inline] def endsWith (s : String) (pat : ρ) [BackwardPattern pat] : Bool :=
s.toSlice.endsWith pat
/--
@@ -263,6 +279,10 @@ Examples:
def trimRight (s : String) : String :=
s.trimAsciiEnd.copy
@[deprecated Slice.trimAsciiEnd (since := "2025-11-20")]
def Slice.trimRight (s : Slice) : Slice :=
s.trimAsciiEnd
/--
Removes leading whitespace from a string by returning a slice whose start position is the first
non-whitespace character, or the end position if there is no non-whitespace character.
@@ -283,6 +303,10 @@ Examples:
def trimLeft (s : String) : String :=
s.trimAsciiStart.copy
@[deprecated Slice.trimAsciiStart (since := "2025-11-20")]
def Slice.trimLeft (s : Slice) : Slice :=
s.trimAsciiStart
/--
Removes leading and trailing whitespace from a string.
@@ -302,6 +326,10 @@ Examples:
def trim (s : String) : String :=
s.trimAscii.copy
@[deprecated Slice.trimAscii (since := "2025-11-20")]
def Slice.trim (s : Slice) : Slice :=
s.trimAscii
@[export lean_string_trim]
def Internal.trimImpl (s : String) : String :=
(String.trimAscii s).copy
@@ -361,7 +389,7 @@ Examples:
* {lean}`"red green blue".dropPrefix? 'r' == some "ed green blue".toSlice`
* {lean}`"red green blue".dropPrefix? Char.isLower == some "ed green blue".toSlice`
-/
def dropPrefix? [ForwardPattern ρ] (s : String) (pat : ρ) : Option String.Slice :=
def dropPrefix? (s : String) (pat : ρ) [ForwardPattern pat] : Option String.Slice :=
s.toSlice.dropPrefix? pat
/--
@@ -381,7 +409,7 @@ Examples:
* {lean}`"red green blue".dropSuffix? 'e' == some "red green blu".toSlice`
* {lean}`"red green blue".dropSuffix? Char.isLower == some "red green blu".toSlice`
-/
def dropSuffix? [BackwardPattern ρ] (s : String) (pat : ρ) : Option String.Slice :=
def dropSuffix? (s : String) (pat : ρ) [BackwardPattern pat] : Option String.Slice :=
s.toSlice.dropSuffix? pat
/--
@@ -401,13 +429,17 @@ Examples:
* {lean}`"red green blue".dropPrefix 'r' == "ed green blue".toSlice`
* {lean}`"red green blue".dropPrefix Char.isLower == "ed green blue".toSlice`
-/
def dropPrefix [ForwardPattern ρ] (s : String) (pat : ρ) : String.Slice :=
def dropPrefix (s : String) (pat : ρ) [ForwardPattern pat] : String.Slice :=
s.toSlice.dropPrefix pat
@[deprecated String.dropPrefix (since := "2025-11-17")]
def stripPrefix (s pre : String) : String :=
(s.dropPrefix pre).toString
@[deprecated Slice.dropPrefix (since := "2025-11-20")]
def Slice.stripPrefix (s pre : Slice) : Slice :=
s.dropPrefix pre
/--
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`s` unmodified
otherwise.
@@ -425,11 +457,15 @@ Examples:
* {lean}`"red green blue".dropSuffix 'e' == "red green blu".toSlice`
* {lean}`"red green blue".dropSuffix Char.isLower == "red green blu".toSlice`
-/
def dropSuffix [BackwardPattern ρ] (s : String) (pat : ρ) : String.Slice :=
def dropSuffix (s : String) (pat : ρ) [BackwardPattern pat] : String.Slice :=
s.toSlice.dropSuffix pat
@[deprecated String.dropSuffix (since := "2025-11-17")]
def stripSuffix (s : String) (suff : String) : String :=
(s.dropSuffix suff).toString
@[deprecated Slice.dropSuffix (since := "2025-11-20")]
def Slice.stripSuffix (s : Slice) (suff : Slice) : Slice :=
s.dropSuffix suff
end String

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

@@ -2535,11 +2535,6 @@ theorem back?_eq_some_iff {xs : Vector α n} {a : α} :
rcases xs with xs, rfl
rcases ys with ys, rfl
simp [Array.back_append]
split <;> rename_i h
· rw [dif_pos]
simp_all
· rw [dif_neg]
rwa [Array.isEmpty_iff_size_eq_zero] at h
theorem back_append_right {xs : Vector α n} {ys : Vector α m} [NeZero m] :
(xs ++ ys).back = ys.back := by

View File

@@ -116,7 +116,7 @@ macro:max x:term noWs "[" i:term "]" noWs "?" : term => `(getElem? $x $i)
/--
The syntax `arr[i]!` gets the `i`'th element of the collection `arr` and
panics `i` is out of bounds.
panics if `i` is out of bounds.
-/
macro:max x:term noWs "[" i:term "]" noWs "!" : term => `(getElem! $x $i)

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

@@ -236,20 +236,21 @@ instance [Ring R] [LE R] [LT R] [LawfulOrderLT R] [IsPreorder R] [OrderedRing R]
end Preorder
theorem mul_pos [LE R] [LT R] [IsPreorder R] [OrderedRing R] {a b : R} (h₁ : 0 < a) (h₂ : 0 < b) : 0 < a * b := by
simpa [Semiring.zero_mul] using mul_lt_mul_of_pos_right h₁ h₂
theorem zero_le_one [LE R] [LT R] [LawfulOrderLT R] [IsPreorder R] [OrderedRing R] : (0 : R) 1 :=
Preorder.le_of_lt zero_lt_one
theorem not_one_lt_zero [LE R] [LT R] [LawfulOrderLT R] [IsPreorder R] [OrderedRing R] : ¬ ((1 : R) < 0) :=
fun h => Preorder.lt_irrefl (0 : R) (Preorder.lt_trans zero_lt_one h)
section PartialOrder
variable [LE R] [LT R] [IsPartialOrder R] [OrderedRing R]
theorem mul_pos {a b : R} (h₁ : 0 < a) (h₂ : 0 < b) : 0 < a * b := by
simpa [Semiring.zero_mul] using mul_lt_mul_of_pos_right h₁ h₂
variable [LawfulOrderLT R]
theorem zero_le_one : (0 : R) 1 := Preorder.le_of_lt zero_lt_one
theorem not_one_lt_zero : ¬ ((1 : R) < 0) :=
fun h => Preorder.lt_irrefl (0 : R) (Preorder.lt_trans zero_lt_one h)
theorem mul_le_mul_of_nonneg_left {a b c : R} (h : a b) (h' : 0 c) : c * a c * b := by
rw [PartialOrder.le_iff_lt_or_eq] at h'
cases h' with

View File

@@ -82,6 +82,10 @@ class Semiring (α : Type u) extends Add α, Mul α where
ofNat_succ : a : Nat, OfNat.ofNat (α := α) (a + 1) = OfNat.ofNat a + 1 := by intros; rfl
/-- Numerals are consistently defined with respect to the canonical map from natural numbers. -/
ofNat_eq_natCast : n : Nat, OfNat.ofNat (α := α) n = Nat.cast n := by intros; rfl
/--
Multiplying by a numeral is consistently defined with respect to the canonical map from natural
numbers.
-/
nsmul_eq_natCast_mul : n : Nat, a : α, n a = Nat.cast n * a := by intros; rfl
/--
@@ -204,6 +208,11 @@ theorem pow_add (a : α) (k₁ k₂ : Nat) : a ^ (k₁ + k₂) = a^k₁ * a^k₂
theorem pow_add_congr (a r : α) (k k₁ k₂ : Nat) : k = k₁ + k₂ a^k₁ * a^k₂ = r a ^ k = r := by
intros; subst k r; rw [pow_add]
theorem one_pow (n : Nat) : (1 : α) ^ n = 1 := by
induction n
next => simp [pow_zero]
next => simp [pow_succ, *, mul_one]
theorem natCast_pow (x : Nat) (k : Nat) : ((x ^ k : Nat) : α) = (x : α) ^ k := by
induction k
next => simp [pow_zero, Nat.pow_zero, natCast_one]
@@ -376,6 +385,11 @@ variable {α : Type u} [CommSemiring α]
theorem mul_left_comm (a b c : α) : a * (b * c) = b * (a * c) := by
rw [ mul_assoc, mul_assoc, mul_comm a]
theorem mul_pow (a b : α) (n : Nat) : (a*b)^n = a^n * b^n := by
induction n
next => simp [pow_zero, mul_one]
next n ih => simp [pow_succ, ih, mul_comm, mul_assoc, mul_left_comm]
end CommSemiring
open Semiring hiding add_comm add_assoc add_zero

View File

@@ -180,6 +180,53 @@ where
else
.mult { x := pw₁.x, k := pw₁.k + pw₂.k } (go fuel m₁ m₂)
noncomputable def Mon.mul_k : Mon Mon Mon :=
Nat.rec
(fun m₁ m₂ => concat m₁ m₂)
(fun _ ih m₁ m₂ =>
Mon.rec (t := m₂)
m₁
(fun pw₂ m₂' _ => Mon.rec (t := m₁)
m₂
(fun pw₁ m₁' _ =>
Bool.rec (t := pw₁.varLt pw₂)
(Bool.rec (t := pw₂.varLt pw₁)
(.mult { x := pw₁.x, k := Nat.add pw₁.k pw₂.k } (ih m₁' m₂'))
(.mult pw₂ (ih (.mult pw₁ m₁') m₂')))
(.mult pw₁ (ih m₁' (.mult pw₂ m₂'))))))
hugeFuel
theorem Mon.mul_k_eq_mul : Mon.mul_k m₁ m₂ = Mon.mul m₁ m₂ := by
unfold mul_k mul
generalize hugeFuel = fuel
fun_induction mul.go
· rfl
· rfl
case case3 m₂ _ =>
cases m₂
· contradiction
· dsimp
case case4 fuel pw₁ m₁ pw₂ m₂ h ih =>
dsimp only
rw [h]
dsimp only
rw [ih]
case case5 fuel pw₁ m₁ pw₂ m₂ h₁ h₂ ih =>
dsimp only
rw [h₁]
dsimp only
rw [h₂]
dsimp only
rw [ih]
case case6 fuel pw₁ m₁ pw₂ m₂ h₁ h₂ ih =>
dsimp only
rw [h₁]
dsimp only
rw [h₂]
dsimp only
rw [ih]
rfl
def Mon.mul_nc (m₁ m₂ : Mon) : Mon :=
match m₁ with
| .unit => m₂
@@ -190,6 +237,28 @@ def Mon.degree : Mon → Nat
| .unit => 0
| .mult pw m => pw.k + degree m
noncomputable def Mon.degree_k : Mon Nat :=
Nat.rec
(fun m => m.degree)
(fun _ ih m =>
Mon.rec (t := m)
0
(fun pw m' _ => Nat.add pw.k (ih m')))
hugeFuel
theorem Mon.degree_k_eq_degree : Mon.degree_k m = Mon.degree m := by
unfold degree_k
generalize hugeFuel = fuel
induction fuel generalizing m with
| zero => rfl
| succ fuel ih =>
conv => rhs; unfold degree
split
· rfl
· dsimp only
rw [ ih]
rfl
def Var.revlex (x y : Var) : Ordering :=
bif x.blt y then .gt
else bif y.blt x then .lt
@@ -270,7 +339,7 @@ noncomputable def Mon.grevlex_k (m₁ m₂ : Mon) : Ordering :=
Bool.rec
(Bool.rec .gt .lt (Nat.blt m₁.degree m₂.degree))
(revlex_k m₁ m₂)
(Nat.beq m₁.degree m₂.degree)
(Nat.beq m₁.degree_k m₂.degree_k)
theorem Mon.revlex_k_eq_revlex (m₁ m₂ : Mon) : m₁.revlex_k m₂ = m₁.revlex m₂ := by
unfold revlex_k revlex
@@ -302,18 +371,18 @@ theorem Mon.grevlex_k_eq_grevlex (m₁ m₂ : Mon) : m₁.grevlex_k m₂ = m₁.
next h =>
have h₁ : Nat.blt m₁.degree m₂.degree = true := by simp [h]
have h₂ : Nat.beq m₁.degree m₂.degree = false := by rw [ Bool.not_eq_true, Nat.beq_eq]; omega
simp [h₁, h₂]
simp [degree_k_eq_degree, h₁, h₂]
next h =>
split
next h' =>
have h₂ : Nat.beq m₁.degree m₂.degree = true := by rw [Nat.beq_eq, h']
simp [h₂]
simp [degree_k_eq_degree, h₂]
next h' =>
have h₁ : Nat.blt m₁.degree m₂.degree = false := by
rw [ Bool.not_eq_true, Nat.blt_eq]; assumption
have h₂ : Nat.beq m₁.degree m₂.degree = false := by
rw [ Bool.not_eq_true, Nat.beq_eq]; assumption
simp [h₁, h₂]
simp [degree_k_eq_degree, h₁, h₂]
inductive Poly where
| num (k : Int)
@@ -481,7 +550,7 @@ noncomputable def Poly.mulMon_k (k : Int) (m : Mon) (p : Poly) : Poly :=
(Bool.rec
(Poly.rec
(fun k' => Bool.rec (.add (Int.mul k k') m (.num 0)) (.num 0) (Int.beq' k' 0))
(fun k' m' _ ih => .add (Int.mul k k') (m.mul m') ih)
(fun k' m' _ ih => .add (Int.mul k k') (m.mul_k m') ih)
p)
(p.mulConst_k k)
(Mon.beq' m .unit))
@@ -511,7 +580,7 @@ noncomputable def Poly.mulMon_k (k : Int) (m : Mon) (p : Poly) : Poly :=
next =>
have h : Int.beq' k 0 = false := by simp [*]
simp [h]
next ih => simp [ ih]
next ih => simp [ ih, Mon.mul_k_eq_mul]
def Poly.mulMon_nc (k : Int) (m : Mon) (p : Poly) : Poly :=
bif k == 0 then
@@ -656,6 +725,29 @@ noncomputable def Expr.toPoly_k (e : Expr) : Poly :=
(k.beq 0))
e
def Mon.degreeOf (m : Mon) (x : Var) : Nat :=
match m with
| .unit => 0
| .mult pw m => bif pw.x == x then pw.k else degreeOf m x
def Mon.cancelVar (m : Mon) (x : Var) : Mon :=
match m with
| .unit => .unit
| .mult pw m => bif pw.x == x then m else .mult pw (cancelVar m x)
def Poly.cancelVar' (c : Int) (x : Var) (p : Poly) (acc : Poly) : Poly :=
match p with
| .num k => acc.addConst k
| .add k m p =>
let n := m.degreeOf x
bif n > 0 && c^n k then
cancelVar' c x p (acc.insert (k / (c^n)) (m.cancelVar x))
else
cancelVar' c x p (acc.insert k m)
def Poly.cancelVar (c : Int) (x : Var) (p : Poly) : Poly :=
cancelVar' c x p (.num 0)
@[simp] theorem Expr.toPoly_k_eq_toPoly (e : Expr) : e.toPoly_k = e.toPoly := by
induction e <;> simp only [toPoly, toPoly_k]
next a ih => rw [Poly.mulConst_k_eq_mulConst]; congr
@@ -1175,6 +1267,72 @@ theorem Expr.eq_of_toPoly_nc_eq {α} [Ring α] (ctx : Context α) (a b : Expr) (
simp [denote_toPoly_nc] at h
assumption
section
attribute [local simp] Semiring.pow_zero Semiring.mul_one Semiring.one_mul cond_eq_ite
theorem Mon.denote_cancelVar [CommSemiring α] (ctx : Context α) (m : Mon) (x : Var)
: m.denote ctx = x.denote ctx ^ (m.degreeOf x) * (m.cancelVar x).denote ctx := by
fun_induction cancelVar <;> simp [degreeOf]
next h =>
simp at h; simp [*, denote, Power.denote_eq]
next h ih =>
simp at h; simp [*, denote, Semiring.mul_assoc, CommSemiring.mul_comm, CommSemiring.mul_left_comm]
theorem Poly.denote_cancelVar' {α} [CommRing α] (ctx : Context α) (p : Poly) (c : Int) (x : Var) (acc : Poly)
: c 0 c * x.denote ctx = 1 (p.cancelVar' c x acc).denote ctx = p.denote ctx + acc.denote ctx := by
intro h₁ h₂
fun_induction cancelVar'
next acc k => simp [denote_addConst, denote, Semiring.add_comm]
next h ih =>
simp [ih, denote_insert, denote]
conv => rhs; rw [Mon.denote_cancelVar (x := x)]
simp [ Semiring.add_assoc]
congr 1
rw [Semiring.add_comm, Ring.zsmul_eq_intCast_mul,]
congr 1
simp +zetaDelta [Int.dvd_def] at h
have d, h := h.2
simp +zetaDelta [h]
rw [Int.mul_ediv_cancel_left _ (Int.pow_ne_zero h₁)]
rw [Ring.intCast_mul]
conv => rhs; lhs; rw [CommSemiring.mul_comm]
rw [Semiring.mul_assoc, Ring.intCast_pow]
congr 1
rw [ Semiring.mul_assoc, CommSemiring.mul_pow, h₂, Semiring.one_pow, Semiring.one_mul]
next ih =>
simp [ih, denote_insert, denote, Ring.zsmul_eq_intCast_mul, Semiring.add_assoc,
Semiring.add_comm, add_left_comm]
theorem Poly.denote_cancelVar {α} [CommRing α] (ctx : Context α) (p : Poly) (c : Int) (x : Var)
: c 0 c * x.denote ctx = 1 (p.cancelVar c x).denote ctx = p.denote ctx := by
intro h₁ h₂
have := denote_cancelVar' ctx p c x (.num 0) h₁ h₂
rw [cancelVar, this, denote, Ring.intCast_zero, Semiring.add_zero]
noncomputable def cancel_var_cert (c : Int) (x : Var) (p₁ p₂ : Poly) : Bool :=
c != 0 && p₂.beq' (p₁.cancelVar c x)
theorem eq_cancel_var {α} [CommRing α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx = 0 p₂.denote ctx = 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
theorem diseq_cancel_var {α} [CommRing α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx 0 p₂.denote ctx 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
theorem le_cancel_var {α} [CommRing α] [LE α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx 0 p₂.denote ctx 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
theorem lt_cancel_var {α} [CommRing α] [LT α] (ctx : Context α) (c : Int) (x : Var) (p₁ p₂ : Poly)
: cancel_var_cert c x p₁ p₂ c * x.denote ctx = 1 p₁.denote ctx < 0 p₂.denote ctx < 0 := by
simp [cancel_var_cert]; intros h₁ _ h₂ _; subst p₂
simp [Poly.denote_cancelVar ctx p₁ c x h₁ h₂]; assumption
end
/-!
Theorems for justifying the procedure for commutative rings with a characteristic in `grind`.
-/
@@ -1393,15 +1551,34 @@ 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 eq_mul {α} [CommRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_cert p₁ k p p₁.denote ctx = 0 p.denote ctx = 0 := by
apply mul
noncomputable def mul_ne_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
k != 0 && (p₁.mulConst_k k |>.beq' p)
theorem diseq_mul {α} [CommRing α] [NoNatZeroDivisors α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_ne_cert p₁ k p p₁.denote ctx 0 p.denote ctx 0 := by
simp [mul_ne_cert]; intro h₁ _ h₂; subst p
simp [Poly.denote_mulConst]; intro h₃
rw [ zsmul_eq_intCast_mul] at h₃
have := no_int_zero_divisors h₁ h₃
contradiction
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 +1752,79 @@ 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]
noncomputable def mul_ineq_cert (p₁ : Poly) (k : Int) (p : Poly) : Bool :=
k > 0 && (p₁.mulConst_k k |>.beq' p)
theorem le_mul {α} [CommRing α] [LE α] [LT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_ineq_cert p₁ k p p₁.denote ctx 0 p.denote ctx 0 := by
simp [mul_ineq_cert]; intro h₁ _ h₂; subst p; simp [Poly.denote_mulConst]
replace h₂ := zsmul_nonpos (Int.le_of_lt h₁) h₂
simp [Ring.zsmul_eq_intCast_mul] at h₂
assumption
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 lt_mul {α} [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPreorder α] [OrderedRing α] (ctx : Context α) (p₁ : Poly) (k : Int) (p : Poly)
: mul_ineq_cert p₁ k p p₁.denote ctx < 0 p.denote ctx < 0 := by
simp [mul_ineq_cert]; intro h₁ _ h₂; subst p; simp [Poly.denote_mulConst]
replace h₂ := zsmul_neg_iff k h₂ |>.mpr h₁
simp [Ring.zsmul_eq_intCast_mul] at h₂
assumption
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
@@ -1636,6 +1832,13 @@ theorem inv_int_eq [Field α] [IsCharP α 0] (b : Int) : b != 0 → (denoteInt b
have := IsCharP.intCast_eq_zero_iff (α := α) 0 b; simp [*] at this
rw [Field.mul_inv_cancel this]
theorem intCast_eq_denoteInt [Field α] (b : Int) : (IntCast.intCast b : α) = denoteInt b := by
simp [denoteInt_eq]
theorem inv_int_eq' [Field α] [IsCharP α 0] (b : Int) : b != 0 (IntCast.intCast b : α) * (denoteInt b)⁻¹ = 1 := by
rw [intCast_eq_denoteInt]
apply inv_int_eq
theorem inv_int_eqC {α c} [Field α] [IsCharP α c] (b : Int) : b % c != 0 (denoteInt b : α) * (denoteInt b)⁻¹ = 1 := by
simp; intro h
have : (denoteInt b : α) 0 := by

View File

@@ -16,6 +16,7 @@ namespace Lean.Grind
A field is a commutative ring with inverses for all non-zero elements.
-/
class Field (α : Type u) extends CommRing α, Inv α, Div α where
/-- An exponentiation operator. -/
[zpow : HPow α Int α]
/-- Division is multiplication by the inverse. -/
div_eq_mul_inv : a b : α, a / b = a * b⁻¹
@@ -94,7 +95,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 +170,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

@@ -842,7 +842,7 @@ Position reporting:
`#guard_msgs` appears.
- `positions := false` does not report position info.
For example, `#guard_msgs (error, drop all) in cmd` means to check warnings and drop
For example, `#guard_msgs (error, drop all) in cmd` means to check errors and drop
everything else.
The command elaborator has special support for `#guard_msgs` for linting.

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
@@ -2920,7 +2923,40 @@ protected def List.hasDecEq {α : Type u} [DecidableEq α] : (a b : List α) →
| isFalse nabs => isFalse (fun h => List.noConfusion h (fun _ habs => absurd habs nabs))
| isFalse nab => isFalse (fun h => List.noConfusion h (fun hab _ => absurd hab nab))
instance {α : Type u} [DecidableEq α] : DecidableEq (List α) := List.hasDecEq
instance {α : Type u} [DecidableEq α] : DecidableEq (List α) := fun xs ys =>
/-
The first match step is expanded to make this instance
maximally-definitionally-equivalent to the compare-with-empty-list cases.
-/
match xs with
| .nil => match ys with
| .nil => isTrue rfl
| .cons _ _ => isFalse List.noConfusion
| .cons a as => match ys with
| .nil => isFalse List.noConfusion
| .cons b bs =>
match decEq a b with
| isTrue hab =>
match List.hasDecEq as bs with
| isTrue habs => isTrue (hab habs rfl)
| isFalse nabs => isFalse (List.noConfusion · (fun _ habs => absurd habs nabs))
| isFalse nab => isFalse (List.noConfusion · (fun hab _ => absurd hab nab))
/--
Equality with `List.nil` is decidable even if the underlying type does not have decidable equality.
-/
instance List.instDecidableNilEq (a : List α) : Decidable (Eq List.nil a) :=
match a with
| .nil => isTrue rfl
| .cons _ _ => isFalse List.noConfusion
/--
Equality with `List.nil` is decidable even if the underlying type does not have decidable equality.
-/
instance List.instDecidableEqNil (a : List α) : Decidable (Eq a List.nil) :=
match a with
| .nil => isTrue rfl
| .cons _ _ => isFalse List.noConfusion
/--
The length of a list.
@@ -3413,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
/--
@@ -3432,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
@@ -3446,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
@@ -3498,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

@@ -27,6 +27,7 @@ public import Lean.Compiler.IR.Sorry
public import Lean.Compiler.IR.ToIR
public import Lean.Compiler.IR.ToIRType
public import Lean.Compiler.IR.Meta
public import Lean.Compiler.IR.Toposort
-- The following imports are not required by the compiler. They are here to ensure that there
-- are no orphaned modules.
@@ -71,6 +72,7 @@ def compile (decls : Array Decl) : CompilerM (Array Decl) := do
decls updateSorryDep decls
logDecls `result decls
checkDecls decls
decls toposortDecls decls
addDecls decls
inferMeta decls
return decls

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