Compare commits

...

127 Commits

Author SHA1 Message Date
Kim Morrison
eb896cabfa Merge remote-tracking branch 'origin/master' into align_extract 2025-02-17 15:37:00 +11:00
Kim Morrison
353eaf7014 feat: align Array/Vector.extract lemmas with List 2025-02-17 15:35:56 +11:00
Luisa Cicolini
6a17e62523 feat: add BitVec.[(getMsbD, msb)_extractLsb', (getLsbD, getMsbD, msb)_extractLsb] , add and_eq_decide, or_eq_decide, decide_eq_true_iff to bool_to_prop (#6792)
This PR adds theorems `BitVec.(getMsbD, msb)_(extractLsb', extractLsb),
getMsbD_extractLsb'_eq_getLsbD`.

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
Co-authored-by: Alex Keizer <alex@keizer.dev>
Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
Co-authored-by: Tobias Grosser <github@grosser.es>
2025-02-17 03:02:37 +00:00
Kim Morrison
1ce7047bf5 feat: cleanup of get and back functions on List/Array (#7059)
This PR moves away from using `List.get` / `List.get?` / `List.get!` and
`Array.get!`, in favour of using the `GetElem` mediated getters. In
particular it deprecates `List.get?`, `List.get!` and `Array.get?`. Also
adds `Array.back`, taking a proof, matching `List.getLast`.
2025-02-17 01:43:45 +00:00
Leonardo de Moura
ef759d874f fix: grind using reducible transparency setting (#7102)
This PR modifies `grind` to run with the `reducible` transparency
setting. We do not want `grind` to unfold arbitrary terms during
definitional equality tests. This PR also fixes several issues
introduced by this change. The most common problem was the lack of a
hint in proofs, particularly in those constructed using proof by
reflection. This PR also introduces new sanity checks when `set_option
grind.debug true` is used.
2025-02-16 22:30:04 +00:00
Kitamado
6f5bb3e896 fix: allow trailing comma in array syntax (#7055)
This PR improves array and vector literal syntax by allowing trailing
commas. For example, `#[1, 2, 3,]`.

see: [Why Are Trailing Commas Not Allowed in Array
Literals?](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Why.20Are.20Trailing.20Commas.20Not.20Allowed.20in.20Array.20Literals.3F)

Note: we need to preserve the current name for the array syntax
(`«term#[_,]»`) to avoid a bootstrapping issue. The `FromJson`/`ToJson`
deriving handlers use array syntax in macros, and the stage0 version is
used in most of the prelude.
2025-02-16 19:26:23 +00:00
Joachim Breitner
96c6f9dc96 feat: fun_induction and fun_cases tactics (#7069)
This PR adds the `fun_induction` and `fun_cases` tactics, which add
convenience around using functional induction and functional cases
principles.

```
fun_induction foo  x y z
```
elaborates `foo x y z`, then looks up `foo.induct`, and then essentially
does
```
induction z using foo.induct y
```
including and in particular figuring out which arguments are parameters,
targets or dropped. This only works for non-mutual functions so far.

Likewise there is the `fun_cases` tactic using `foo.fun_cases`.
2025-02-16 10:59:56 +00:00
Leonardo de Moura
f50b863868 feat: cutsat helper functions (#7098)
This PR adds some helper functions for cutsat in the `grind` tactic.
2025-02-16 05:32:46 +00:00
Leonardo de Moura
dd3652ecdc feat: cutsat preparations (#7097)
This PR implements several modifications for the cutsat procedure in
`grind`.
- The maximal variable is now at the beginning of linear polynomials. 
- The old `LinearArith.Solver` was deleted, and the normalizer was moved
to `Simp`.
- cutsat first files were created, and basic infrastructure for
representing divisibility constraints was added.
2025-02-16 02:52:14 +00:00
Tobias Grosser
a9efbf04f4 feat: make BitVec.getElem the simp normal form and use it in ext (#5498)
This PR makes `BitVec.getElem` the simp normal form in case a proof is
available and changes `ext` to return `x[i]` + a hypothesis that proves
that we are in-bounds. This aligns `BitVec` further with the API
conventions of the Lean standard datatypes.

We move our proofs to this new normal form, which results in slightly
smaller proofs. With the exception of `getElem_ofFin`, no new API
surface is added as the `getElem` API has already been completed over
the previous months. We also move `getElem_shiftConcat_*` a bit higher
as they are needed in earlier proofs. To keep the changeset small, we do
not update the API of `BVDecide` but insert `←
BitVec.getLsbD_eq_getElem` at the few locations where it is needed.
Finally, we add a simproc for getElem, mirroring the existing ones for
getLsbD/getMsdD.

---------

Co-authored-by: Alex Keizer <alex@keizer.dev>
2025-02-16 00:04:56 +00:00
Leonardo de Moura
3a76ac5620 chore: cleanup and missing grind normalization rules (#7095)
This PR adds missing `grind` normalization rules, and removes dead
theorems.
2025-02-15 23:45:35 +00:00
Leonardo de Moura
747ea91c3a refactor: add denote' functions to Int/Linear.lean (#7094)
This PR adds the functions `Poly.denote'`, `RelCnstr.denote'`, and
`DvdCnstr.denote'`. These functions are useful for representing the
denotation of normalized results in `simp +arith` and the `grind`
preprocessor. This PR also adjusts all auxiliary normalization theorems
to use them to represent the normalized constraints. Previously, we were
converting `RelCnstr` and `DvdCnstr` back into raw constraints. While
this overhead was reasonable for `simp +arith`, it is not for the cutsat
procedure, which has no need for raw constraints. All constraints have
already been normalized by the time they reach cutsat.
2025-02-15 22:10:23 +00:00
Leonardo de Moura
ecdc2d57f2 refactor: Int.Linear module (#7093)
This PR cleans up the `Int.Linear` module by normalizing function and
type names and adding documentation strings. We will use it to implement
cutsat in the `grind` tactic.
2025-02-15 19:20:18 +00:00
Leonardo de Moura
f4afcfc923 feat: divisibility constraint normalizer (#7092)
This PR implements divisibility constraint normalization in `simp
+arith`.
2025-02-15 04:20:40 +00:00
jrr6
9cce0ce8d9 fix: ensure get_elem_tactic works in absence of goals (#7088)
This PR fixes the behavior of the indexed-access notation `xs[i]` in
cases where the proof of `i`'s validity is filled in during unification.

Closes #6999.
2025-02-15 03:00:36 +00:00
Leonardo de Moura
57aadf8af9 feat: add helper theorems for normalizing divisibility constraints (#7091)
This PR adds helper theorems for normalizing divisibility constraints.
They are going to be used to implement the cutsat procedure in the
`grind` tactic.
2025-02-15 02:44:49 +00:00
Kyle Miller
1babe9fc67 feat: make binders in #check be hoverable (#7074)
This PR modifies the signature pretty printer to add hover information
for parameters in binders. This makes the binders be consistent with the
hovers in pi types.

Suggested by @david-christiansen
2025-02-14 17:28:54 +00:00
Markus Himmel
dd1a4188a0 feat: Fin.toNat (#7079)
This PR introduces `Fin.toNat` as an alias for `Fin.val`. We add this
function for discoverability and consistency reasons. The normal form
for proofs remains `Fin.val`, and there is a `simp` lemma rewriting
`Fin.toNat` to `Fin.val`.
2025-02-14 11:59:44 +00:00
Markus Himmel
ed42d068d4 feat: UIntX.ofNatTruncate (#7080)
This PR adds the functions `UIntX.ofNatTruncate` (the version for
`UInt32` already exists).
2025-02-14 11:59:41 +00:00
Markus Himmel
784444c7a9 feat: IntX.minValue, IntX.maxValue, IntX.ofIntLE, IntX.ofIntTruncate (#7081)
This PR adds functions `IntX.ofIntLE`, `IntX.ofIntTruncate`, which are
analogous to the unsigned counterparts `UIntX.ofNatLT` and
`UInt.ofNatTruncate`.
2025-02-14 11:59:37 +00:00
Marc Huisinga
05fb67af90 feat: request cancellation (#7054)
This PR adds language server support for request cancellation to the
following expensive requests: Code actions, auto-completion, document
symbols, folding ranges and semantic highlighting. This means that when
the client informs the language server that a request is stale (e.g.
because it belongs to a previous state of the document), the language
server will now prematurely cancel the computation of the response in
order to reduce the CPU load for requests that will be discarded by the
client anyways.
2025-02-14 11:55:43 +00:00
Marc Huisinga
22d1d04059 fix: incremental goal state requests select incomplete snapshot (#6887)
This PR fixes a bug where the goal state selection would sometimes
select incomplete incremental snapshots on whitespace, leading to an
incorrect "no goals" response. Fixes #6594, a regression that was
originally introduced in 4.11.0 by #4727.

The fundamental cause of #6594 was that the snapshot selection would
always select the first snapshot with a range that contains the cursor
position. For tactics, whitespace had to be included in this range.
However, in the test case of #6594, this meant that the snapshot
selection would also sometimes pick a snapshot before the cursor that
still contains the cursor in its whitespace, but which also does not
necessarily contain all the information needed to produce a correct goal
state. Specifically, at the `InfoTree`-level, when the cursor is in
whitespace, we distinguish competing goal states by their level of
indentation. The snapshot selection did not have access to this
information, so it necessarily had to do the wrong thing in some cases.

This PR fixes the issue by adjusting the snapshot selection for goals to
explicitly account for whitespace and indentation, and refactoring the
language processor architecture to thread enough information through to
the snapshot selection so that it can decide which snapshots to use
without having to force too many tasks, which would destroy
incrementality in goal state requests.

Specifically, this PR makes the following adjustments:
- Refactor `SnapshotTask` to contain both a `Syntax` and a `Range`.
Before, `SnapshotTask`s had a single range that was used both for
displaying file progress information and for selecting snapshots in
server requests. For most snapshots, this range did not include
whitespace, though for tactics it did. Now, the `reportingRange` field
of `SnapshotTask` is intended exclusively for reporting file progress
information, and the `Syntax` is used for selecting snapshots in server
requests. Importantly, the `Syntax` contains the full range information
of the snapshot, i.e. its regular range and its range including
whitespace.
- Adjust all call-sites of `SnapshotTask` to produce a reasonable
`Syntax`.
- Adjust the goal snapshot selection to account for whitespace and
indentation, as the `InfoTree` goal selection does.
- Fix a bug in the snapshot tree tracing that would cause it to render
the `Info` of a snapshot at the wrong location when `trace.Elab.info`
was also set.

This PR is based on #6329.
2025-02-14 11:53:24 +00:00
Paul Reichert
36ac6eb912 feat: insertMany, ofList, ofArray, foldr, foldM functions for the tree map (#7051)
This PR implements the methods `insertMany`, `ofList`, `ofArray`,
`foldr` and `foldrM` on the tree map.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-14 08:24:33 +00:00
Markus Himmel
47548aa171 chore: rename UIntX.ofNatCore, UIntX.ofNat' -> UIntX.ofNatLT (#7071)
This PR unifies the existing functions `UIntX.ofNatCore` and
`UIntX.ofNat'` under a new name, `UIntX.ofNatLT`.
2025-02-14 06:58:15 +00:00
Leonardo de Moura
b26b781992 feat: simprocs for Int and Nat divides predicates (#7078)
This PR implements simprocs for `Int` and `Nat` divides predicates.
2025-02-14 05:43:38 +00:00
Mac Malone
c9c3366521 feat: lake: support plugins (#7001)
This PR adds support for plugins to Lake. Precompiled modules are now
loaded as plugins rather than via `--load-dynlib`.

Additional plugins can be added through an experimental `plugins`
configuration option. The syntax for specifying this is not yet
convenient, and will be improved in future changes. A parallel `dynlibs`
configuration option has been added for specifying additional dynamic
libraries to build and pass to `--load-dynlib`.

This PR also changes the default directory for `.olean`, `.ilean`, and
module dynamic libraries (i.e., `leanLibDir`) to `lib/lean` instead of
the previous default of `lib`. This avoids potential name clashes
between single module shared libraries and the shared libraries of a
full `lean_lib`.

On non-Windows systems, module dynamic libraries are no longer linked to
their imports or external symbols. Symbols from those libraries are left
unresolved until load time. This avoids nesting these dependencies
within the shared library and means Lake no longer needs to augment the
shared library path to allow Lean to resolve such nested dependencies on
load.
2025-02-14 04:57:31 +00:00
Leonardo de Moura
2c2a3a65b2 feat: support theorems for cutsat Div-Solve rule (#7077)
This PR proves the helper theorems for justifying the "Div-Solve" rule
in the cutsat procedure.
2025-02-14 04:55:58 +00:00
Kim Morrison
8cefb2cf65 feat: premise selection API (#7061)
This PR provides a basic API for a premise selection tool, which can be
provided in downstream libraries. It does not implement premise
selection itself!
2025-02-14 04:08:18 +00:00
Lean stage0 autoupdater
80c8837f49 chore: update stage0 2025-02-13 16:00:29 +00:00
Markus Himmel
40c6dfa3ae chore: dsimproc for UIntX.ofNatLT (#7068)
This PR is a follow-up to #7057 and adds a builtin dsimproc for
`UIntX.ofNatLT` which it turns out we need in stage0 before we can get
the deprecation of `UIntX.ofNatCore` in favor of `UIntX.ofNatLT` off the
ground.
2025-02-13 14:51:42 +00:00
Bulhwi Cha
cc76c46244 doc: fix typo (#7067) 2025-02-13 13:21:18 +00:00
Markus Himmel
b38da34db2 chore: rename BitVec.ofNatLt -> BitVec.ofNatLT (#7064)
This PR renames `BitVec.ofNatLt` to `BitVec.ofNatLT` and sets up
deprecations for the old name.
2025-02-13 12:52:31 +00:00
Markus Himmel
4a900cc65c chore: rename IntX.toNat -> IntX.toNatClampNeg (#7066)
This PR renames `IntX.toNat` to `IntX.toNatClampNeg` (to reduce
surprises) and sets up a deprecation.
2025-02-13 12:14:28 +00:00
Markus Himmel
a3fd2eb0fe chore: make IntX constructor private, provide UIntX.toIntX (#7062)
This PR introduces the functions `UIntX.toIntX` as the public API to
obtain the `IntX` that is 2's complement equivalent to a given `UIntX`.
2025-02-13 11:29:31 +00:00
Paul Reichert
6ac530aa1a feat: deprecated find, fold, foldM, mergeBy functions for the tree map (#7036)
This PR adds some deprecated function aliases to the tree map in order
to ease the transition from the `RBMap` to the tree map.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-13 11:12:22 +00:00
Markus Himmel
04fe72fee0 feat: missing conversion functions for ISize (#7063)
This PR adds `ISize.toInt8`, `ISize.toInt16`, `Int8.toISize`,
`Int16.toISize`.
2025-02-13 11:02:00 +00:00
Joachim Breitner
a833afa935 feat: binderNameHint in congr (#7053)
This PR makes `simp` heed the `binderNameHint` also in the assumptions
of congruence rules. Fixes #7052.
2025-02-13 09:38:42 +00:00
Markus Himmel
7c9454edd2 feat: UIntX.ofFin (#7056)
This PR adds the `UIntX.ofFin` conversion functions.
2025-02-13 08:45:01 +00:00
Markus Himmel
1ecb4a43ae chore: rename UIntX.val -> UIntX.toFin (#7050)
This PR renames the functions `UIntX.val` to `UIntX.toFin`.
2025-02-13 07:50:47 +00:00
Kim Morrison
ae9d12aeaa chore: upstream an Int lemma (#7060) 2025-02-13 03:19:02 +00:00
Leonardo de Moura
e617ce7e4f refactor: move grind offset constraint module to Grind/Arith/Offset (#7058)
This PR moves the `grind` offset constraint module to the
`Grind/Arith/Offset` subdirectory in preparation to the full linear
integer arithmetic module.
2025-02-12 23:16:07 +00:00
Lean stage0 autoupdater
b9894b40af chore: update stage0 2025-02-12 17:09:23 +00:00
Markus Himmel
9ff4d53d0b chore: rename UIntX.mk -> UIntX.ofBitVec (#7046)
This PR renames `UIntX.mk` to `UIntX.ofBitVec` and adds deprecations.
2025-02-12 16:08:03 +00:00
Markus Himmel
1e262c2c0e chore: add UIntX.ofNatLT (#7057)
This PR adds the function `UIntX.ofNatLT`. This is supposed to be a
replacement for `UIntX.ofNatCore` and `UIntX.ofNat'`, but for
bootstrapping reasons we need this function to exist in stage0 before we
can proceed with the renaming and deprecations, so this PR just adds the
function.
2025-02-12 15:12:29 +00:00
Markus Himmel
b08fc5dfda feat: IntX.ofBitVec (#7048)
This PR adds the functions `IntX.ofBitVec`.
2025-02-12 14:49:31 +00:00
Joachim Breitner
761c88f10e feat: propagate wfParam through let (#7039)
This PR improves the well-founded definition preprocessing to propagate
`wfParam` through let expressions.

Fixes #7038.
2025-02-12 13:22:08 +00:00
Sebastian Ullrich
07b0e5b7fe chore: compile against glibc 2.26 (#7037)
This PR relaxes the minimum required glibc version for Lean and Lean
executables to 2.26 on x86-64 Linux
2025-02-12 09:29:51 +00:00
Sebastian Ullrich
f7e207a824 chore: remove save tactic (#7047)
This PR removes the `save` and `checkpoint` tactics that have been
superseded by incremental elaboration
2025-02-12 09:19:30 +00:00
Cameron Zwarich
f61e2989a2 fix: make several LCNF environment extensions have asyncMode of .sync (#7041)
This PR marks several LCNF-specific environment extensions as having an
asyncMode of .sync rather than the default of .mainOnly, so they work
correctly even in async contexts.
2025-02-12 09:13:49 +00:00
Joachim Breitner
bdf4b792a8 feat: wf_preprocess for {List,Array}.Monadic functions (#7034)
This PR adds `wf_preprocess` theorems for
`{List,Array}.{foldlM,foldrM,mapM,filterMapM,flatMapM}`
2025-02-12 09:06:12 +00:00
Sebastian Ullrich
d3af1268a7 test: fix simp_arith1 benchmark (#7049) 2025-02-12 10:22:32 +00:00
Lean stage0 autoupdater
01be97309e chore: update stage0 2025-02-12 09:15:43 +00:00
Kim Morrison
3cf6fb2405 chore: fix linter.listVariables naming (#7044) 2025-02-12 05:17:39 +00:00
Leonardo de Moura
2a67a49f31 chore: simp_arith has been deprecated (#7043)
This PR deprecates the tactics `simp_arith`, `simp_arith!`,
`simp_all_arith` and `simp_all_arith!`. Users can just use the `+arith`
option.
2025-02-12 03:55:45 +00:00
Leonardo de Moura
fb2e5e5555 chore: remove dead code from Nat/Linear.lean (#7042) 2025-02-12 02:14:00 +00:00
Leonardo de Moura
b87c01b1c0 feat: simp +arith sorts linear atoms (#7040)
This PR ensures that terms such as `f (2*x + y)` and `f (y + x + x)`
have the same normal form when using `simp +arith`
2025-02-11 23:37:30 +00:00
Paul Reichert
0f1133fe69 feat: tree map data structures and operations (#6914)
This PR introduces ordered map data structures, namely `DTreeMap`,
`TreeMap`, `TreeSet` and their `.Raw` variants, into the standard
library. There are still some operations missing that the hash map has.
As of now, the operations are unverified, but the corresponding lemmas
will follow in subsequent PRs. While the tree map has already been
optimized, more micro-optimization will follow as soon as the new code
generator is ready.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-11 14:47:47 +00:00
Henrik Böving
f348a082da feat: present bv_decide counter examples for UIntX and enums better (#7033)
This PR improves presentation of counter examples for UIntX and enum
inductives in bv_decide.
2025-02-11 11:01:40 +00:00
Leonardo de Moura
befee896b3 feat: linear integer inequality normalization using gcd of coefficients (#7030)
This PR adds completes the linear integer inequality normalizer for
`grind`. The missing normalization step replaces a linear inequality of
the form `a_1*x_1 + ... + a_n*x_n + b <= 0` with `a_1/k * x_1 + ... +
a_n/k * x_n + ceil(b/k) <= 0` where `k = gcd(a_1, ..., a_n)`.
`ceil(b/k)` is implemented using the helper `cdiv b k`.
2025-02-11 03:45:25 +00:00
Mac Malone
e7fa5891ea feat: lake: provide help on Elan's + option (#7024)
This PR documents how to use Elan's `+` option with `lake new|init`. It
also provides an more informative error message if a `+` option leaks
into Lake (e.g., if a user provides the option to a Lake run without
Elan).
2025-02-11 00:43:38 +00:00
Sebastian Ullrich
3927445973 chore: build Lean with Elab.async (#6989) 2025-02-10 18:16:20 +00:00
Henrik Böving
7d1d761148 feat: bv_decide rewrite multiplication with power of two to shift (#7029)
This PR adds simprocs to bv_decide's preprocessor that rewrite
multiplication with powers of two to constant shifts.
2025-02-10 17:42:59 +00:00
Sebastian Ullrich
7790420cae chore: trivial changes from async-proofs branch (#7028) 2025-02-10 16:44:05 +00:00
Joachim Breitner
4016a80f66 feat: nested well-founded recursion via automatic preprocessing (#6744)
This PR extend the preprocessing of well-founded recursive definitions
to bring assumptions like `h✝ : x ∈ xs` into scope automatically.

This fixes #5471, and follows (roughly) the design written there.
See the module docs at `src/Lean/Elab/PreDefinition/WF/AutoAttach.lean`
for details on the implementation.

This only works for higher-order functions that have a suitable setup.
See for example section “Well-founded recursion preprocessing setup” in
`src/Init/Data/List/Attach.lean`.

This does not change the `decreasing_tactic`, so in some cases there is
still the need for a manual termination proof some cases. We expect a
better termination tactic in the near future.
2025-02-10 16:43:41 +00:00
Lean stage0 autoupdater
feb8cc2d4a chore: update stage0 2025-02-10 16:30:51 +00:00
Markus Himmel
5eed373feb doc: misc. style guide and naming scheme additions (#7026)
This PR clarifies the styling of `do` blocks, and enhanes the naming
conventions with information about the `ext` and `mono` name components
as well as advice about primed names and naming of simp sets.
2025-02-10 15:27:30 +00:00
Sebastian Ullrich
895cdce9bc fix: codegen was allowed improper env ext accesses (#7023) 2025-02-10 15:08:02 +00:00
Kim Morrison
3411518548 chore: rename simp sets (#7017)
This PR renames the simp set `boolToPropSimps` to `bool_to_prop` and
`bv_toNat` to `bitvec_to_nat`. I'll be adding more similarly named simp
sets.
2025-02-10 14:20:18 +00:00
Kim Morrison
13b4b11657 chore: deprecated compile_time_search_path% (#7022)
This PR deprecates `compile_time_search_path%`; it didn't prove useful,
and we've shot ourselves in the foot with it more than once.
2025-02-10 13:49:17 +00:00
Henrik Böving
fa05bccd58 feat: add basic extract theorems for bv_decide (#7021)
This PR adds theorems for interactions of extractLsb with `&&&`, `^^^`,
`~~~` and `bif` to bv_decide's preprocessor.
2025-02-10 13:48:20 +00:00
Kim Morrison
c307e8a04f feat: improvements to simp confluence (#7013)
This PR makes improvements to the simp set for List/Array/Vector/Option
to improve confluence, in preparation for `simp_lc`.
2025-02-10 12:17:44 +00:00
Henrik Böving
2aca375cd9 fix: correct trace nodes in bv_decide (#7019)
This PR properly spells out the trace nodes in bv_decide so they are
visible with just `trace.Meta.Tactic.bv` and `trace.Meta.Tactic.sat`
instead of always having to enable the profiler.
2025-02-10 11:24:52 +00:00
Lean stage0 autoupdater
46ae4c0d7c chore: update stage0 2025-02-10 11:58:06 +00:00
Sebastian Ullrich
6f445a1c05 chore: Task.get block profiling (#7016)
* `--profile` now reports `blocking` time spent in `Task.get` inside
other profiling categories
* environment variable `LEAN_TRACE_TASK_GET_BLOCKED` when set makes
`lean` dump stack traces of `Task.get` blocks
2025-02-10 10:56:49 +00:00
Kim Morrison
80cf782bc6 chore: rename simp sets (#7018)
This is preliminary to #7017; we'll need an update-stage0 before the
actual rename can take place.
2025-02-10 10:56:20 +00:00
Kim Morrison
1622f578c9 chore: replace HashMap.get_ lemmas with getElem_ versions (#7004)
This PR replaces various `HashMap.get_X` with `getElem_X` versions. Now
the left hand sides are in simp normal form (and this fixes some
confluence problems).
2025-02-10 10:37:21 +00:00
Kim Morrison
47814f9da1 chore: add @[simp] to List.flatten_toArray (#7014) 2025-02-10 10:30:41 +00:00
Henrik Böving
0d95bf68cc feat: basic support for handling enum inductives in bv_decide (#6946)
This PR implements basic support for handling of enum inductives in
`bv_decide`. It now supports equality on enum inductive variables (or
other uninterpreted atoms) and constants.
2025-02-10 10:00:20 +00:00
Leonardo de Moura
d61f506da2 feat: simp +arith normalizes coefficient in linear integer polynomials (#7015)
This PR makes sure `simp +arith` normalizes coefficients in linear
integer polynomials. There is still one todo: tightening the bound of
inequalities.
2025-02-10 06:13:28 +00:00
Kim Morrison
7f3e170509 chore: unprotect List.foldlM (#7003) 2025-02-09 22:54:51 +00:00
Leonardo de Moura
bcffbdd3a1 chore: improve withAbstractAtoms (#7012)
We should not abstract free variables
2025-02-09 22:46:09 +00:00
Leonardo de Moura
e14c593003 feat: simp +arith for integers (#7011)
This PR adds `simp +arith` for integers. It uses the new `grind`
normalizer for linear integer arithmetic. We still need to implement
support for dividing the coefficients by their GCD. It also fixes
several bugs in the normalizer.
2025-02-09 21:41:58 +00:00
Leonardo de Moura
bcde913a96 chore: improve expose_names doc string (#7010) 2025-02-09 17:24:07 +00:00
Leonardo de Moura
33b45132a4 feat: bv_decide hint (#7009)
This PR ensures users get an error message saying which module to import
when they try to use `bv_decide`.
2025-02-09 17:11:28 +00:00
Kim Morrison
ef4c6ed83c chore: remove unused Int simp lemmas (#7005) 2025-02-09 16:20:38 +00:00
Leonardo de Moura
cd3eb9125c feat: linear integer arith normalizer (#7002)
This PR implements the normalizer for linear integer arithmetic
expressions. It is not connect to `simp +arith` yet because of some
spurious `[simp]` attributes.
2025-02-09 04:32:54 +00:00
Leonardo de Moura
f6c5aed7ef feat: add Int.Linear normalization support (#7000)
This PR adds helper theorems for justifying the linear integer
normalizer.
2025-02-08 23:01:01 +00:00
Kyle Miller
dd293d1fbd doc: mention Props are equal to True or False (#6998)
This PR modifies the `Prop` docstring to point out that every
proposition is propositionally equal to either `True` or `False`. This
will help point users toward seeing that `Prop` is like `Bool`.

I considered mentioning `Classical.propComplete`, but it's probably
better not making it seem like that's how you should work with
propositions.
2025-02-08 18:11:26 +00:00
Bolton Bailey
4989a60af3 chore: change Lake configuration error message (#6829)
This PR changes the error message for Lake configuration failure to
reflect that issues do not always arise from an invalid lakefile, but
sometimes arise from other issues like network errors. The new error
message encompasses all of these possibilities.

Closes #6827
2025-02-08 15:04:39 +00:00
Joachim Breitner
7c809a94af refactor: elaborate forIn notation without extra let (#6977)
This PR avoids a `let` in the elaboration of `forIn`. It was introduced
in https://github.com/leanprover/lean4/commit/f51328ff112 but nothing
seems to break when I simplify the code. This removes an unexpected `let
col✝ :=…` from the “Expected type” view in the Info View and from the
termination proofs.
2025-02-08 10:32:34 +00:00
Leonardo de Moura
5eca093a89 feat: exact? in try? (#6995)
This PR implements support for `exact?` in the `try?` tactic.
2025-02-07 22:43:30 +00:00
Leonardo de Moura
6d46e31ad8 feat: compress try? suggestions (#6994)
This PR adds the `Try.Config.merge` flag (`true` by default) to the
`try?` tactic. When set to `true`, `try?` compresses suggestions such
as:
```lean
· induction xs, ys using bla.induct
    · grind only [List.length_reverse]
    · grind only [bla]
```
into:
```lean
induction xs, ys using bla.induct <;> grind only [List.length_reverse, bla]
```

This PR also ensures `try?` does not generate suggestions that mixes
`grind` and `grind only`, or `simp` and `simp only` tactics.

This PR also adds the `try? +harder` option (previously called `lib`),
but it has not been fully implemented yet.
2025-02-07 19:17:25 +00:00
Leonardo de Moura
605b9e63c9 chore: disable broken test
It is timing out on OSX, and `master` is failing to build.
This is a temporary "fix."
2025-02-07 11:13:50 -08:00
Sebastian Ullrich
0d1907c1df feat: parallel progress notifications (#6329)
This PR enables the language server to present multiple disjoint line
ranges as being worked on. Even before parallelism lands, we make use of
this feature to show post-elaboration tasks such as kernel checking on
the first line of a declaration to distinguish them from the final
tactic step.


![image](https://github.com/user-attachments/assets/f6170689-6835-40c0-baba-df067a60b605)
2025-02-07 16:50:31 +00:00
Leonardo de Moura
2b67ef451a feat: improve try? suggestion (#6991)
This PR improves how suggestions for the `<;>` combinator are generated.
2025-02-07 16:33:25 +00:00
Sebastian Ullrich
bfe2d28c50 chore: re-enable Elab.async in the server (#6990) 2025-02-07 16:12:31 +00:00
Sebastian Ullrich
de24063c4b fix: convert kernel interrupt into elab interrupt (#6988)
This PR ensures interrupting the kernel does not lead to wrong, sticky
error messages in the editor
2025-02-07 15:55:32 +00:00
Sebastian Ullrich
7c79f05cd4 feat: API to avoid deadlocks from dropped promises (#6958)
This PR improves the `Promise` API by considering how dropped promises
can lead to never-finished tasks.
2025-02-07 15:33:10 +00:00
Sebastian Ullrich
1248a55d32 perf: avoid taking mutex on already-resolved promises (#6984) 2025-02-07 10:14:35 +00:00
Sebastian Ullrich
ac9708051a feat: respect Task.map/bind (sync := true) after waiting (#6976)
This PR extends the behavior of the `sync` flag for `Task.map/bind` etc.
to encompass synchronous execution even when they first have to wait on
completion of the first task, drastically lowering the overhead of such
tasks. Thus the flag is now equivalent to e.g. .NET's
`TaskContinuationOptions.ExecuteSynchronously`.
2025-02-07 09:06:57 +00:00
Kim Morrison
af385d7c10 feat: improve monadic Array lemmas (#6982)
This PR improves some lemmas about monads and monadic operations on
Array/Vector, using @Rob23oa's work in
https://github.com/leanprover-community/batteries/pull/1109, and
adding/generalizing some additional lemmas.
2025-02-07 04:02:02 +00:00
Kim Morrison
92f0d31ed7 chore: linting List (#6970) 2025-02-07 01:44:51 +00:00
Leonardo de Moura
0376cae739 feat: try? tactic improvements (#6981)
This PR adds new configuration options to `try?`.
- `try? -only` omits `simp only` and `grind only` suggestions
- `try? +missing` enables partial solutions where some subgoals are
"solved" using `sorry`, and must be manually proved by the user.
- `try? (max:=<num>)` sets the maximum number of suggestions produced
(default is 8).
2025-02-07 01:35:41 +00:00
Leonardo de Moura
c92425f98d feat: try? validation and cleanup (#6980)
This PR improves the `try?` tactic runtime validation and error
messages. It also simplifies the implementation, and removes unnecessary
code.
2025-02-06 23:59:38 +00:00
Sofia Rodrigues
52198837df feat: improve some files separation and standardize error messages in UV modules (#6830)
This PR improves some files separation and standardize error messages in
UV modules
2025-02-06 23:24:42 +00:00
Leonardo de Moura
eab09084a3 feat: try? composite suggestions (#6979)
This PR adds support for more complex suggestions in `try?`. Example:
```lean
example (as : List α) (a : α) : concat as a = as ++ [a] := by
  try?
```
suggestion
```
Try this: · induction as, a using concat.induct
  · rfl
  · simp_all
```
2025-02-06 21:56:14 +00:00
Marc Huisinga
45d39422bc fix: inlay hints in untitled files (#6978)
This PR fixes a bug where both the inlay hint change invalidation logic
and the inlay hint edit delay logic were broken in untitled files.
Thanks to @Julian for spotting this!
2025-02-06 19:26:11 +00:00
Lean stage0 autoupdater
06d022b9c0 chore: update stage0 2025-02-06 17:39:42 +00:00
Marc Huisinga
dcd70cbfba feat: inlay hint refinements (#6959)
This PR implements a number of refinements for the auto-implicit inlay
hints implemented in #6768.
Specifically:
- In #6768, there was a bug where the inlay hint edit delay could
accumulate on successive edits, which meant that it could sometimes take
much longer for inlay hints to show up. This PR implements the basic
infrastructure for request cancellation and implements request
cancellation for semantic tokens and inlay hints to resolve the issue.
With this edit delay bug fixed, it made more sense to increase the edit
delay slightly from 2000ms to 3000ms.
- In #6768, we applied the edit delay to every single inlay hint request
in order to reduce the amount of inlay hint flickering. This meant that
the edit delay also had a significant effect on how far inlay hints
would lag behind the file progress bar. This PR adjusts the edit delay
logic so that it only affects requests sent directly after a
corresponding `didChange` notification. Once the edit delay is used up,
all further semantic token requests are responded to without delay, so
that the only latency that affects how far the inlay hints lag behind
the progress bar is how often we emit refresh requests and how long VS
Code takes to respond to them.
- For inlay hints, refresh requests are now emitted 500ms after a
response to an inlay hint request, not 2000ms, which means that after
the edit delay, inlay hints should only lag behind the progress bar by
about up to 500ms. This is justifiable for inlay hints because the
response should be much smaller than e.g. is the case for semantic
tokens.
- In #6768, 'Restart File' did not prompt a refresh, but it does now.
- VS Code does not immediately remove old inlay hints from the document
when they are applied. In #6768, this meant that inlay hints would
linger around for a bit once applied. To mitigate this issue, this PR
adjusts the inlay hint edit delay logic to identify edits sent from the
client as being inlay hint applications, and sets the edit delay to 0ms
for the inlay hint requests following it. This means that inlay hints
are now applied immediately.
- In #6768, hovering over single-letter auto-implicit inlay hints was a
bit finicky because VS Code uses the regular cursor icon on inlay hints,
not the thin text cursor icon, which means that it is easy to put the
cursor in the wrong spot. We now add the separation character (` ` or
`{`) preceding an auto-implicit to the hover range as well, which makes
hovering over inlay hints much smoother.
2025-02-06 16:43:56 +00:00
Lean stage0 autoupdater
07c880f7ff chore: update stage0 2025-02-06 12:27:11 +00:00
Joachim Breitner
2e6206bbeb refactor: rename auto_attach attribute to wf_preprocess (#6972)
As per dicussion with team colleages, the feature shouldn’t be called
“auto attach” but rather “well-founded recursion preprocessing” to avoid
(imprecise) jargon.
2025-02-06 11:28:23 +00:00
Henrik Böving
4540a6436f refactor: bv_decide's type analysis to prepare for enum support (#6971)
This PR does some refactoring on bv_decide's type analysis in
preparation for enum support in #6946.
2025-02-06 11:16:57 +00:00
Joachim Breitner
dc001a01e5 feat: binderNameHint (#6947)
This PR adds the `binderNameHint` gadget. It can be used in rewrite and
simp rules to preserve a user-provided name where possible.

The expression `binderNameHint v binder e` defined to be `e`.

If it is used on the right-hand side of an equation that is applied by a
tactic like `rw` or `simp`,
and `v` is a local variable, and `binder` is an expression that (after
beta-reduction) is a binder
(so `fun w => …` or `∀ w, …`), then it will rename `v` to the name used
in the binder, and remove
the `binderNameHint`.

A typical use of this gadget would be as follows; the gadget ensures
that after rewriting, the local
variable is still `name`, and not `x`:
```
theorem all_eq_not_any_not (l : List α) (p : α → Bool) :
    l.all p = !l.any fun x => binderNameHint x p (!p x) := sorry

example (names : List String) : names.all (fun name => "Waldo".isPrefixOf name) = true := by
  rw [all_eq_not_any_not]
  -- ⊢ (!names.any fun name => !"Waldo".isPrefixOf name) = true
```

This gadget is supported by `simp`, `dsimp` and `rw` in the
right-hand-side of an equation, but not
in hypotheses or by other tactics.
2025-02-06 11:03:27 +00:00
Kim Morrison
a00cc12436 chore: rename Nat.not_eq_zero_of_lt (#6968)
Renames a lemma.

Closes #6714
2025-02-06 10:20:17 +00:00
Markus Himmel
947cd742bf doc: style guide and naming convention for the standard library (#6950)
This PR adds a style guide and a naming convention for the standard
library.
2025-02-06 08:33:48 +00:00
Lean stage0 autoupdater
ee42e8cf24 chore: update stage0 2025-02-06 08:27:23 +00:00
Leonardo de Moura
b01ca8ee23 feat: use expose_names in try? (#6967)
This PR ensures `try?` can suggest tactics that need to reference
inaccessible local names.
Example: 
```lean
/--
info: Try these:
• · expose_names; induction as, bs_1 using app.induct <;> grind [= app]
• · expose_names; induction as, bs_1 using app.induct <;> grind only [app]
-/
#guard_msgs (info) in
example : app (app as bs) cs = app as (app bs cs) := by
  have bs := 20 -- shadows `bs` in the target
  try?
```
2025-02-06 05:44:25 +00:00
Kim Morrison
fd4599fd7a feat: add internal linter for List/Array/Vector variable names (#6966)
This PR adds an internal-use-only strict linter for the variable names
of `List`/`Array`/`Vector` variables, and begins cleaning up.
2025-02-06 04:49:21 +00:00
Leonardo de Moura
fbeec32c2b feat: implement try? using evalAndSuggest (#6965)
This PR re-implements the `try?` tactic using the new `evalAndSuggest`
infrastructure.
2025-02-06 04:47:26 +00:00
Kim Morrison
de99c8015a feat: #info_trees in command (#6964)
This PR adds a convenience command `#info_trees in`, which prints the
info trees generated by the following command. It is useful for
debugging or learning about `InfoTree`.
2025-02-06 03:11:53 +00:00
Kim Morrison
49297f12a5 chore: further cleanup of index variable naming in List (#6963) 2025-02-06 02:39:06 +00:00
Kim Morrison
8fd107c10f doc: improve List.toArray doc-string (#6962)
This PR improves the doc-string for `List.toArray`.

Thanks to @jt0202 for pointing this out.
2025-02-06 01:56:47 +00:00
Leonardo de Moura
13b1f56f88 feat: evalAndSuggest helper tactic (#6961)
This PR adds the auxiliary tactic `evalAndSuggest`. It will be used to
refactor `try?`.
2025-02-05 22:13:47 +00:00
Lean stage0 autoupdater
20e67945ea chore: update stage0 2025-02-05 16:42:25 +00:00
jrr6
60aeb79a75 feat: allow updating binders to and from strict- and instance-implicit (#6634)
This PR adds support for changing the binder annotations of existing
variables to and from strict-implicit and instance-implicit using the
`variable` command.

This PR requires a stage0 update to fully take effect.

Closes #6078
2025-02-05 15:43:54 +00:00
Lean stage0 autoupdater
f1ed830b9a chore: update stage0 2025-02-05 14:42:28 +00:00
Joachim Breitner
255d931e0c feat: add auto_attach simp set (no functionality yet) (#6956)
this PR helps with bootstrapping #6744.
2025-02-05 13:33:35 +00:00
915 changed files with 20072 additions and 4376 deletions

View File

@@ -137,7 +137,6 @@ jobs:
let large = ${{ github.repository == 'leanprover/lean4' }};
let matrix = [
{
// portable release build: use channel with older glibc (2.27)
"name": "Linux LLVM",
"os": "ubuntu-latest",
"release": false,
@@ -152,6 +151,7 @@ jobs:
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
},
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"release": true,
@@ -175,8 +175,8 @@ jobs:
"os": "ubuntu-latest",
"check-level": 2,
"CMAKE_PRESET": "debug",
// exclude seriously slow tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress'"
// exclude seriously slow/stackoverflowing tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress|3807'"
},
// TODO: suddenly started failing in CI
/*{

View File

@@ -179,7 +179,7 @@ local macro "have_eq " lhs:term:max rhs:term:max : tactic =>
`(tactic|
(have h : $lhs = $rhs :=
-- TODO: replace with linarith
by simp_arith at *; apply Nat.le_antisymm <;> assumption
by simp +arith at *; apply Nat.le_antisymm <;> assumption
try subst $lhs))
/-!

View File

@@ -4,7 +4,7 @@
Platforms built & tested by our CI, available as binary releases via elan (see below)
* x86-64 Linux with glibc 2.27+
* x86-64 Linux with glibc 2.26+
* x86-64 macOS 10.15+
* aarch64 (Apple Silicon) macOS 10.15+
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022

9
doc/std/README.md Normal file
View File

@@ -0,0 +1,9 @@
# The Lean standard library
This directory contains development information about the Lean standard library. The user-facing documentation of the standard library
is part of the [Lean Language Reference](https://lean-lang.org/doc/reference/latest/).
Here you will find
* the [standard library vision document](./vision.md), including the call for contributions,
* the [standard library style guide](./style.md), and
* the [standard library naming conventions](./naming.md).

3
doc/std/naming-tree.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 68 KiB

241
doc/std/naming.md Normal file
View File

@@ -0,0 +1,241 @@
# Standard library naming conventions
The easiest way to access a result in the standard library is to correctly guess the name of the declaration (possibly with the help of identifier autocompletion). This is faster and has lower friction than more sophisticated search tools, so easily guessable names (which are still reasonably short) make Lean users more productive.
The guide that follows contains very few hard rules, many heuristics and a selection of examples. It cannot and does not present a deterministic algorithm for choosing good names in all situations. It is intended as a living document that gets clarified and expanded as situations arise during code reviews for the standard library. If applying one of the suggestions in this guide leads to nonsensical results in a certain situation, it is
probably safe to ignore the suggestion (or even better, suggest a way to improve the suggestion).
## Prelude
Identifiers use a mix of `UpperCamelCase`, `lowerCamelCase` and `snake_case`, used for types, data, and theorems, respectively.
Structure fields should be named such that the projections have the correct names.
## Naming convention for types
When defining a type, i.e., a (possibly 0-ary) function whose codomain is Sort u for some u, it should be named in UpperCamelCase. Examples include `List`, and `List.IsPrefix`.
When defining a predicate, prefix the name by `Is`, like in `List.IsPrefix`. The `Is` prefix may be omitted if
* the resulting name would be ungrammatical, or
* the predicate depends on additional data in a way where the `Is` prefix would be confusing (like `List.Pairwise`), or
* the name is an adjective (like `Std.Time.Month.Ordinal.Valid`)
## Namespaces and generalized projection notation
Almost always, definitions and theorems relating to a type should be placed in a namespace with the same name as the type. For example, operations and theorems about lists should be placed in the `List` namespace, and operations and theorems about `Std.Time.PlainDate` should be placed in the `Std.Time.PlainDate` namespace.
Declarations in the root namespace will be relatively rare. The most common type of declaration in the root namespace are declarations about data and properties exported by notation type classes, as long as they are not about a specific type implementing that type class. For example, we have
```lean
theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b := sorry
```
in the root namespace, but
```lean
theorem List.cons_beq_cons [BEq α] {a b : α} {l₁ l₂ : List α} :
(a :: l₁ == b :: l₂) = (a == b && l₁ == l₂) := rfl
```
belongs in the `List` namespace.
Subtleties arise when multiple namespaces are in play. Generally, place your theorem in the most specific namespace that appears in one of the hypotheses of the theorem. The following names are both correct according to this convention:
```lean
theorem List.Sublist.reverse : l₁ <+ l₂ l₁.reverse <+ l₂.reverse := sorry
theorem List.reverse_sublist : l₁.reverse <+ l₂.reverse l₁ <+ l₂ := sorry
```
Notice that the second theorem does not have a hypothesis of type `List.Sublist l` for some `l`, so the name `List.Sublist.reverse_iff` would be incorrect.
The advantage of placing results in a namespace like `List.Sublist` is that it enables generalized projection notation, i.e., given `h : l₁ <+ l₂`,
one can write `h.reverse` to obtain a proof of `l₁.reverse <+ l₂.reverse`. Thinking about which dot notations are convenient can act as a guideline
for deciding where to place a theorem, and is, on occasion, a good reason to duplicate a theorem into multiple namespaces.
### The `Std` namespace
New types that are added will usually be placed in the `Std` namespace and in the `Std/` source directory, unless there are good reasons to place
them elsewhere.
Inside the `Std` namespace, all internal declarations should be `private` or else have a name component that clearly marks them as internal, preferably
`Internal`.
## Naming convention for data
When defining data, i.e., a (possibly 0-ary) function whose codomain is not Sort u, but has type Type u for some u, it should be named in lowerCamelCase. Examples include `List.append` and `List.isPrefixOf`.
If your data is morally fully specified by its type, then use the naming procedure for theorems described below and convert the result to lower camel case.
If your function returns an `Option`, consider adding `?` as a suffix. If your function may panic, consider adding `!` as a suffix. In many cases, there will be multiple variants of a function; one returning an option, one that may panic and possibly one that takes a proof argument.
## Naming algorithm for theorems and some definitions
There is, in principle, a general algorithm for naming a theorem. The problem with this algorithm is that it produces very long and unwieldy names which need to be shortened. So choosing a name for a declaration can be thought of as consisting of a mechanical part and a creative part.
Usually the first part is to decide which namespace the result should live in, according to the guidelines described above.
Next, consider the type of your declaration as a tree. Inner nodes of this tree are function types or function applications. Leaves of the tree are 0-ary functions or bound variables.
As an example, consider the following result from the standard library:
```lean
example {α : Type u} {β : Type v} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
[Inhabited β] {m : Std.HashMap α β} {a : α} {h' : a m} : m[a]? = some (m[a]'h') :=
sorry
```
The correct namespace is clearly `Std.HashMap`. The corresponding tree looks like this:
![](naming-tree.svg)
The preferred spelling of a notation can be looked up by hovering over the notation.
Now traverse the tree and build a name according to the following rules:
* When encountering a function type, first turn the result type into a name, then all of the argument types from left to right, and join the names using `_of_`.
* When encountering a function that is neither an infix notation nor a structure projection, first put the function name and then the arguments, joined by an underscore.
* When encountering an infix notation, join the arguments using the name of the notation, separated by underscores.
* When encountering a structure projection, proceed as for normal functions, but put the name of the projection last.
* When encountering a name, put it in lower camel case.
* Skip bound variables and proofs.
* Type class arguments are also generally skipped.
When encountering namespaces names, concatenate them in lower camel case.
Applying this algorithm to our example yields the name `Std.HashMap.getElem?_eq_optionSome_getElem_of_mem`.
From there, the name should be shortened, using the following heuristics:
* The namespace of functions can be omitted if it is clear from context or if the namespace is the current one. This is almost always the case.
* For infix operators, it is possible to leave out the RHS or the name of the notation and the RHS if they are clear from context.
* Hypotheses can be left out if it is clear that they are required or if they appear in the conclusion.
Based on this, here are some possible names for our example:
1. `Std.HashMap.getElem?_eq`
2. `Std.HashMap.getElem?_eq_of_mem`
3. `Std.HashMap.getElem?_eq_some`
4. `Std.HashMap.getElem?_eq_some_of_mem`
5. `Std.HashMap.getElem?_eq_some_getElem`
6. `Std.Hashmap.getElem?_eq_some_getElem_of_mem`
Choosing a good name among these then requires considering the context of the lemma. In this case it turns out that the first four options are underspecified as there is also a lemma relating `m[a]?` and `m[a]!` which could have the same name. This leaves the last two options, the first of which is shorter, and this is how the lemma is called in the Lean standard library.
Here are some additional examples:
```lean
example {x y : List α} (h : x <+: y) (hx : x []) :
x.head hx = y.head (h.ne_nil hx) := sorry
```
Since we have an `IsPrefix` parameter, this should live in the `List.IsPrefix` namespace, and the algorithm suggests `List.IsPrefix.head_eq_head_of_ne_nil`, which is shortened to `List.IsPrefix.head`. Note here the difference between the namespace name (`IsPrefix`) and the recommended spelling of the corresponding notation (`prefix`).
```lean
example : l₁ <+: l₂ reverse l₁ <:+ reverse l₂ := sorry
```
Again, this result should be in the `List.IsPrefix` namespace; the algorithm suggests `List.IsPrefix.reverse_prefix_reverse`, which becomes `List.IsPrefix.reverse`.
The following examples show how the traversal order often matters.
```lean
theorem Nat.mul_zero (n : Nat) : n * 0 = 0 := sorry
theorem Nat.zero_mul (n : Nat) : 0 * n = 0 := sorry
```
Here we see that one name may be a prefix of another name:
```lean
theorem Int.mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 := sorry
theorem Int.mul_ne_zero_iff {a b : Int} : a * b 0 a 0 b 0 := sorry
```
It is usually a good idea to include the `iff` in a theorem name even if the name would still be unique without the name. For example,
```lean
theorem List.head?_eq_none_iff : l.head? = none l = [] := sorry
```
is a good name: if the lemma was simply called `List.head?_eq_none`, users might try to `apply` it when the goal is `l.head? = none`, leading
to confusion.
The more common you expect (or want) a theorem to be, the shorter you should try to make the name. For example, we have both
```lean
theorem Std.HashMap.getElem?_eq_none_of_contains_eq_false {a : α} : m.contains a = false m[a]? = none := sorry
theorem Std.HashMap.getElem?_eq_none {a : α} : ¬a m m[a]? = none := sorry
```
As users of the hash map are encouraged to use ∈ rather than contains, the second lemma gets the shorter name.
## Special cases
There are certain special “keywords” that may appear in identifiers.
| Keyword | Meaning | Example |
| :---- | :---- | :---- |
| `def` | Unfold a definition. Avoid this for public APIs. | `Nat.max_def` |
| `refl` | Theorems of the form `a R a`, where R is a reflexive relation and `a` is an explicit parameter | `Nat.le_refl` |
| `rfl` | Like `refl`, but with `a` implicit | `Nat.le_rfl` |
| `irrefl` | Theorems of the form `¬a R a`, where R is an irreflexive relation | `Nat.lt_irrefl` |
| `symm` | Theorems of the form `a R b → b R a`, where R is a symmetric relation (compare `comm` below) | `Eq.symm` |
| `trans` | Theorems of the form `a R b → b R c → a R c`, where R is a transitive relation (R may carry data) | `Eq.trans` |
| `antisymmm` | Theorems of the form `a R b → b R a → a = b`, where R is an antisymmetric relation | `Nat.le_antisymm` |
| `congr` | Theorems of the form `a R b → f a S f b`, where R and S are usually equivalence relations | `Std.HashMap.mem_congr` |
| `comm` | Theorems of the form `f a b = f b a` (compare `symm` above) | `Eq.comm`, `Nat.add_comm` |
| `assoc` | Theorems of the form `g (f a b) c = f a (g b c)` (note the order! In most cases, we have f = g) | `Nat.add_sub_assoc` |
| `distrib` | Theorems of the form `f (g a b) = g (f a) (f b)` | `Nat.add_left_distrib` |
| `self` | May be used if a variable appears multiple times in the conclusion | `List.mem_cons_self` |
| `inj` | Theorems of the form `f a = f b ↔ a = b`. | `Int.neg_inj`, `Nat.add_left_inj` |
| `cancel` | Theorems which have one of the forms `f a = f b → a = b` or `g (f a) = a`, where `f` and `g` usually involve a binary operator | `Nat.add_sub_cancel` |
| `cancel_iff` | Same as `inj`, but with different conventions for left and right (see below) | `Nat.add_right_cancel_iff` |
| `ext` | Theorems of the form `f a = f b → a = b`, where `f` usually involves some kind of projection | `List.ext_getElem`
| `mono` | Theorems of the form `a R b → f a R f b`, where `R` is a transitive relation | `List.countP_mono_left`
### Left and right
The keywords left and right are useful to disambiguate symmetric variants of theorems.
```lean
theorem imp_congr_left (h : a b) : (a c) (b c) := sorry
theorem imp_congr_right (h : a (b c)) : (a b) (a c) := sorry
```
It is not always obvious which version of a theorem should be “left” and which should be “right”.
Heuristically, the theorem should name the side which is “more variable”, but there are exceptions. For some of the special keywords discussed in this section, there are conventions which should be followed, as laid out in the following examples:
```lean
theorem Nat.left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := sorry
theorem Nat.right_distrib (n m k : Nat) : (n + m) * k = n * k + m * k := sorry
theorem Nat.add_left_cancel {n m k : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel {n m k : Nat} : n + m = k + m n = k := sorry
theorem Nat.add_left_cancel_iff {m k n : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel_iff {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_left_inj {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_right_inj {m k n : Nat} : n + m = n + k m = k := sorry
```
Note in particular that the convention is opposite for `cancel_iff` and `inj`.
```lean
theorem Nat.add_sub_self_left (a b : Nat) : (a + b) - a = b := sorry
theorem Nat.add_sub_self_right (a b : Nat) : (a + b) - b = a := sorry
theorem Nat.add_sub_cancel (n m : Nat) : (n + m) - m = n := sorry
```
## Primed names
Avoid disambiguating variants of a concept by appending the `'` character (e.g., introducing both `BitVec.sshiftRight` and `BitVec.sshiftRight'`), as it is impossible to tell the difference without looking at the type signature, the documentation or even the code, and even if you know what the two variants are there is no way to tell which is which. Prefer descriptive pairs `BitVec.sshiftRightNat`/`BitVec.sshiftRight`.
## Acronyms
For acronyms which are three letters or shorter, all letters should use the same case as dictated by the convention. For example, `IO` is a correct name for a type and the name `IO.Ref` may become `IORef` when used as part of a definition name and `ioRef` when used as part of a theorem name.
For acronyms which are at least four letters long, switch to lower case starting from the second letter. For example, `Json` is a correct name for a type, as is `JsonRPC`.
If an acronym is typically spelled using mixed case, this mixed spelling may be used in identifiers (for example `Std.Net.IPv4Addr`).
## Simp sets
Simp sets centered around a conversion function should be called `source_to_target`. For example, a simp set for the `BitVec.toNat` function, which goes from `BitVec` to
`Nat`, should be called `bitvec_to_nat`.

View File

@@ -1,3 +1,5 @@
# Standard library style
Please take some time to familiarize yourself with the stylistic conventions of
the project and the specific part of the library you are planning to contribute
to. While the Lean compiler may not enforce strict formatting rules,
@@ -6,5 +8,473 @@ Attention to formatting is more than a cosmetic concern—it reflects the same
level of precision and care required to meet the deeper standards of the Lean 4
standard library.
A full style guide and naming convention are currently under construction and
will be added here soon.
Below we will give specific formatting prescriptions for various language constructs. Note that this style guide only applies to the Lean standard library, even though some examples in the guide are taken from other parts of the Lean code base.
## Basic whitespace rules
Syntactic elements (like `:`, `:=`, `|`, `::`) are surrounded by single spaces, with the exception of `,` and `;`, which are followed by a space but not preceded by one. Delimiters (like `()`, `{}`) do not have spaces on the inside, with the exceptions of subtype notation and structure instance notation.
Examples of correctly formatted function parameters:
* `{α : Type u}`
* `[BEq α]`
* `(cmp : αα → Ordering)`
* `(hab : a = b)`
* `{d : { l : List ((n : Nat) × Vector Nat n) // l.length % 2 = 0 }}`
Examples of correctly formatted terms:
* `1 :: [2, 3]`
* `letI : Ord α := ⟨cmp⟩; True`
* `(⟨2, 3⟩ : Nat × Nat)`
* `((2, 3) : Nat × Nat)`
* `{ x with fst := f (4 + f 0), snd := 4, .. }`
* `match 1 with | 0 => 0 | _ => 0`
* `fun ⟨a, b⟩ _ _ => by cases hab <;> apply id; rw [hbc]`
Configure your editor to remove trailing whitespace. If you have set up Visual Studio Code for Lean development in the recommended way then the correct setting is applied automatically.
## Splitting terms across multiple lines
When splitting a term across multiple lines, increase indentation by two spaces starting from the second line. When splitting a function application, try to split at argument boundaries. If an argument itself needs to be split, increase indentation further as appropriate.
When splitting at an infix operator, the operator goes at the end of the first line, not at the beginning of the second line. When splitting at an infix operator, you may or may not increase indentation depth, depending on what is more readable.
When splitting an `if`-`then`-`else` expression, the `then` keyword wants to stay with the condition and the `else` keyword wants to stay with the alternative term. Otherwise, indent as if the `if` and `else` keywords were arguments to the same function.
When splitting a comma-separated bracketed sequence (i.e., anonymous constructor application, list/array/vector literal, tuple) it is allowed to indent subsequent lines for alignment, but indenting by two spaces is also allowed.
Do not orphan parentheses.
Correct:
```lean
def MacroScopesView.isPrefixOf (v₁ v₂ : MacroScopesView) : Bool :=
v₁.name.isPrefixOf v₂.name &&
v₁.scopes == v₂.scopes &&
v₁.mainModule == v₂.mainModule &&
v₁.imported == v₂.imported
```
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
example : Nat :=
functionWithAVeryLongNameSoThatSomeArgumentsWillNotFit firstArgument secondArgument
(firstArgumentWithAnEquallyLongNameAndThatFunctionDoesHaveMoreArguments firstArgument
secondArgument)
secondArgument
```
Correct:
```lean
theorem size_alter [LawfulBEq α] {k : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).size =
if m.contains k && (f (m.get? k)).isNone then
m.size - 1
else if !m.contains k && (f (m.get? k)).isSome then
m.size + 1
else
m.size := by
simp_to_raw using Raw₀.size_alter
```
Correct:
```lean
theorem get?_alter [LawfulBEq α] {k k' : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).get? k' =
if h : k == k' then
cast (congrArg (Option β) (eq_of_beq h)) (f (m.get? k))
else m.get? k' := by
simp_to_raw using Raw₀.get?_alter
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Vector Nat :=
#v[imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm]
```
## Basic file structure
Every file should start with a copyright header, imports (in the standard library, this always includes a `prelude` declaration) and a module documentation string. There should not be a blank line between the copyright header and the imports. There should be a blank line between the imports and the module documentation string.
Correct:
```lean
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
Yury Kudryashov
-/
prelude
import Init.Data.List.Pairwise
import Init.Data.List.Find
/-!
**# Lemmas about `List.eraseP` and `List.erase`.**
-/
```
Syntax that is not supposed to be user-facing must be scoped. New public syntax must always be discussed explicitly in an RFC.
## Top-level commands and declarations
All top-level commands are unindented. Sectioning commands like `section` and `namespace` do not increase the indentation level.
Attributes may be placed on the same line as the rest of the command or on a separate line.
Multi-line declaration headers are indented by four spaces starting from the second line. The colon that indicates the type of a declaration may not be placed at the start of a line or on its own line.
Declaration bodies are indented by two spaces. Short declaration bodies may be placed on the same line as the declaration type.
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
```
Correct:
```lean
@[simp]
theorem eraseP_nil : [].eraseP p = [] := rfl
```
### Documentation comments
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
Declarations should be documented as required by the `docBlame` linter, which may be activated in a file using
`set_option linter.missingDocs true` (we allow these to stay in the file).
Single-line documentation comments should go on the same line as `/--`/`-/`, while multi-line documentation strings
should have these delimiters on their own line, with the documentation comment itself unindented.
Documentation comments must be written in the indicative mood. Use American orthography.
Correct:
```lean
/-- Carries out a monadic action on each mapping in the hash map in some order. -/
@[inline] def forM (f : (a : α) β a m PUnit) (b : Raw α β) : m PUnit :=
b.buckets.forM (AssocList.forM f)
```
Correct:
```lean
/--
Monadically computes a value by folding the given function over the mappings in the hash
map in some order.
-/
@[inline] def foldM (f : δ (a : α) β a m δ) (init : δ) (b : Raw α β) : m δ :=
b.buckets.foldlM (fun acc l => l.foldlM f acc) init
```
### Where clauses
The `where` keyword should be unindented, and all declarations bound by it should be indented with two spaces.
Blank lines before and after `where` and between declarations bound by `where` are optional and should be chosen
to maximize readability.
Correct:
```lean
@[simp] theorem partition_eq_filter_filter (p : α Bool) (l : List α) :
partition p l = (filter p l, filter (not p) l) := by
simp [partition, aux]
where
aux (l) {as bs} : partition.loop p l (as, bs) =
(as.reverse ++ filter p l, bs.reverse ++ filter (not p) l) :=
match l with
| [] => by simp [partition.loop, filter]
| a :: l => by cases pa : p a <;> simp [partition.loop, pa, aux, filter, append_assoc]
```
### Termination arguments
The `termination_by`, `decreasing_by`, `partial_fixpoint` keywords should be unindented. The associated terms should be indented like declaration bodies.
Correct:
```lean
@[inline] def multiShortOption (handle : Char m PUnit) (opt : String) : m PUnit := do
let rec loop (p : String.Pos) := do
if h : opt.atEnd p then
return
else
handle (opt.get' p h)
loop (opt.next' p h)
termination_by opt.utf8ByteSize - p.byteIdx
decreasing_by
simp [String.atEnd] at h
apply Nat.sub_lt_sub_left h
simp [String.lt_next opt p]
loop 1
```
Correct:
```lean
def substrEq (s1 : String) (off1 : String.Pos) (s2 : String) (off2 : String.Pos) (sz : Nat) : Bool :=
off1.byteIdx + sz s1.endPos.byteIdx && off2.byteIdx + sz s2.endPos.byteIdx && loop off1 off2 { byteIdx := off1.byteIdx + sz }
where
loop (off1 off2 stop1 : Pos) :=
if _h : off1.byteIdx < stop1.byteIdx then
let c₁ := s1.get off1
let c₂ := s2.get off2
c₁ == c₂ && loop (off1 + c₁) (off2 + c₂) stop1
else true
termination_by stop1.1 - off1.1
decreasing_by
have := Nat.sub_lt_sub_left _h (Nat.add_lt_add_left c₁.utf8Size_pos off1.1)
decreasing_tactic
```
Correct:
```lean
theorem div_add_mod (m n : Nat) : n * (m / n) + m % n = m := by
rw [div_eq, mod_eq]
have h : Decidable (0 < n n m) := inferInstance
cases h with
| isFalse h => simp [h]
| isTrue h =>
simp [h]
have ih := div_add_mod (m - n) n
rw [Nat.left_distrib, Nat.mul_one, Nat.add_assoc, Nat.add_left_comm, ih, Nat.add_comm, Nat.sub_add_cancel h.2]
decreasing_by apply div_rec_lemma; assumption
```
### Deriving
The `deriving` clause should be unindented.
Correct:
```lean
structure Iterator where
array : ByteArray
idx : Nat
deriving Inhabited
```
## Language constructs
### Pattern matching, induction etc.
Match arms are indented at the indentation level that the match statement would have if it was on its own line. If the match is implicit, then the arms should be indented as if the match was explicitly given. The content of match arms is indented two spaces, so that it appears on the same level as the match pattern.
Correct:
```lean
def alter [BEq α] {β : Type v} (a : α) (f : Option β Option β) :
AssocList α (fun _ => β) AssocList α (fun _ => β)
| nil => match f none with
| none => nil
| some b => AssocList.cons a b nil
| cons k v l =>
if k == a then
match f v with
| none => l
| some b => cons a b l
else
cons k v (alter a f l)
```
Correct:
```lean
theorem eq_append_cons_of_mem {a : α} {xs : List α} (h : a xs) :
as bs, xs = as ++ a :: bs a as := by
induction xs with
| nil => cases h
| cons x xs ih =>
simp at h
cases h with
| inl h => exact [], xs, by simp_all
| inr h =>
by_cases h' : a = x
· subst h'
exact [], xs, by simp
· obtain as, bs, rfl, h := ih h
exact x :: as, bs, rfl, by simp_all
```
Aligning match arms is allowed, but not required.
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
### Structures
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
When using structure instance syntax over multiple lines, the opening brace should go on the preceding line, while the closing brace should go on its own line. The rest of the syntax should be indented by one level. During structure updates, the `with` clause goes on the same line as the opening brace. Aligning at the assignment symbol is allowed but not required.
Correct:
```lean
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
IO AddConstAsyncResult := do
let sigPromise IO.Promise.new
let infoPromise IO.Promise.new
let extensionsPromise IO.Promise.new
let checkedEnvPromise IO.Promise.new
let asyncConst := {
constInfo := {
name := constName
kind
sig := sigPromise.result
constInfo := infoPromise.result
}
exts? := guard reportExts *> some extensionsPromise.result
}
return {
constName, kind
mainEnv := { env with
asyncConsts := env.asyncConsts.add asyncConst
checked := checkedEnvPromise.result }
asyncEnv := { env with
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
}
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
}
```
Correct:
```lean
instance [Inhabited α] : Inhabited (Descr α β σ) where
default := {
name := default
mkInitial := default
ofOLeanEntry := default
toOLeanEntry := default
addEntry := fun s _ => s
}
```
## Tactic proofs
Tactic proofs are the most common thing to break during any kind of upgrade, so it is important to write them in a way that minimizes the likelihood of proofs breaking and that makes it easy to debug breakages if they do occur.
If there are multiple goals, either use a tactic combinator (like `all_goals`) to operate on all of them or a clearly specified subset, or use focus dots to work on goals one at a time. Using structured proofs (e.g., `induction … with`) is encouraged but not mandatory.
Squeeze non-terminal `simp`s (i.e., calls to `simp` which do not close the goal). Squeezing terminal `simp`s is generally discouraged, although there are exceptions (for example if squeezing yields a noticeable performance improvement).
Do not over-golf proofs in ways that are likely to lead to hard-to-debug breakage. Examples of things to avoid include complex multi-goal manipulation using lots of tactic combinators, complex uses of the substitution operator (`▸`) and clever point-free expressions (possibly involving anonymous function notation for multiple arguments).
Do not under-golf proofs: for routine tasks, use the most powerful tactics available.
Do not use `erw`. Avoid using `rfl` after `simp` or `rw`, as this usually indicates a missing lemma that should be used instead of `rfl`.
Use `(d)simp` or `rw` instead of `delta` or `unfold`. Use `refine` instead of `refine`. Use `haveI` and `letI` only if they are actually required.
Prefer highly automated tactics (like `grind` and `omega`) over low-level proofs, unless the automated tactic requires unacceptable additional imports or has bad performance. If you decide against using a highly automated tactic, leave a comment explaining the decision.
## `do` notation
The `do` keyword goes on the same line as the corresponding `:=` (or `=>`, or similar). `Id.run do` should be treated as if it was a bare `do`.
Use early `return` statements to reduce nesting depth and make the non-exceptional control flow of a function easier to see.
Alternatives for `let` matches may be placed in the same line or in the next line, indented by two spaces. If the term that is
being matched on is itself more than one line and there is an alternative present, consider breaking immediately after `←` and indent
as far as necessary to ensure readability.
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl? fvarId | throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl
findFunDecl? fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl?
fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def tagUntaggedGoals (parentTag : Name) (newSuffix : Name) (newGoals : List MVarId) : TacticM Unit := do
let mctx getMCtx
let mut numAnonymous := 0
for g in newGoals do
if mctx.isAnonymousMVar g then
numAnonymous := numAnonymous + 1
modifyMCtx fun mctx => Id.run do
let mut mctx := mctx
let mut idx := 1
for g in newGoals do
if mctx.isAnonymousMVar g then
if numAnonymous == 1 then
mctx := mctx.setMVarUserName g parentTag
else
mctx := mctx.setMVarUserName g (parentTag ++ newSuffix.appendIndexAfter idx)
idx := idx + 1
pure mctx
```

View File

@@ -13,16 +13,17 @@ as part of verified applications.
The standard library is a public API that contains the components listed in the
standard library outline below. Not all public APIs in the Lean distribution
are part of the standard library, and the standard library does not correspond
to a certain directory within the Lean source repository. For example, the
metaprogramming framework is not part of the standard library.
to a certain directory within the Lean source repository (like `Std`). For
example, the metaprogramming framework is not part of the standard library, but
basic types like `True` and `Nat` are.
The standard library is under active development. Our guiding principles are:
* Provide comprehensive, verified building blocks for real-world software.
* Build a public API of the highest quality with excellent internal consistency.
* Carefully optimize components that may be used in performance-critical software.
* Ensure smooth adoption and maintenance for users.
* Offer excellent documentation, example projects, and guides.
* Provide comprehensive, verified building blocks for real-world software.
* Build a public API of the highest quality with excellent internal consistency.
* Carefully optimize components that may be used in performance-critical software.
* Ensure smooth adoption and maintenance for users.
* Offer excellent documentation, example projects, and guides.
* Provide a reliable and extensible basis that libraries for software
development, software verification and mathematics can build on.
@@ -32,23 +33,23 @@ call for contributions below.
### Standard library outline
1. Core types and operations
1. Basic types
2. Numeric types, including floating point numbers
3. Containers
4. Strings and formatting
2. Language constructs
1. Ranges and iterators
2. Comparison, ordering, hashing and related type classes
3. Basic monad infrastructure
3. Libraries
1. Random numbers
2. Dates and times
4. Operating system abstractions
1. Concurrency and parallelism primitives
2. Asynchronous I/O
3. FFI helpers
4. Environment, file system, processes
1. Core types and operations
1. Basic types
2. Numeric types, including floating point numbers
3. Containers
4. Strings and formatting
2. Language constructs
1. Ranges and iterators
2. Comparison, ordering, hashing and related type classes
3. Basic monad infrastructure
3. Libraries
1. Random numbers
2. Dates and times
4. Operating system abstractions
1. Concurrency and parallelism primitives
2. Asynchronous I/O
3. FFI helpers
4. Environment, file system, processes
5. Locales
The material covered in the first three sections (core types and operations,

20
flake.lock generated
View File

@@ -67,12 +67,30 @@
"type": "github"
}
},
"nixpkgs-older": {
"flake": false,
"locked": {
"lastModified": 1523316493,
"narHash": "sha256-5qJS+i5ECICPAKA6FhPLIWkhPKDnOZsZbh2PHYF1Kbs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"nixpkgs-cadical": "nixpkgs-cadical",
"nixpkgs-old": "nixpkgs-old"
"nixpkgs-old": "nixpkgs-old",
"nixpkgs-older": "nixpkgs-older"
}
},
"systems": {

View File

@@ -5,17 +5,20 @@
# old nixpkgs used for portable release with older glibc (2.27)
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
inputs.nixpkgs-old.flake = false;
# old nixpkgs used for portable release with older glibc (2.26)
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
inputs.nixpkgs-older.flake = false;
# for cadical 1.9.5; sync with CMakeLists.txt
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
pkgs = import inputs.nixpkgs { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old = import nixpkgs-old { inherit system; };
pkgsDist-old = import inputs.nixpkgs-older { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
pkgsDist-old-aarch = import inputs.nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
cadical = if pkgs.stdenv.isLinux then
# use statically-linked cadical on Linux to avoid glibc versioning troubles

View File

@@ -39,3 +39,4 @@ import Init.While
import Init.Syntax
import Init.Internal
import Init.Try
import Init.BinderNameHint

View File

@@ -0,0 +1,42 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
prelude
import Init.Prelude
import Init.Tactics
set_option linter.unusedVariables false in
/--
The expression `binderNameHint v binder e` defined to be `e`.
If it is used on the right-hand side of an equation that is used for rewriting by `rw` or `simp`,
and `v` is a local variable, and `binder` is an expression that (after beta-reduction) is a binder
(`fun w => …` or `∀ w, …`), then it will rename `v` to the name used in that binder, and remove
the `binderNameHint`.
A typical use of this gadget would be as follows; the gadget ensures that after rewriting, the local
variable is still `name`, and not `x`:
```
theorem all_eq_not_any_not (l : List α) (p : α → Bool) :
l.all p = !l.any fun x => binderNameHint x p (!p x) := sorry
example (names : List String) : names.all (fun name => "Waldo".isPrefixOf name) = true := by
rw [all_eq_not_any_not]
-- ⊢ (!names.any fun name => !"Waldo".isPrefixOf name) = true
```
If `binder` is not a binder, then the name of `v` attains a macro scope. This only matters when the
resulting term is used in a non-hygienic way, e.g. in termination proofs for well-founded recursion.
This gadget is supported by
* `simp`, `dsimp` and `rw` in the right-hand-side of an equation
* `simp` in the assumptions of congruence rules
It is ineffective in other positions (hyptheses of rewrite rules) or when used by other tactics
(e.g. `apply`).
-/
@[simp ]
def binderNameHint {α : Sort u} {β : Sort v} {γ : Sort w} (v : α) (binder : β) (e : γ) : γ := e

View File

@@ -38,7 +38,8 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
apply_dite f P (fun _ => x) (fun _ => y)
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
@[simp] theorem dite_eq_ite [Decidable P] : (dite P (fun _ => a) fun _ => b) = ite P a b := rfl
@[simp] theorem dite_eq_ite [Decidable P] :
(dite P (fun _ => a) (fun _ => b)) = ite P a b := rfl
@[deprecated "Use `ite_eq_right_iff`" (since := "2024-09-18")]
theorem ite_some_none_eq_none [Decidable P] :

View File

@@ -195,7 +195,7 @@ end Classical
/- Export for Mathlib compat. -/
export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
/-- Extract an element from a existential statement, using `Classical.choose`. -/
/-- Extract an element from an existential statement, using `Classical.choose`. -/
-- This enables projection notation.
@[reducible] noncomputable def Exists.choose {p : α Prop} (P : a, p a) : α := Classical.choose P

View File

@@ -5,6 +5,7 @@ Author: Leonardo de Moura, Sebastian Ullrich
-/
prelude
import Init.Core
import Init.BinderNameHint
universe u v w
@@ -35,6 +36,12 @@ 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]
(x : ρ) (b : β) (f : (a : α) β m (ForInStep β)) :
forIn x b f = forIn' x b (fun x h => binderNameHint x f <| binderNameHint h () <| f x) := by
simp [binderNameHint]
rfl -- very strange why `simp` did not close it
/-- Extract the value from a `ForInStep`, ignoring whether it is `done` or `yield`. -/
def ForInStep.value (x : ForInStep α) : α :=
match x with

View File

@@ -9,25 +9,49 @@ import Init.RCases
import Init.ByCases
-- Mapping by a function with a left inverse is injective.
theorem map_inj_of_left_inverse [Applicative m] [LawfulApplicative m] {f : α β}
(w : g : β α, x, g (f x) = x) {x y : m α}
(h : f <$> x = f <$> y) : x = y := by
rcases w with g, w
replace h := congrArg (g <$> ·) h
simpa [w] using h
theorem map_inj_of_left_inverse [Functor m] [LawfulFunctor m] {f : α β}
(w : g : β α, x, g (f x) = x) {x y : m α} :
f <$> x = f <$> y x = y := by
constructor
· intro h
rcases w with g, w
replace h := congrArg (g <$> ·) h
simpa [w] using h
· rintro rfl
rfl
-- Mapping by an injective function is injective, as long as the domain is nonempty.
theorem map_inj_of_inj [Applicative m] [LawfulApplicative m] [Nonempty α] {f : α β}
(w : x y, f x = f y x = y) {x y : m α}
(h : f <$> x = f <$> y) : x = y := by
apply map_inj_of_left_inverse ?_ h
let a := Nonempty α
refine ?_, ?_
· intro b
by_cases p : a, f a = b
· exact Exists.choose p
· exact a
· intro b
simp only [exists_apply_eq_apply, reduceDIte]
apply w
apply Exists.choose_spec (p := fun a => f a = f b)
@[simp] theorem map_inj_right_of_nonempty [Functor m] [LawfulFunctor m] [Nonempty α] {f : α β}
(w : {x y}, f x = f y x = y) {x y : m α} :
f <$> x = f <$> y x = y := by
constructor
· intro h
apply (map_inj_of_left_inverse ?_).mp h
let a := Nonempty α
refine ?_, ?_
· intro b
by_cases p : a, f a = b
· exact Exists.choose p
· exact a
· intro b
simp only [exists_apply_eq_apply, reduceDIte]
apply w
apply Exists.choose_spec (p := fun a => f a = f b)
· rintro rfl
rfl
@[simp] theorem map_inj_right [Monad m] [LawfulMonad m]
{f : α β} (h : {x y : α}, f x = f y x = y) {x y : m α} :
f <$> x = f <$> y x = y := by
by_cases hempty : Nonempty α
· exact map_inj_right_of_nonempty h
· constructor
· intro h'
have (z : m α) : z = (do let a z; let b pure (f a); x) := by
conv => lhs; rw [ bind_pure z]
congr; funext a
exact (hempty a).elim
rw [this x, this y]
rw [ bind_assoc, map_eq_pure_bind, h', map_eq_pure_bind, bind_assoc]
· intro h'
rw [h']

View File

@@ -593,7 +593,9 @@ set_option linter.unusedVariables.funArgs false in
be available and then calls `f` on the result.
`prio`, if provided, is the priority of the task.
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished.
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished and
otherwise on the thread that `x` finished on. `prio` is ignored in this case. This should only be
done when executing `f` is cheap and non-blocking.
-/
@[noinline, extern "lean_task_map"]
protected def map (f : α β) (x : Task α) (prio := Priority.default) (sync := false) : Task β :=
@@ -607,7 +609,9 @@ for the value of `x` to be available and then calls `f` on the result,
resulting in a new task which is then run for a result.
`prio`, if provided, is the priority of the task.
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished.
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished and
otherwise on the thread that `x` finished on. `prio` is ignored in this case. This should only be
done when executing `f` is cheap and non-blocking.
-/
@[noinline, extern "lean_task_bind"]
protected def bind (x : Task α) (f : α Task β) (prio := Priority.default) (sync := false) :

View File

@@ -39,7 +39,7 @@ class EvalInformation (α : Sort u) (β : Sort v) where
evalVar : α Nat β
def Context.var (ctx : Context α) (idx : Nat) : Variable ctx.op :=
ctx.vars.getD idx ctx.arbitrary, none
ctx.vars[idx]?.getD ctx.arbitrary, none
instance : ContextInformation (Context α) where
isNeutral ctx x := ctx.var x |>.neutral.isSome

View File

@@ -27,3 +27,4 @@ import Init.Data.Array.Range
import Init.Data.Array.Erase
import Init.Data.Array.Zip
import Init.Data.Array.InsertIdx
import Init.Data.Array.Extract

View File

@@ -70,7 +70,7 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
funext α β p f L h'
cases L
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith]
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith_eq_pmap]
apply List.pmap_congr_left
intro a m h₁ h₂
congr
@@ -318,7 +318,7 @@ See however `foldl_subtype` below.
theorem foldl_attach (l : Array α) (f : β α β) (b : β) :
l.attach.foldl (fun acc t => f acc t.1) b = l.foldl f b := by
rcases l with l
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.map_attach, size_toArray,
simp only [List.attach_toArray, List.attachWith_mem_toArray, size_toArray,
List.length_pmap, List.foldl_toArray', mem_toArray, List.foldl_subtype]
congr
ext
@@ -337,7 +337,7 @@ See however `foldr_subtype` below.
theorem foldr_attach (l : Array α) (f : α β β) (b : β) :
l.attach.foldr (fun t acc => f t.1 acc) b = l.foldr f b := by
rcases l with l
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.map_attach, size_toArray,
simp only [List.attach_toArray, List.attachWith_mem_toArray, size_toArray,
List.length_pmap, List.foldr_toArray', mem_toArray, List.foldr_subtype]
congr
ext
@@ -354,7 +354,12 @@ theorem attachWith_map {l : Array α} (f : α → β) {P : β → Prop} {H : ∀
cases l
simp [List.attachWith_map]
theorem map_attachWith {l : Array α} {P : α Prop} {H : (a : α), a l P a}
@[simp] theorem map_attachWith {l : Array α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f = l.attach.map fun x, h => f x, H _ h := by
cases l <;> simp_all
theorem map_attachWith_eq_pmap {l : Array α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f =
l.pmap (fun a (h : a l P a) => f a, H _ h.1) (fun a h => h, H a h) := by
@@ -362,11 +367,14 @@ theorem map_attachWith {l : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈
ext <;> simp
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
theorem map_attach {l : Array α} (f : { x // x l } β) :
theorem map_attach_eq_pmap {l : Array α} (f : { x // x l } β) :
l.attach.map f = l.pmap (fun a h => f a, h) (fun _ => id) := by
cases l
ext <;> simp
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
abbrev map_attach := @map_attach_eq_pmap
theorem attach_filterMap {l : Array α} {f : α Option β} :
(l.filterMap f).attach = l.attach.filterMap
fun x, h => (f x).pbind (fun b m => some b, mem_filterMap.mpr x, h, m) := by
@@ -505,7 +513,7 @@ theorem count_attach [DecidableEq α] (l : Array α) (a : {x // x ∈ l}) :
l.attach.count a = l.count a := by
rcases l with l
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.count_toArray]
rw [List.map_attach, List.count_eq_countP]
rw [List.map_attach_eq_pmap, List.count_eq_countP]
simp only [Subtype.beq_iff]
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
@@ -642,6 +650,16 @@ and simplifies these to the function directly taking the value.
rw [List.filterMap_subtype]
simp [hf]
@[simp] theorem flatMap_subtype {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } Array β} {g : α Array β} (hf : x h, f x, h = g x) :
(l.flatMap f) = l.unattach.flatMap g := by
cases l
simp only [size_toArray, List.flatMap_toArray, List.unattach_toArray, List.length_unattach,
mk.injEq]
rw [List.flatMap_subtype]
simp [hf]
@[simp] theorem findSome?_subtype {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } Option β} {g : α Option β} (hf : x h, f x, h = g x) :
l.findSome? f = l.unattach.findSome? g := by
@@ -687,4 +705,67 @@ and simplifies these to the function directly taking the value.
(Array.mkArray n x).unattach = Array.mkArray n x.1 := by
simp [unattach]
/-! ### Well-founded recursion preprocessing setup -/
@[wf_preprocess] theorem Array.map_wfParam (xs : Array α) (f : α β) :
(wfParam xs).map f = xs.attach.unattach.map f := by
simp [wfParam]
@[wf_preprocess] theorem Array.map_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α β) :
xs.unattach.map f = xs.map fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[wf_preprocess] theorem foldl_wfParam (xs : Array α) (f : β α β) (x : β) :
(wfParam xs).foldl f x = xs.attach.unattach.foldl f x := by
simp [wfParam]
@[wf_preprocess] theorem foldl_unattach (P : α Prop) (xs : Array (Subtype P)) (f : β α β) (x : β):
xs.unattach.foldl f x = xs.foldl (fun s x, h =>
binderNameHint s f <| binderNameHint x (f s) <| binderNameHint h () <| f s (wfParam x)) x := by
simp [wfParam]
@[wf_preprocess] theorem foldr_wfParam (xs : Array α) (f : α β β) (x : β) :
(wfParam xs).foldr f x = xs.attach.unattach.foldr f x := by
simp [wfParam]
@[wf_preprocess] theorem foldr_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α β β) (x : β):
xs.unattach.foldr f x = xs.foldr (fun x, h s =>
binderNameHint x f <| binderNameHint s (f x) <| binderNameHint h () <| f (wfParam x) s) x := by
simp [wfParam]
@[wf_preprocess] theorem filter_wfParam (xs : Array α) (f : α Bool) :
(wfParam xs).filter f = xs.attach.unattach.filter f:= by
simp [wfParam]
@[wf_preprocess] theorem filter_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α Bool) :
xs.unattach.filter f = (xs.filter (fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x))).unattach := by
simp [wfParam]
@[wf_preprocess] theorem reverse_wfParam (xs : Array α) :
(wfParam xs).reverse = xs.attach.unattach.reverse := by simp [wfParam]
@[wf_preprocess] theorem reverse_unattach (P : α Prop) (xs : Array (Subtype P)) :
xs.unattach.reverse = xs.reverse.unattach := by simp
@[wf_preprocess] theorem filterMap_wfParam (xs : Array α) (f : α Option β) :
(wfParam xs).filterMap f = xs.attach.unattach.filterMap f := by
simp [wfParam]
@[wf_preprocess] theorem filterMap_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α Option β) :
xs.unattach.filterMap f = xs.filterMap fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[wf_preprocess] theorem flatMap_wfParam (xs : Array α) (f : α Array β) :
(wfParam xs).flatMap f = xs.attach.unattach.flatMap f := by
simp [wfParam]
@[wf_preprocess] theorem flatMap_unattach (P : α Prop) (xs : Array (Subtype P)) (f : α Array β) :
xs.unattach.flatMap f = xs.flatMap fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
end Array

View File

@@ -19,7 +19,7 @@ universe u v w
/-! ### Array literal syntax -/
/-- Syntax for `Array α`. -/
syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term
syntax (name := «term#[_,]») "#[" withoutPosition(term,*,?) "]" : term
macro_rules
| `(#[ $elems,* ]) => `(List.toArray [ $elems,* ])
@@ -48,7 +48,7 @@ theorem ext (a b : Array α)
: a = b := by
let rec extAux (a b : List α)
(h₁ : a.length = b.length)
(h₂ : (i : Nat) (hi₁ : i < a.length) (hi₂ : i < b.length) a.get i, hi₁ = b.get i, hi₂)
(h₂ : (i : Nat) (hi₁ : i < a.length) (hi₂ : i < b.length) a[i] = b[i])
: a = b := by
induction a generalizing b with
| nil =>
@@ -63,11 +63,11 @@ theorem ext (a b : Array α)
have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
have headEq : a = b := h₂ 0 hz₁ hz₂
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁
have h₂' : (i : Nat) (hi₁ : i < as.length) (hi₂ : i < bs.length) as.get i, hi₁ = bs.get i, hi₂ := by
have h₂' : (i : Nat) (hi₁ : i < as.length) (hi₂ : i < bs.length) as[i] = bs[i] := by
intro i hi₁ hi₂
have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
have hi₂' : i+1 < (b::bs).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
have : (a::as).get i+1, hi₁' = (b::bs).get i+1, hi₂' := h₂ (i+1) hi₁' hi₂'
have : (a::as)[i+1] = (b::bs)[i+1] := h₂ (i+1) hi₁' hi₂'
apply this
have tailEq : as = bs := ih bs h₁' h₂'
rw [headEq, tailEq]
@@ -123,7 +123,8 @@ namespace List
@[simp] theorem getElem?_toArray {a : List α} {i : Nat} : a.toArray[i]? = a[i]? := rfl
@[simp] theorem getElem!_toArray [Inhabited α] {a : List α} {i : Nat} :
a.toArray[i]! = a[i]! := rfl
a.toArray[i]! = a[i]! := by
simp [getElem!_def]
end List
@@ -254,17 +255,37 @@ def range' (start size : Nat) (step : Nat := 1) : Array Nat :=
@[inline] protected def singleton (v : α) : Array α := #[v]
/--
Return the last element of an array, or panic if the array is empty.
See `back` for the version that requires a proof the array is non-empty,
or `back?` for the version that returns an option.
-/
def back! [Inhabited α] (a : Array α) : α :=
a[a.size - 1]!
@[deprecated back! (since := "2024-10-31")] abbrev back := @back!
/--
Return the last element of an array, given a proof that the array is not empty.
def get? (a : Array α) (i : Nat) : Option α :=
if h : i < a.size then some a[i] else none
See `back!` for the version that panics if the array is empty,
or `back?` for the version that returns an option.
-/
def back (a : Array α) (h : 0 < a.size := by get_elem_tactic) : α :=
a[a.size - 1]'(Nat.sub_one_lt_of_lt h)
/--
Return the last element of an array, or `none` if the array is empty.
See `back!` for the version that panics if the array is empty,
or `back` for the version that requires a proof the array is non-empty.
-/
def back? (a : Array α) : Option α :=
a[a.size - 1]?
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
def get? (a : Array α) (i : Nat) : Option α :=
if h : i < a.size then some a[i] else none
@[inline] def swapAt (a : Array α) (i : Nat) (v : α) (hi : i < a.size := by get_elem_tactic) : α × Array α :=
let e := a[i]
let a := a.set i v
@@ -881,6 +902,10 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
as
decreasing_by simp_wf; decreasing_trivial_pre_omega
@[simp] theorem popWhile_empty (p : α Bool) :
popWhile p #[] = #[] := by
simp [popWhile]
def takeWhile (p : α Bool) (as : Array α) : Array α :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
go (i : Nat) (r : Array α) : Array α :=

View File

@@ -17,13 +17,13 @@ theorem Array.of_push_eq_push {as bs : Array α} (h : as.push a = bs.push b) : a
private theorem List.size_toArrayAux (as : List α) (bs : Array α) : (as.toArrayAux bs).size = as.length + bs.size := by
induction as generalizing bs with
| nil => simp [toArrayAux]
| cons a as ih => simp_arith [toArrayAux, *]
| cons a as ih => simp +arith [toArrayAux, *]
private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Array α} (h : as.toArrayAux cs = bs.toArrayAux ds) (hlen : cs.size = ds.size) : as = bs cs = ds := by
match as, bs with
| [], [] => simp [toArrayAux] at h; simp [h]
| a::as, [] => simp [toArrayAux] at h; rw [ h] at hlen; simp_arith [size_toArrayAux] at hlen
| [], b::bs => simp [toArrayAux] at h; rw [h] at hlen; simp_arith [size_toArrayAux] at hlen
| a::as, [] => simp [toArrayAux] at h; rw [ h] at hlen; simp +arith [size_toArrayAux] at hlen
| [], b::bs => simp [toArrayAux] at h; rw [h] at hlen; simp +arith [size_toArrayAux] at hlen
| a::as, b::bs =>
simp [toArrayAux] at h
have : (cs.push a).size = (ds.push b).size := by simp [*]

View File

@@ -163,7 +163,7 @@ theorem count_le_size (a : α) (l : Array α) : count a l ≤ l.size := countP_l
theorem count_le_count_push (a b : α) (l : Array α) : count a l count a (l.push b) := by
simp [count_push]
@[simp] theorem count_singleton (a b : α) : count a #[b] = if b == a then 1 else 0 := by
theorem count_singleton (a b : α) : count a #[b] = if b == a then 1 else 0 := by
simp [count_eq_countP]
@[simp] theorem count_append (a : α) : l₁ l₂, count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=

View File

@@ -12,9 +12,9 @@ import Init.ByCases
namespace Array
private theorem rel_of_isEqvAux
{r : α α Bool} {a b : Array α} (hsz : a.size = b.size) {i : Nat} (hi : i a.size)
(heqv : Array.isEqvAux a b hsz r i hi)
{j : Nat} (hj : j < i) : r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz hi))) := by
{r : α α Bool} {xs ys : Array α} (hsz : xs.size = ys.size) {i : Nat} (hi : i xs.size)
(heqv : Array.isEqvAux xs ys hsz r i hi)
{j : Nat} (hj : j < i) : r (xs[j]'(Nat.lt_of_lt_of_le hj hi)) (ys[j]'(Nat.lt_of_lt_of_le hj (hsz hi))) := by
induction i with
| zero => contradiction
| succ i ih =>
@@ -27,8 +27,8 @@ private theorem rel_of_isEqvAux
subst hj'
exact heqv.left
private theorem isEqvAux_of_rel {r : α α Bool} {a b : Array α} (hsz : a.size = b.size) {i : Nat} (hi : i a.size)
(w : j, (hj : j < i) r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz hi)))) : Array.isEqvAux a b hsz r i hi := by
private theorem isEqvAux_of_rel {r : α α Bool} {xs ys : Array α} (hsz : xs.size = ys.size) {i : Nat} (hi : i xs.size)
(w : j, (hj : j < i) r (xs[j]'(Nat.lt_of_lt_of_le hj hi)) (ys[j]'(Nat.lt_of_lt_of_le hj (hsz hi)))) : Array.isEqvAux xs ys hsz r i hi := by
induction i with
| zero => simp [Array.isEqvAux]
| succ i ih =>
@@ -36,23 +36,23 @@ private theorem isEqvAux_of_rel {r : αα → Bool} {a b : Array α} (hsz :
exact w i (Nat.lt_add_one i), ih _ fun j hj => w j (Nat.lt_add_right 1 hj)
-- This is private as the forward direction of `isEqv_iff_rel` may be used.
private theorem rel_of_isEqv {r : α α Bool} {a b : Array α} :
Array.isEqv a b r h : a.size = b.size, (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h h')) := by
private theorem rel_of_isEqv {r : α α Bool} {xs ys : Array α} :
Array.isEqv xs ys r h : xs.size = ys.size, (i : Nat) (h' : i < xs.size), r (xs[i]) (ys[i]'(h h')) := by
simp only [isEqv]
split <;> rename_i h
· exact fun h' => h, fun i => rel_of_isEqvAux h (Nat.le_refl ..) h'
· intro; contradiction
theorem isEqv_iff_rel {a b : Array α} {r} :
Array.isEqv a b r h : a.size = b.size, (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h h')) :=
theorem isEqv_iff_rel {xs ys : Array α} {r} :
Array.isEqv xs ys r h : xs.size = ys.size, (i : Nat) (h' : i < xs.size), r (xs[i]) (ys[i]'(h h')) :=
rel_of_isEqv, fun h, w => by
simp only [isEqv, h, reduceDIte]
exact isEqvAux_of_rel h (by simp [h]) w
theorem isEqv_eq_decide (a b : Array α) (r) :
Array.isEqv a b r =
if h : a.size = b.size then decide ( (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h h'))) else false := by
by_cases h : Array.isEqv a b r
theorem isEqv_eq_decide (xs ys : Array α) (r) :
Array.isEqv xs ys r =
if h : xs.size = ys.size then decide ( (i : Nat) (h' : i < xs.size), r (xs[i]) (ys[i]'(h h'))) else false := by
by_cases h : Array.isEqv xs ys r
· simp only [h, Bool.true_eq]
simp only [isEqv_iff_rel] at h
obtain h, w := h
@@ -63,24 +63,24 @@ theorem isEqv_eq_decide (a b : Array α) (r) :
Bool.not_eq_true]
simpa [isEqv_iff_rel] using h'
@[simp] theorem isEqv_toList [BEq α] (a b : Array α) : (a.toList.isEqv b.toList r) = (a.isEqv b r) := by
@[simp] theorem isEqv_toList [BEq α] (xs ys : Array α) : (xs.toList.isEqv ys.toList r) = (xs.isEqv ys r) := by
simp [isEqv_eq_decide, List.isEqv_eq_decide]
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) (h : Array.isEqv a b (fun x y => x = y)) : a = b := by
theorem eq_of_isEqv [DecidableEq α] (xs ys : Array α) (h : Array.isEqv xs ys (fun x y => x = y)) : xs = ys := by
have h, h' := rel_of_isEqv h
exact ext _ _ h (fun i lt _ => by simpa using h' i lt)
private theorem isEqvAux_self (r : α α Bool) (hr : a, r a a) (a : Array α) (i : Nat) (h : i a.size) :
Array.isEqvAux a a rfl r i h = true := by
private theorem isEqvAux_self (r : α α Bool) (hr : a, r a a) (xs : Array α) (i : Nat) (h : i xs.size) :
Array.isEqvAux xs xs rfl r i h = true := by
induction i with
| zero => simp [Array.isEqvAux]
| succ i ih =>
simp_all only [isEqvAux, Bool.and_self]
theorem isEqv_self_beq [BEq α] [ReflBEq α] (a : Array α) : Array.isEqv a a (· == ·) = true := by
theorem isEqv_self_beq [BEq α] [ReflBEq α] (xs : Array α) : Array.isEqv xs xs (· == ·) = true := by
simp [isEqv, isEqvAux_self]
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (· = ·) = true := by
theorem isEqv_self [DecidableEq α] (xs : Array α) : Array.isEqv xs xs (· = ·) = true := by
simp [isEqv, isEqvAux_self]
instance [DecidableEq α] : DecidableEq (Array α) :=
@@ -89,22 +89,22 @@ instance [DecidableEq α] : DecidableEq (Array α) :=
| true => isTrue (eq_of_isEqv a b h)
| false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction
theorem beq_eq_decide [BEq α] (a b : Array α) :
(a == b) = if h : a.size = b.size then
decide ( (i : Nat) (h' : i < a.size), a[i] == b[i]'(h h')) else false := by
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
(xs == ys) = if h : xs.size = ys.size then
decide ( (i : Nat) (h' : i < xs.size), xs[i] == ys[i]'(h h')) else false := by
simp [BEq.beq, isEqv_eq_decide]
@[simp] theorem beq_toList [BEq α] (a b : Array α) : (a.toList == b.toList) = (a == b) := by
@[simp] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
simp [beq_eq_decide, List.beq_eq_decide]
end Array
namespace List
@[simp] theorem isEqv_toArray [BEq α] (a b : List α) : (a.toArray.isEqv b.toArray r) = (a.isEqv b r) := by
@[simp] theorem isEqv_toArray [BEq α] (as bs : List α) : (as.toArray.isEqv bs.toArray r) = (as.isEqv bs r) := by
simp [isEqv_eq_decide, Array.isEqv_eq_decide]
@[simp] theorem beq_toArray [BEq α] (a b : List α) : (a.toArray == b.toArray) = (a == b) := by
@[simp] theorem beq_toArray [BEq α] (as bs : List α) : (as.toArray == bs.toArray) = (as == bs) := by
simp [beq_eq_decide, Array.beq_eq_decide]
end List
@@ -114,7 +114,7 @@ namespace Array
instance [BEq α] [LawfulBEq α] : LawfulBEq (Array α) where
rfl := by simp [BEq.beq, isEqv_self_beq]
eq_of_beq := by
rintro a b h
rintro _ _ h
simpa using h
end Array

View File

@@ -0,0 +1,427 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop
/-!
# Lemmas about `Array.extract`
This file follows the contents of `Init.Data.List.TakeDrop` and `Init.Data.List.Nat.TakeDrop`.
-/
open Nat
namespace Array
/-! ### extract -/
@[simp] theorem extract_of_size_lt {as : Array α} {i j : Nat} (h : as.size < j) :
as.extract i j = as.extract i as.size := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract] at h₁ h₂
simp [h]
theorem size_extract_le {as : Array α} {i j : Nat} :
(as.extract i j).size j - i := by
simp
omega
theorem size_extract_le' {as : Array α} {i j : Nat} :
(as.extract i j).size as.size - i := by
simp
omega
theorem size_extract_of_le {as : Array α} {i j : Nat} (h : j as.size) :
(as.extract i j).size = j - i := by
simp
omega
@[simp]
theorem extract_push {as : Array α} {b : α} {start stop : Nat} (h : stop as.size) :
(as.push b).extract start stop = as.extract start stop := by
ext i h₁ h₂
· simp
omega
· simp only [size_extract, size_push] at h₁ h₂
simp only [getElem_extract, getElem_push]
rw [dif_pos (by omega)]
@[simp]
theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
as.extract 0 stop = as.pop := by
ext i h₁ h₂
· simp
omega
· simp only [size_extract, size_pop] at h₁ h₂
simp [getElem_extract, getElem_pop]
@[simp]
theorem extract_append_extract {as : Array α} {i j k : Nat} :
as.extract i j ++ as.extract j k = as.extract (min i j) (max j k) := by
ext l h₁ h₂
· simp
omega
· simp only [size_append, size_extract] at h₁ h₂
simp only [getElem_append, size_extract, getElem_extract]
split <;>
· congr 1
omega
@[simp]
theorem extract_eq_empty_iff {as : Array α} :
as.extract i j = #[] min j as.size i := by
constructor
· intro h
replace h := congrArg Array.size h
simp at h
omega
· intro h
exact eq_empty_of_size_eq_zero (by simp; omega)
theorem extract_eq_empty_of_le {as : Array α} (h : min j as.size i) :
as.extract i j = #[] :=
extract_eq_empty_iff.2 h
theorem lt_of_extract_ne_empty {as : Array α} (h : as.extract i j #[]) :
i < min j as.size :=
gt_of_not_le (mt extract_eq_empty_of_le h)
@[simp]
theorem extract_eq_self_iff {as : Array α} :
as.extract i j = as as.size = 0 i = 0 as.size j := by
constructor
· intro h
replace h := congrArg Array.size h
simp at h
omega
· intro h
ext l h₁ h₂
· simp
omega
· simp only [size_extract] at h₁
simp only [getElem_extract]
congr 1
omega
theorem extract_eq_self_of_le {as : Array α} (h : as.size j) :
as.extract 0 j = as :=
extract_eq_self_iff.2 (.inr rfl, h)
theorem le_of_extract_eq_self {as : Array α} (h : as.extract i j = as) :
as.size j := by
replace h := congrArg Array.size h
simp at h
omega
@[simp]
theorem extract_size_left {as : Array α} :
as.extract as.size j = #[] := by
simp
omega
@[simp]
theorem push_extract_getElem {as : Array α} {i j : Nat} (h : j < as.size) :
(as.extract i j).push as[j] = as.extract (min i j) (j + 1) := by
ext l h₁ h₂
· simp
omega
· simp only [size_push, size_extract] at h₁ h₂
simp only [getElem_push, size_extract, getElem_extract]
split <;>
· congr
omega
theorem extract_succ_right {as : Array α} {i j : Nat} (w : i < j + 1) (h : j < as.size) :
as.extract i (j + 1) = (as.extract i j).push as[j] := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, push_extract_getElem] at h₁ h₂
simp only [getElem_extract, push_extract_getElem]
congr
omega
theorem extract_sub_one {as : Array α} {i j : Nat} (h : j < as.size) :
as.extract i (j - 1) = (as.extract i j).pop := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, size_pop] at h₁ h₂
simp only [getElem_extract, getElem_pop]
@[simp]
theorem getElem?_extract_of_lt {as : Array α} {i j k : Nat} (h : k < min j as.size - i) :
(as.extract i j)[k]? = some (as[i + k]'(by omega)) := by
simp [getElem?_extract, h]
theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
(as.extract 0 (j + 1))[j]? = as[j]? := by
simp [getElem?_extract]
omega
@[simp] theorem extract_extract {as : Array α} {i j k l : Nat} :
(as.extract i j).extract k l = as.extract (i + k) (min (i + l) j) := by
ext m h₁ h₂
· simp
omega
· simp only [size_extract] at h₁ h₂
simp [Nat.add_assoc]
theorem extract_eq_empty_of_eq_empty {as : Array α} {i j : Nat} (h : as = #[]) :
as.extract i j = #[] := by
simp [h]
theorem ne_empty_of_extract_ne_empty {as : Array α} {i j : Nat} (h : as.extract i j #[]) :
as #[] :=
mt extract_eq_empty_of_eq_empty h
theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
(as.set k a).extract i j =
if _ : k < i then
as.extract i j
else if _ : k < min j as.size then
(as.extract i j).set (k - i) a (by simp; omega)
else as.extract i j := by
split
· ext l h₁ h₂
· simp
· simp at h₁ h₂
simp [getElem_set]
omega
· split
· ext l h₁ h₂
· simp
· simp only [getElem_extract, getElem_set]
split
· rw [if_pos]; omega
· rw [if_neg]; omega
· ext l h₁ h₂
· simp
· simp at h₁ h₂
simp [getElem_set]
omega
theorem set_extract {as : Array α} {i j k : Nat} (h : k < (as.extract i j).size) {a : α} :
(as.extract i j).set k a = (as.set (i + k) a (by simp at h; omega)).extract i j := by
ext l h₁ h₂
· simp
· simp_all [getElem_set]
@[simp]
theorem extract_append {as bs : Array α} {i j : Nat} :
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, size_append] at h₁ h₂
simp only [getElem_extract, getElem_append, size_extract]
split
· split
· rfl
· omega
· split
· omega
· congr 1
omega
theorem extract_append_left {as bs : Array α} :
(as ++ bs).extract 0 as.size = as.extract 0 as.size := by
simp
@[simp] theorem extract_append_right {as bs : Array α} :
(as ++ bs).extract as.size (as.size + i) = bs.extract 0 i := by
simp only [extract_append, extract_size_left, Nat.sub_self, empty_append]
congr 1
omega
@[simp] theorem map_extract {as : Array α} {i j : Nat} :
(as.extract i j).map f = (as.map f).extract i j := by
ext l h₁ h₂
· simp
· simp only [size_map, size_extract] at h₁ h₂
simp only [getElem_map, getElem_extract]
@[simp] theorem extract_mkArray {a : α} {n i j : Nat} :
(mkArray n a).extract i j = mkArray (min j n - i) a := by
ext l h₁ h₂
· simp
· simp only [size_extract, size_mkArray] at h₁ h₂
simp only [getElem_extract, getElem_mkArray]
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} :
as.extract i j = as.extract i j' min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
rcases as with as
simp
theorem extract_eq_extract_left {as : Array α} {i i' j : Nat} :
as.extract i j = as.extract i' j min j as.size - i = min j as.size - i' := by
constructor
· intro h
replace h := congrArg Array.size h
simpa using h
· intro h
ext l h₁ h₂
· simpa
· simp only [size_extract] at h₁ h₂
simp only [getElem_extract]
congr 1
omega
theorem extract_add_left {as : Array α} {i j k : Nat} :
as.extract (i + j) k = (as.extract i k).extract j (k - i) := by
simp [extract_eq_extract_right]
omega
theorem mem_extract_iff_getElem {as : Array α} {a : α} {i j : Nat} :
a as.extract i j (k : Nat) (hm : k < min j as.size - i), as[i + k] = a := by
rcases as with as
simp [List.mem_take_iff_getElem]
constructor <;>
· rintro k, h, rfl
exact k, by omega, rfl
theorem set_eq_push_extract_append_extract {as : Array α} {i : Nat} (h : i < as.size) {a : α} :
as.set i a = (as.extract 0 i).push a ++ (as.extract (i + 1) as.size) := by
rcases as with as
simp at h
simp [List.set_eq_take_append_cons_drop, h, List.take_of_length_le]
theorem extract_reverse {as : Array α} {i j : Nat} :
as.reverse.extract i j = (as.extract (as.size - j) (as.size - i)).reverse := by
ext l h₁ h₂
· simp
omega
· simp only [size_extract, size_reverse] at h₁ h₂
simp only [getElem_extract, getElem_reverse, size_extract]
congr 1
omega
theorem reverse_extract {as : Array α} {i j : Nat} :
(as.extract i j).reverse = as.reverse.extract (as.size - j) (as.size - i) := by
rw [extract_reverse]
simp
by_cases h : j as.size
· have : as.size - (as.size - j) = j := by omega
simp [this, extract_eq_extract_left]
omega
· have : as.size - (as.size - j) = as.size := by omega
simp only [Nat.not_le] at h
simp [h, this, extract_eq_extract_left]
omega
/-! ### takeWhile -/
theorem takeWhile_map (f : α β) (p : β Bool) (as : Array α) :
(as.map f).takeWhile p = (as.takeWhile (p f)).map f := by
rcases as with as
simp [List.takeWhile_map]
theorem popWhile_map (f : α β) (p : β Bool) (as : Array α) :
(as.map f).popWhile p = (as.popWhile (p f)).map f := by
rcases as with as
simp [List.dropWhile_map, List.map_reverse]
theorem takeWhile_filterMap (f : α Option β) (p : β Bool) (as : Array α) :
(as.filterMap f).takeWhile p = (as.takeWhile fun a => (f a).all p).filterMap f := by
rcases as with as
simp [List.takeWhile_filterMap]
theorem popWhile_filterMap (f : α Option β) (p : β Bool) (as : Array α) :
(as.filterMap f).popWhile p = (as.popWhile fun a => (f a).all p).filterMap f := by
rcases as with as
simp [List.dropWhile_filterMap, List.filterMap_reverse]
theorem takeWhile_filter (p q : α Bool) (as : Array α) :
(as.filter p).takeWhile q = (as.takeWhile fun a => !p a || q a).filter p := by
rcases as with as
simp [List.takeWhile_filter]
theorem popWhile_filter (p q : α Bool) (as : Array α) :
(as.filter p).popWhile q = (as.popWhile fun a => !p a || q a).filter p := by
rcases as with as
simp [List.dropWhile_filter, List.filter_reverse]
theorem takeWhile_append {xs ys : Array α} :
(xs ++ ys).takeWhile p =
if (xs.takeWhile p).size = xs.size then xs ++ ys.takeWhile p else xs.takeWhile p := by
rcases xs with xs
rcases ys with ys
simp only [List.append_toArray, List.takeWhile_toArray, List.takeWhile_append, size_toArray]
split <;> rfl
@[simp] theorem takeWhile_append_of_pos {p : α Bool} {l₁ l₂ : Array α} (h : a l₁, p a) :
(l₁ ++ l₂).takeWhile p = l₁ ++ l₂.takeWhile p := by
rcases l₁ with l₁
rcases l₂ with l₂
simp at h
simp [List.takeWhile_append_of_pos h]
theorem popWhile_append {xs ys : Array α} :
(xs ++ ys).popWhile p =
if (ys.popWhile p).isEmpty then xs.popWhile p else xs ++ ys.popWhile p := by
rcases xs with xs
rcases ys with ys
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, List.dropWhile_append,
List.isEmpty_eq_true, List.isEmpty_toArray, List.isEmpty_reverse]
-- Why do these not fire with `simp`?
rw [List.popWhile_toArray, List.isEmpty_toArray, List.isEmpty_reverse]
split
· rfl
· simp
@[simp] theorem popWhile_append_of_pos {p : α Bool} {l₁ l₂ : Array α} (h : a l₂, p a) :
(l₁ ++ l₂).popWhile p = l₁.popWhile p := by
rcases l₁ with l₁
rcases l₂ with l₂
simp at h
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, mk.injEq,
List.reverse_inj]
rw [List.dropWhile_append_of_pos]
simpa
@[simp] theorem takeWhile_mkArray_eq_filter (p : α Bool) :
(mkArray n a).takeWhile p = (mkArray n a).filter p := by
simp [ List.toArray_replicate]
theorem takeWhile_mkArray (p : α Bool) :
(mkArray n a).takeWhile p = if p a then mkArray n a else #[] := by
simp [takeWhile_mkArray_eq_filter, filter_mkArray]
@[simp] theorem popWhile_mkArray_eq_filter_not (p : α Bool) :
(mkArray n a).popWhile p = (mkArray n a).filter (fun a => !p a) := by
simp [ List.toArray_replicate, List.filter_reverse]
theorem popWhile_mkArray (p : α Bool) :
(mkArray n a).popWhile p = if p a then #[] else mkArray n a := by
simp only [popWhile_mkArray_eq_filter_not, size_mkArray, filter_mkArray, Bool.not_eq_eq_eq_not,
Bool.not_true]
split <;> simp_all
theorem extract_takeWhile {as : Array α} {i : Nat} :
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
rcases as with as
simp [List.take_takeWhile]
@[simp] theorem all_takeWhile {as : Array α} :
(as.takeWhile p).all p = true := by
rcases as with as
rw [List.takeWhile_toArray] -- Not sure why this doesn't fire with `simp`.
simp
@[simp] theorem any_popWhile {as : Array α} :
(as.popWhile p).any (fun a => !p a) = !as.all p := by
rcases as with as
rw [List.popWhile_toArray] -- Not sure why this doesn't fire with `simp`.
simp
theorem takeWhile_eq_extract_findIdx_not {xs : Array α} {p : α Bool} :
takeWhile p xs = xs.extract 0 (xs.findIdx (fun a => !p a)) := by
rcases xs with xs
simp [List.takeWhile_eq_take_findIdx_not]
end Array

View File

@@ -412,6 +412,21 @@ theorem findIdx_le_findIdx {l : Array α} {p q : α → Bool} (h : ∀ x ∈ l,
cases l
simp [hf]
theorem false_of_mem_extract_findIdx {xs : Array α} {p : α Bool} (h : x xs.extract 0 (xs.findIdx p)) :
p x = false := by
rcases xs with xs
exact List.false_of_mem_take_findIdx (by simpa using h)
@[simp] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α Bool} :
(xs.extract 0 i).findIdx p = min i (xs.findIdx p) := by
cases xs
simp
@[simp] theorem min_findIdx_findIdx {xs : Array α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
cases xs
simp
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := rfl
@@ -525,6 +540,11 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
cases l
simp [hf]
@[simp] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α Bool} :
(xs.take i).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun j => j < i)) := by
cases xs
simp
/-! ### idxOf
The verification API for `idxOf` is still incomplete.

View File

@@ -1350,8 +1350,9 @@ theorem map_filter_eq_foldl (f : α → β) (p : α → Bool) (l : Array α) :
simp only [List.filter_cons, List.foldr_cons]
split <;> simp_all
@[simp] theorem filter_append {p : α Bool} (l₁ l₂ : Array α) :
filter p (l₁ ++ l₂) = filter p l₁ ++ filter p l₂ := by
@[simp] theorem filter_append {p : α Bool} (l₁ l₂ : Array α) (w : stop = l₁.size + l₂.size) :
filter p (l₁ ++ l₂) 0 stop = filter p l₁ ++ filter p l₂ := by
subst w
rcases l₁ with l₁
rcases l₂ with l₂
simp [List.filter_append]
@@ -1440,12 +1441,18 @@ theorem _root_.List.filterMap_toArray (f : α → Option β) (l : List α) :
rcases l with l
simp [h]
@[simp] theorem filterMap_eq_map (f : α β) (w : stop = as.size ) :
@[simp] theorem filterMap_eq_map (f : α β) (w : stop = as.size) :
filterMap (some f) as 0 stop = map f as := by
subst w
cases as
simp
/-- Variant of `filterMap_eq_map` with `some ∘ f` expanded out to a lambda. -/
@[simp]
theorem filterMap_eq_map' (f : α β) (w : stop = as.size) :
filterMap (fun x => some (f x)) as 0 stop = map f as :=
filterMap_eq_map f w
theorem filterMap_some_fun : filterMap (some : α Option α) = id := by
funext l
cases l
@@ -1514,8 +1521,9 @@ theorem forall_mem_filterMap {f : α → Option β} {l : Array α} {P : β → P
intro a
rw [forall_comm]
@[simp] theorem filterMap_append {α β : Type _} (l l' : Array α) (f : α Option β) :
filterMap f (l ++ l') = filterMap f l ++ filterMap f l' := by
@[simp] theorem filterMap_append {α β : Type _} (l l' : Array α) (f : α Option β) (w : stop = l.size + l'.size) :
filterMap f (l ++ l') 0 stop = filterMap f l ++ filterMap f l' := by
subst w
cases l
cases l'
simp
@@ -1557,7 +1565,12 @@ theorem filterMap_eq_push_iff {f : α → Option β} {l : Array α} {l' : Array
@[simp] theorem size_append (as bs : Array α) : (as ++ bs).size = as.size + bs.size := by
simp only [size, toList_append, List.length_append]
@[simp] theorem append_push {as bs : Array α} {a : α} : as ++ bs.push a = (as ++ bs).push a := by
@[simp] theorem push_append {a : α} {xs ys : Array α} : (xs ++ ys).push a = xs ++ ys.push a := by
cases xs
cases ys
simp
theorem append_push {as bs : Array α} {a : α} : as ++ bs.push a = (as ++ bs).push a := by
cases as
cases bs
simp
@@ -1662,12 +1675,15 @@ theorem getElem_append_right' (l₁ : Array α) {l₂ : Array α} {i : Nat} (hi
rw [getElem_append_right] <;> simp [*, Nat.le_add_left]
theorem getElem_of_append {l l₁ l₂ : Array α} (eq : l = l₁.push a ++ l₂) (h : l₁.size = i) :
l[i]'(eq h by simp_arith) = a := Option.some.inj <| by
l[i]'(eq h by simp +arith) = a := Option.some.inj <| by
rw [ getElem?_eq_getElem, eq, getElem?_append_left (by simp; omega), h]
simp
@[simp] theorem append_singleton {a : α} {as : Array α} : as ++ #[a] = as.push a := rfl
@[simp] theorem append_singleton_assoc {a : α} {as bs : Array α} : as ++ (#[a] ++ bs) = as.push a ++ bs := by
rw [ append_assoc, append_singleton]
theorem push_eq_append {a : α} {as : Array α} : as.push a = as ++ #[a] := rfl
theorem append_inj {s₁ s₂ t₁ t₂ : Array α} (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : s₁.size = s₂.size) :
@@ -1878,7 +1894,8 @@ theorem append_eq_map_iff {f : α → β} :
rw [ flatten_map_toArray]
simp
theorem flatten_toArray (l : List (Array α)) :
-- We set this to lower priority so that `flatten_toArray_map` is applied first when relevant.
@[simp 500] theorem flatten_toArray (l : List (Array α)) :
l.toArray.flatten = (l.map Array.toList).flatten.toArray := by
apply ext'
simp
@@ -1930,15 +1947,17 @@ theorem flatten_eq_flatMap {L : Array (Array α)} : flatten L = L.flatMap id :=
Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
@[simp] theorem filterMap_flatten (f : α Option β) (L : Array (Array α)) :
filterMap f (flatten L) = flatten (map (filterMap f) L) := by
@[simp] theorem filterMap_flatten (f : α Option β) (L : Array (Array α)) (w : stop = L.flatten.size) :
filterMap f (flatten L) 0 stop = flatten (map (filterMap f) L) := by
subst w
induction L using array₂_induction
simp only [flatten_toArray_map, size_toArray, List.length_flatten, List.filterMap_toArray',
List.filterMap_flatten, List.map_toArray, List.map_map, Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
@[simp] theorem filter_flatten (p : α Bool) (L : Array (Array α)) :
filter p (flatten L) = flatten (map (filter p) L) := by
@[simp] theorem filter_flatten (p : α Bool) (L : Array (Array α)) (w : stop = L.flatten.size) :
filter p (flatten L) 0 stop = flatten (map (filter p) L) := by
subst w
induction L using array₂_induction
simp only [flatten_toArray_map, size_toArray, List.length_flatten, List.filter_toArray',
List.filter_flatten, List.map_toArray, List.map_map, Function.comp_def]
@@ -2140,7 +2159,8 @@ theorem flatMap_eq_foldl (f : α → Array β) (l : Array α) :
| nil => simp
| cons a l ih =>
intro l'
simp [ih ((l' ++ (f a).toList)), toArray_append]
rw [List.foldl_cons, ih]
simp [toArray_append]
/-! ### mkArray -/
@@ -2317,10 +2337,10 @@ theorem getElem?_swap (a : Array α) (i j : Nat) (hi hj) (k : Nat) : (a.swap i j
getElem?_toList]
split <;> rename_i h₂
· simp only [ h₂, Nat.not_le.2 (Nat.lt_succ_self _), Nat.le_refl, and_false]
exact (List.getElem?_reverse' (j+1) i (Eq.trans (by simp_arith) h)).symm
exact (List.getElem?_reverse' (j+1) i (Eq.trans (by simp +arith) h)).symm
split <;> rename_i h₃
· simp only [ h₃, Nat.not_le.2 (Nat.lt_succ_self _), Nat.le_refl, false_and]
exact (List.getElem?_reverse' i (j+1) (Eq.trans (by simp_arith) h)).symm
exact (List.getElem?_reverse' i (j+1) (Eq.trans (by simp +arith) h)).symm
simp only [Nat.succ_le, Nat.lt_iff_le_and_ne.trans (and_iff_left h₃),
Nat.lt_succ.symm.trans (Nat.lt_iff_le_and_ne.trans (and_iff_left (Ne.symm h₂)))]
· rw [H]; split <;> rename_i h₂
@@ -2365,6 +2385,10 @@ theorem getElem?_swap (a : Array α) (i j : Nat) (hi hj) (k : Nat) : (a.swap i j
theorem reverse_ne_empty_iff {xs : Array α} : xs.reverse #[] xs #[] :=
not_congr reverse_eq_empty_iff
@[simp] theorem isEmpty_reverse {xs : Array α} : xs.reverse.isEmpty = xs.isEmpty := by
cases xs
simp
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
theorem getElem?_reverse' {l : Array α} (i j) (h : i + j + 1 = l.size) : l.reverse[i]? = l[j]? := by
rcases l with l
@@ -2395,11 +2419,25 @@ theorem reverse_eq_iff {as bs : Array α} : as.reverse = bs ↔ as = bs.reverse
@[simp] theorem map_reverse (f : α β) (l : Array α) : l.reverse.map f = (l.map f).reverse := by
cases l <;> simp [*]
@[simp] theorem filter_reverse (p : α Bool) (l : Array α) : (l.reverse.filter p) = (l.filter p).reverse := by
/-- Variant of `filter_reverse` with a hypothesis giving the stop condition. -/
@[simp] theorem filter_reverse' (p : α Bool) (l : Array α) (w : stop = l.size) :
(l.reverse.filter p 0 stop) = (l.filter p).reverse := by
subst w
cases l
simp
@[simp] theorem filterMap_reverse (f : α Option β) (l : Array α) : (l.reverse.filterMap f) = (l.filterMap f).reverse := by
theorem filter_reverse (p : α Bool) (l : Array α) : (l.reverse.filter p) = (l.filter p).reverse := by
cases l
simp
/-- Variant of `filterMap_reverse` with a hypothesis giving the stop condition. -/
@[simp] theorem filterMap_reverse' (f : α Option β) (l : Array α) (w : stop = l.size) :
(l.reverse.filterMap f 0 stop) = (l.filterMap f).reverse := by
subst w
cases l
simp
theorem filterMap_reverse (f : α Option β) (l : Array α) : (l.reverse.filterMap f) = (l.filterMap f).reverse := by
cases l
simp
@@ -2875,17 +2913,57 @@ rather than `(arr.push a).size` as the argument.
(h : start = arr.size + 1) : (arr.push a).foldr f init start = arr.foldr f (f a init) :=
foldrM_push' _ _ _ _ h
@[simp] theorem foldl_push_eq_append (l l' : Array α) : l.foldl push l' = l' ++ l := by
cases l
cases l'
@[simp] theorem foldl_push_eq_append {as : Array α} {bs : Array β} {f : α β} (w : stop = as.size) :
as.foldl (fun b a => Array.push b (f a)) bs 0 stop = bs ++ as.map f := by
subst w
rcases as with as
rcases bs with bs
simp only [List.foldl_toArray']
induction as generalizing bs <;> simp [*]
@[simp] theorem foldl_cons_eq_append {as : Array α} {bs : List β} {f : α β} (w : stop = as.size) :
as.foldl (fun b a => (f a) :: b) bs 0 stop = (as.map f).reverse.toList ++ bs := by
subst w
rcases as with as
simp
@[simp] theorem foldr_flip_push_eq_append (l l' : Array α) :
l.foldr (fun x y => push y x) l' = l' ++ l.reverse := by
cases l
cases l'
@[simp] theorem foldr_cons_eq_append {as : Array α} {bs : List β} {f : α β} (w : start = as.size) :
as.foldr (fun a b => (f a) :: b) bs start 0 = (as.map f).toList ++ bs := by
subst w
rcases as with as
simp
/-- Variant of `foldr_cons_eq_append` specialized to `f = id`. -/
@[simp] theorem foldr_cons_eq_append' {as : Array α} {bs : List α} (w : start = as.size) :
as.foldr List.cons bs start 0 = as.toList ++ bs := by
subst w
rcases as with as
simp
@[simp] theorem foldr_append_eq_append (l : Array α) (f : α Array β) (l' : Array β) :
l.foldr (f · ++ ·) l' = (l.map f).flatten ++ l' := by
rcases l with l
rcases l' with l'
induction l <;> simp_all [Function.comp_def, flatten_toArray]
@[simp] theorem foldl_append_eq_append (l : Array α) (f : α Array β) (l' : Array β) :
l.foldl (· ++ f ·) l' = l' ++ (l.map f).flatten := by
rcases l with l
rcases l' with l'
induction l generalizing l'<;> simp_all [Function.comp_def, flatten_toArray]
@[simp] theorem foldr_flip_append_eq_append (l : Array α) (f : α Array β) (l' : Array β) :
l.foldr (fun x y => y ++ f x) l' = l' ++ (l.map f).reverse.flatten := by
rcases l with l
rcases l' with l'
induction l generalizing l' <;> simp_all [Function.comp_def, flatten_toArray]
@[simp] theorem foldl_flip_append_eq_append (l : Array α) (f : α Array β) (l' : Array β) :
l.foldl (fun x y => f y ++ x) l' = (l.map f).reverse.flatten ++ l':= by
rcases l with l
rcases l' with l'
induction l generalizing l' <;> simp_all [Function.comp_def, flatten_toArray]
theorem foldl_map' (f : β₁ β₂) (g : α β₂ α) (l : Array β₁) (init : α) (w : stop = l.size) :
(l.map f).foldl g init 0 stop = l.foldl (fun x y => g x (f y)) init := by
subst w
@@ -3038,6 +3116,13 @@ theorem foldl_eq_foldr_reverse (l : Array α) (f : β → α → β) (b) :
theorem foldr_eq_foldl_reverse (l : Array α) (f : α β β) (b) :
l.foldr f b = l.reverse.foldl (fun x y => f y x) b := by simp
@[simp] theorem foldr_push_eq_append {as : Array α} {bs : Array β} {f : α β} (w : start = as.size) :
as.foldr (fun a b => Array.push b (f a)) bs start 0 = bs ++ (as.map f).reverse := by
subst w
rw [foldr_eq_foldl_reverse, foldl_push_eq_append rfl, map_reverse]
@[deprecated foldr_push_eq_append (since := "2025-02-09")] abbrev foldr_flip_push_eq_append := @foldr_push_eq_append
theorem foldl_assoc {op : α α α} [ha : Std.Associative op] {l : Array α} {a₁ a₂} :
l.foldl op (op a₁ a₂) = op a₁ (l.foldl op a₂) := by
rcases l with l
@@ -3145,7 +3230,9 @@ theorem getElem?_lt
theorem getElem?_ge
(a : Array α) {i : Nat} (h : i a.size) : a[i]? = none := dif_neg (Nat.not_lt_of_le h)
@[simp] theorem get?_eq_getElem? (a : Array α) (i : Nat) : a.get? i = a[i]? := rfl
set_option linter.deprecated false in
@[deprecated "`get?` is deprecated" (since := "2025-02-12"), simp]
theorem get?_eq_getElem? (a : Array α) (i : Nat) : a.get? i = a[i]? := rfl
@[deprecated getElem?_eq_none (since := "2024-12-11")]
theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size i) : a[i]? = none := by
@@ -3153,15 +3240,26 @@ theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size ≤ i) : a[i]? = no
@[deprecated getD_getElem? (since := "2024-12-11")] abbrev getD_get? := @getD_getElem?
@[simp] theorem getD_eq_get? (a : Array α) (i d) : a.getD i d = (a[i]?).getD d := by
simp only [getD, get_eq_getElem, get?_eq_getElem?]; split <;> simp [getD_getElem?, *]
@[simp] theorem getD_eq_getD_getElem? (a : Array α) (i d) : a.getD i d = a[i]?.getD d := by
simp only [getD, get_eq_getElem]; split <;> simp [getD_getElem?, *]
@[deprecated getD_eq_getD_getElem? (since := "2025-02-12")] abbrev getD_eq_get? := @getD_eq_getD_getElem?
theorem getElem!_eq_getD [Inhabited α] (a : Array α) : a[i]! = a.getD i default := by
simp only [ get!_eq_getElem!]
rfl
@[deprecated getElem!_eq_getD (since := "2025-02-12")]
theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default := rfl
theorem get!_eq_getElem? [Inhabited α] (a : Array α) (i : Nat) :
a.get! i = (a.get? i).getD default := by
@[deprecated "Use `a[i]!` instead of `a.get! i`." (since := "2025-02-12")]
theorem get!_eq_getD_getElem? [Inhabited α] (a : Array α) (i : Nat) :
a.get! i = a[i]?.getD default := by
by_cases p : i < a.size <;>
simp only [get!_eq_getD, getD_eq_get?, getD_getElem?, p, get?_eq_getElem?]
simp only [get!_eq_getElem!, getElem!_eq_getD, getD_eq_getD_getElem?, getD_getElem?, p]
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_getElem? := @get!_eq_getD_getElem?
/-! # ofFn -/
@@ -3244,11 +3342,13 @@ theorem getElem?_size_le (a : Array α) (i : Nat) (h : a.size ≤ i) : a[i]? = n
theorem getElem_mem_toList (a : Array α) (h : i < a.size) : a[i] a.toList := by
simp only [ getElem_toList, List.getElem_mem]
set_option linter.deprecated false in
@[deprecated "`Array.get?` is deprecated, use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_get?_toList (a : Array α) (i : Nat) : a.get? i = a.toList.get? i := by
simp [ getElem?_toList]
theorem get!_eq_get? [Inhabited α] (a : Array α) : a.get! n = (a.get? n).getD default := by
simp only [get!_eq_getElem?, get?_eq_getElem?]
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
theorem back!_eq_back? [Inhabited α] (a : Array α) : a.back! = a.back?.getD default := by
simp [back!, back?, getElem!_def, Option.getD]; rfl
@@ -3443,11 +3543,11 @@ theorem map_induction (as : Array α) (f : α → β) (motive : Nat → Prop) (h
(motive := fun i arr => motive i arr.size = i i h2, p i arr[i.1])
(init := #[]) (f := fun r a => r.push (f a)) ?_ ?_
obtain m, eq, w := t
· refine m, by simpa [map_eq_foldl] using eq, ?_
· refine m, by simp, ?_
intro i h
simp only [eq] at w
specialize w i, h h
simpa [map_eq_foldl] using w
simpa using w
· exact h0, rfl, nofun
· intro i b m, eq, w
refine ?_, ?_, ?_
@@ -3653,7 +3753,7 @@ theorem toListRev_toArray (l : List α) : l.toArray.toListRev = l.reverse := by
l.toArray.mapM f = List.toArray <$> l.mapM f := by
simp only [ mapM'_eq_mapM, mapM_eq_foldlM]
suffices init : Array β,
foldlM (fun bs a => bs.push <$> f a) init l.toArray = (init ++ toArray ·) <$> mapM' f l by
Array.foldlM (fun bs a => bs.push <$> f a) init l.toArray = (init ++ toArray ·) <$> mapM' f l by
simpa using this #[]
intro init
induction l generalizing init with
@@ -3858,8 +3958,6 @@ end Array
namespace List
@[deprecated back!_toArray (since := "2024-10-31")] abbrev back_toArray := @back!_toArray
@[deprecated setIfInBounds_toArray (since := "2024-11-24")] abbrev setD_toArray := @setIfInBounds_toArray
end List
@@ -3881,9 +3979,6 @@ abbrev getElem_fin_eq_toList_get := @getElem_fin_eq_getElem_toList
@[deprecated "Use reverse direction of `getElem?_toList`" (since := "2024-10-17")]
abbrev getElem?_eq_toList_getElem? := @getElem?_toList
@[deprecated get?_eq_get?_toList (since := "2024-10-17")]
abbrev get?_eq_toList_get? := @get?_eq_get?_toList
@[deprecated getElem?_swap (since := "2024-10-17")] abbrev get?_swap := @getElem?_swap
@[deprecated getElem_push (since := "2024-10-21")] abbrev get_push := @getElem_push

View File

@@ -8,6 +8,9 @@ import Init.Data.Array.Basic
import Init.Data.Nat.Lemmas
import Init.Data.Range
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array
/--

View File

@@ -418,6 +418,11 @@ theorem mapIdx_eq_mapIdx_iff {l : Array α} :
rcases l with l
simp [List.getLast?_mapIdx]
@[simp] theorem back_mapIdx {l : Array α} {f : Nat α β} (h) :
(l.mapIdx f).back h = f (l.size - 1) (l.back (by simpa using h)) := by
rcases l with l
simp [List.getLast_mapIdx]
@[simp] theorem mapIdx_mapIdx {l : Array α} {f : Nat α β} {g : Nat β γ} :
(l.mapIdx f).mapIdx g = l.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff]
@@ -434,3 +439,57 @@ theorem mapIdx_eq_mkArray_iff {l : Array α} {f : Nat → α → β} {b : β} :
simp [List.mapIdx_reverse]
end Array
namespace List
theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] (l : List α)
(f : (i : Nat) α (h : i < l.length) m β) :
l.toArray.mapFinIdxM f = toArray <$> l.mapFinIdxM f := by
let rec go (i : Nat) (acc : Array β) (inv : i + acc.size = l.length) :
Array.mapFinIdxM.map l.toArray f i acc.size inv acc
= toArray <$> mapFinIdxM.go l f (l.drop acc.size) acc
(by simp [Nat.sub_add_cancel (Nat.le.intro (Nat.add_comm _ _ inv))]) := by
match i with
| 0 =>
rw [Nat.zero_add] at inv
simp only [Array.mapFinIdxM.map, inv, drop_length, mapFinIdxM.go, map_pure]
| k + 1 =>
conv => enter [2, 2, 3]; rw [ getElem_cons_drop l acc.size (by omega)]
simp only [Array.mapFinIdxM.map, mapFinIdxM.go, _root_.map_bind]
congr; funext x
conv => enter [1, 4]; rw [ Array.size_push _ x]
conv => enter [2, 2, 3]; rw [ Array.size_push _ x]
refine go k (acc.push x) _
simp only [Array.mapFinIdxM, mapFinIdxM]
exact go _ #[] _
theorem mapIdxM_toArray [Monad m] [LawfulMonad m] (l : List α)
(f : Nat α m β) :
l.toArray.mapIdxM f = toArray <$> l.mapIdxM f := by
let rec go (bs : List α) (acc : Array β) (inv : bs.length + acc.size = l.length) :
mapFinIdxM.go l (fun i a h => f i a) bs acc inv = mapIdxM.go f bs acc := by
match bs with
| [] => simp only [mapFinIdxM.go, mapIdxM.go]
| x :: xs => simp only [mapFinIdxM.go, mapIdxM.go, go]
unfold Array.mapIdxM
rw [mapFinIdxM_toArray]
simp only [mapFinIdxM, mapIdxM]
rw [go]
end List
namespace Array
theorem toList_mapFinIdxM [Monad m] [LawfulMonad m] (l : Array α)
(f : (i : Nat) α (h : i < l.size) m β) :
toList <$> l.mapFinIdxM f = l.toList.mapFinIdxM f := by
rw [List.mapFinIdxM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_mapIdxM [Monad m] [LawfulMonad m] (l : Array α)
(f : Nat α m β) :
toList <$> l.mapIdxM f = l.toList.mapIdxM f := by
rw [List.mapIdxM_toArray]
simp only [Functor.map_map, id_map']
end Array

View File

@@ -12,11 +12,11 @@ namespace Array
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp +arith)
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) : sizeOf (as.get i h) < sizeOf as := by
cases as with | _ as =>
simpa using Nat.lt_trans (List.sizeOf_get _ i, h) (by simp_arith)
simpa using Nat.lt_trans (List.sizeOf_get _ i, h) (by simp +arith)
@[simp] theorem sizeOf_getElem [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) :
sizeOf (as[i]'h) < sizeOf as := sizeOf_get _ _ h
@@ -29,8 +29,8 @@ macro "array_get_dec" : tactic =>
-- subsumed by simp
-- | with_reducible apply sizeOf_get
-- | with_reducible apply sizeOf_getElem
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_get ..)); simp_arith
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_getElem ..)); simp_arith
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_get ..)); simp +arith
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_getElem ..)); simp +arith
)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec)
@@ -45,7 +45,7 @@ macro "array_mem_dec" : tactic =>
| with_reducible
apply Nat.lt_of_lt_of_le (Array.sizeOf_lt_of_mem ?h)
case' h => assumption
simp_arith)
simp +arith)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_mem_dec)

View File

@@ -221,6 +221,121 @@ theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
cases l
simp
end Array
namespace List
theorem filterM_toArray [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) :
l.toArray.filterM p = toArray <$> l.filterM p := by
simp only [Array.filterM, filterM, foldlM_toArray, bind_pure_comp, Functor.map_map]
conv => lhs; rw [ reverse_nil]
generalize [] = acc
induction l generalizing acc with simp
| cons x xs ih =>
congr; funext b
cases b
· simp only [Bool.false_eq_true, reduceIte, pure_bind, cond_false]
exact ih acc
· simp only [reduceIte, reverse_cons, pure_bind, cond_true]
exact ih (x :: acc)
/-- Variant of `filterM_toArray` with a side condition for the stop position. -/
@[simp] theorem filterM_toArray' [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) (w : stop = l.length) :
l.toArray.filterM p 0 stop = toArray <$> l.filterM p := by
subst w
rw [filterM_toArray]
theorem filterRevM_toArray [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) :
l.toArray.filterRevM p = toArray <$> l.filterRevM p := by
simp [Array.filterRevM, filterRevM]
rw [ foldlM_reverse, foldlM_toArray, Array.filterM, filterM_toArray]
simp only [filterM, bind_pure_comp, Functor.map_map, reverse_toArray, reverse_reverse]
/-- Variant of `filterRevM_toArray` with a side condition for the start position. -/
@[simp] theorem filterRevM_toArray' [Monad m] [LawfulMonad m] (l : List α) (p : α m Bool) (w : start = l.length) :
l.toArray.filterRevM p start 0 = toArray <$> l.filterRevM p := by
subst w
rw [filterRevM_toArray]
theorem filterMapM_toArray [Monad m] [LawfulMonad m] (l : List α) (f : α m (Option β)) :
l.toArray.filterMapM f = toArray <$> l.filterMapM f := by
simp [Array.filterMapM, filterMapM]
conv => lhs; rw [ reverse_nil]
generalize [] = acc
induction l generalizing acc with simp [filterMapM.loop]
| cons x xs ih =>
congr; funext o
cases o
· simp only [pure_bind]; exact ih acc
· simp only [pure_bind]; rw [ List.reverse_cons]; exact ih _
/-- Variant of `filterMapM_toArray` with a side condition for the stop position. -/
@[simp] theorem filterMapM_toArray' [Monad m] [LawfulMonad m] (l : List α) (f : α m (Option β)) (w : stop = l.length) :
l.toArray.filterMapM f 0 stop = toArray <$> l.filterMapM f := by
subst w
rw [filterMapM_toArray]
@[simp] theorem flatMapM_toArray [Monad m] [LawfulMonad m] (l : List α) (f : α m (Array β)) :
l.toArray.flatMapM f = toArray <$> l.flatMapM (fun a => Array.toList <$> f a) := by
simp only [Array.flatMapM, bind_pure_comp, foldlM_toArray, flatMapM]
conv => lhs; arg 2; change [].reverse.flatten.toArray
generalize [] = acc
induction l generalizing acc with
| nil => simp only [foldlM_nil, flatMapM.loop, map_pure]
| cons x xs ih =>
simp only [foldlM_cons, bind_map_left, flatMapM.loop, _root_.map_bind]
congr; funext a
conv => lhs; rw [Array.toArray_append, flatten_concat, reverse_cons]
exact ih _
end List
namespace Array
@[congr] theorem filterM_congr [Monad m] {as bs : Array α} (w : as = bs)
{p : α m Bool} {q : α m Bool} (h : a, p a = q a) :
as.filterM p = bs.filterM q := by
subst w
simp [filterM, h]
@[congr] theorem filterRevM_congr [Monad m] {as bs : Array α} (w : as = bs)
{p : α m Bool} {q : α m Bool} (h : a, p a = q a) :
as.filterRevM p = bs.filterRevM q := by
subst w
simp [filterRevM, h]
@[congr] theorem filterMapM_congr [Monad m] {as bs : Array α} (w : as = bs)
{f : α m (Option β)} {g : α m (Option β)} (h : a, f a = g a) :
as.filterMapM f = bs.filterMapM g := by
subst w
simp [filterMapM, h]
@[congr] theorem flatMapM_congr [Monad m] {as bs : Array α} (w : as = bs)
{f : α m (Array β)} {g : α m (Array β)} (h : a, f a = g a) :
as.flatMapM f = bs.flatMapM g := by
subst w
simp [flatMapM, h]
theorem toList_filterM [Monad m] [LawfulMonad m] (a : Array α) (p : α m Bool) :
toList <$> a.filterM p = a.toList.filterM p := by
rw [List.filterM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_filterRevM [Monad m] [LawfulMonad m] (a : Array α) (p : α m Bool) :
toList <$> a.filterRevM p = a.toList.filterRevM p := by
rw [List.filterRevM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_filterMapM [Monad m] [LawfulMonad m] (a : Array α) (f : α m (Option β)) :
toList <$> a.filterMapM f = a.toList.filterMapM f := by
rw [List.filterMapM_toArray]
simp only [Functor.map_map, id_map']
theorem toList_flatMapM [Monad m] [LawfulMonad m] (a : Array α) (f : α m (Array β)) :
toList <$> a.flatMapM f = a.toList.flatMapM (fun a => toList <$> f a) := by
rw [List.flatMapM_toArray]
simp only [Functor.map_map, id_map']
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
/--
@@ -236,6 +351,16 @@ and simplifies these to the function directly taking the value.
simp
rw [List.foldlM_subtype hf]
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β α m β) :
(wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
simp [wfParam]
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : Array (Subtype P)) (f : β α m β) :
xs.unattach.foldlM f = xs.foldlM fun b x, h =>
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
f b (wfParam x) := by
simp [wfParam]
/--
This lemma identifies monadic folds over lists of subtypes, where the function only depends on the value, not the proposition,
and simplifies these to the function directly taking the value.
@@ -249,6 +374,17 @@ and simplifies these to the function directly taking the value.
simp
rw [List.foldrM_subtype hf]
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α β m β) :
(wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
simp [wfParam]
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α β m β) :
xs.unattach.foldrM f = xs.foldrM fun x, h b =>
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
f (wfParam x) b := by
simp [wfParam]
/--
This lemma identifies monadic maps over lists of subtypes, where the function only depends on the value, not the proposition,
and simplifies these to the function directly taking the value.
@@ -260,20 +396,53 @@ and simplifies these to the function directly taking the value.
simp
rw [List.mapM_subtype hf]
-- Without `filterMapM_toArray` relating `filterMapM` on `List` and `Array` we can't prove this yet:
-- @[simp] theorem filterMapM_subtype [Monad m] [LawfulMonad m] {p : α → Prop} {l : Array { x // p x }}
-- {f : { x // p x } → m (Option β)} {g : α → m (Option β)} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
-- l.filterMapM f = l.unattach.filterMapM g := by
-- rcases l with ⟨l⟩
-- simp
-- rw [List.filterMapM_subtype hf]
@[wf_preprocess] theorem mapM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α m β) :
(wfParam xs).mapM f = xs.attach.unattach.mapM f := by
simp [wfParam]
-- Without `flatMapM_toArray` relating `flatMapM` on `List` and `Array` we can't prove this yet:
-- @[simp] theorem flatMapM_subtype [Monad m] [LawfulMonad m] {p : α → Prop} {l : Array { x // p x }}
-- {f : { x // p x } → m (Array β)} {g : α → m (Array β)} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
-- (l.flatMapM f) = l.unattach.flatMapM g := by
-- rcases l with ⟨l⟩
-- simp
-- rw [List.flatMapM_subtype hf]
@[wf_preprocess] theorem mapM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α m β) :
xs.unattach.mapM f = xs.mapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[simp] theorem filterMapM_subtype [Monad m] [LawfulMonad m] {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } m (Option β)} {g : α m (Option β)} (hf : x h, f x, h = g x) (w : stop = l.size) :
l.filterMapM f 0 stop = l.unattach.filterMapM g := by
subst w
rcases l with l
simp
rw [List.filterMapM_subtype hf]
@[wf_preprocess] theorem filterMapM_wfParam [Monad m] [LawfulMonad m]
(xs : Array α) (f : α m (Option β)) :
(wfParam xs).filterMapM f = xs.attach.unattach.filterMapM f := by
simp [wfParam]
@[wf_preprocess] theorem filterMapM_unattach [Monad m] [LawfulMonad m]
(P : α Prop) (xs : Array (Subtype P)) (f : α m (Option β)) :
xs.unattach.filterMapM f = xs.filterMapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[simp] theorem flatMapM_subtype [Monad m] [LawfulMonad m] {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } m (Array β)} {g : α m (Array β)} (hf : x h, f x, h = g x) :
(l.flatMapM f) = l.unattach.flatMapM g := by
rcases l with l
simp
rw [List.flatMapM_subtype]
simp [hf]
@[wf_preprocess] theorem flatMapM_wfParam [Monad m] [LawfulMonad m]
(xs : Array α) (f : α m (Array β)) :
(wfParam xs).flatMapM f = xs.attach.unattach.flatMapM f := by
simp [wfParam]
@[wf_preprocess] theorem flatMapM_unattach [Monad m] [LawfulMonad m]
(P : α Prop) (xs : Array (Subtype P)) (f : α m (Array β)) :
xs.unattach.flatMapM f = xs.flatMapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
end Array

View File

@@ -7,6 +7,20 @@ prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop
/-!
These lemmas are used in the internals of HashMap.
They should find a new home and/or be reformulated.
-/
namespace List
theorem exists_of_set {i : Nat} {a' : α} {l : List α} (h : i < l.length) :
l₁ l₂, l = l₁ ++ l[i] :: l₂ l₁.length = i l.set i a' = l₁ ++ a' :: l₂ := by
refine l.take i, l.drop (i + 1), by simp, length_take_of_le (Nat.le_of_lt h), ?_
simp [set_eq_take_append_cons_drop, h]
end List
namespace Array
theorem exists_of_uset (self : Array α) (i d h) :

View File

@@ -25,13 +25,17 @@ set_option linter.missingDocs true
namespace BitVec
@[inline, deprecated BitVec.ofNatLT (since := "2025-02-13"), inherit_doc BitVec.ofNatLT]
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2 ^ n) : BitVec n :=
BitVec.ofNatLT i p
section Nat
instance natCastInst : NatCast (BitVec w) := BitVec.ofNat w
/-- Theorem for normalizing the bit vector literal representation. -/
-- TODO: This needs more usage data to assess which direction the simp should go.
@[simp, bv_toNat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
@[simp, bitvec_to_nat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
-- Note. Mathlib would like this to go the other direction.
@[simp] theorem natCast_eq_ofNat (w x : Nat) : @Nat.cast (BitVec w) _ x = .ofNat w x := rfl
@@ -55,12 +59,12 @@ end subsingleton
section zero_allOnes
/-- Return a bitvector `0` of size `n`. This is the bitvector with all zero bits. -/
protected def zero (n : Nat) : BitVec n := .ofNatLt 0 (Nat.two_pow_pos n)
protected def zero (n : Nat) : BitVec n := .ofNatLT 0 (Nat.two_pow_pos n)
instance : Inhabited (BitVec n) where default := .zero n
/-- Bit vector of size `n` where all bits are `1`s -/
def allOnes (n : Nat) : BitVec n :=
.ofNatLt (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
.ofNatLT (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
end zero_allOnes
@@ -123,6 +127,7 @@ instance : GetElem (BitVec w) Nat Bool fun _ i => i < w where
theorem getElem_eq_testBit_toNat (x : BitVec w) (i : Nat) (h : i < w) :
x[i] = x.toNat.testBit i := rfl
@[simp]
theorem getLsbD_eq_getElem {x : BitVec w} {i : Nat} (h : i < w) :
x.getLsbD i = x[i] := rfl
@@ -138,7 +143,7 @@ protected def toInt (x : BitVec n) : Int :=
(x.toNat : Int) - (2^n : Nat)
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLT (i % (Int.ofNat (2^n))).toNat (by
apply (Int.toNat_lt _).mpr
· apply Int.emod_lt_of_pos
exact Int.ofNat_pos.mpr (Nat.two_pow_pos _)
@@ -167,12 +172,12 @@ recommended_spelling "one" for "1#n" in [BitVec.ofNat, «term__#__»]
| `($(_) $n $i:num) => `($i:num#$n)
| _ => throw ()
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLt i lt`. -/
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLT i lt`. -/
scoped syntax:max term:max noWs "#'" noWs term:max : term
macro_rules | `($i#'$p) => `(BitVec.ofNatLt $i $p)
macro_rules | `($i#'$p) => `(BitVec.ofNatLT $i $p)
/-- Unexpander for bit vector literals without truncation. -/
@[app_unexpander BitVec.ofNatLt] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNatLT] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
| `($(_) $i $p) => `($i#'$p)
| _ => throw ()
@@ -356,7 +361,7 @@ end relations
section cast
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq x.isLt)
@[inline] protected def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLT x.toNat (eq x.isLt)
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
(BitVec.ofNat n x).cast h = BitVec.ofNat m x := by

View File

@@ -285,7 +285,7 @@ theorem adc_spec (x y : BitVec w) (c : Bool) :
simp [carry, Nat.mod_one]
cases c <;> rfl
case step =>
simp [adcb, Prod.mk.injEq, carry_succ, getLsbD_add_add_bool]
simp [adcb, Prod.mk.injEq, carry_succ, getElem_add_add_bool]
theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := by
simp [adc_spec]
@@ -295,7 +295,7 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
theorem getMsbD_add {i : Nat} {i_lt : i < w} {x y : BitVec w} :
getMsbD (x + y) i =
Bool.xor (getMsbD x i) (Bool.xor (getMsbD y i) (carry (w - 1 - i) x y false)) := by
simp [getMsbD, getLsbD_add, i_lt, show w - 1 - i < w by omega]
simp [getMsbD, getElem_add, i_lt, show w - 1 - i < w by omega]
theorem msb_add {w : Nat} {x y: BitVec w} :
(x + y).msb =
@@ -359,24 +359,25 @@ theorem msb_sub {x y: BitVec w} :
/-! ### Negation -/
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
getLsbD (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) i.val = !(getLsbD x i.val) := by
(((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd)[i.val] = !(getLsbD x i.val) := by
apply iunfoldr_getLsbD (fun _ => ()) i (by simp)
theorem bit_not_add_self (x : BitVec w) :
((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd + x = -1 := by
((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd + x = -1 := by
simp only [add_eq_adc]
apply iunfoldr_replace_snd (fun _ => false) (-1) false rfl
intro i; simp only [ BitVec.not, adcb, testBit_toNat]
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd)]
<;> simp [bit_not_testBit, negOne_eq_allOnes, getLsbD_allOnes]
intro i; simp only [adcb, Fin.is_lt, getLsbD_eq_getElem, atLeastTwo_false_right, bne_false,
ofNat_eq_ofNat, Fin.getElem_fin, Prod.mk.injEq, and_eq_false_imp]
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x[i.val])))) ()).snd)]
<;> simp [bit_not_testBit, negOne_eq_allOnes, getElem_allOnes]
theorem bit_not_eq_not (x : BitVec w) :
((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd = ~~~ x := by
((iunfoldr (fun i c => (c, !(x[i])))) ()).snd = ~~~ x := by
simp [allOnes_sub_eq_not, BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), negOne_eq_allOnes]
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
simp only [ add_eq_adc]
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) _ rfl]
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x[i.val])))) ()).snd) _ rfl]
· rw [BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), sub_toAdd, BitVec.add_comm _ (-x)]
simp [ sub_toAdd, BitVec.sub_add_cancel]
· simp [bit_not_testBit x _]
@@ -575,16 +576,18 @@ theorem setWidth_setWidth_succ_eq_setWidth_setWidth_add_twoPow (x : BitVec w) (i
setWidth w (x.setWidth i) + (x &&& twoPow w i) := by
rw [add_eq_or_of_and_eq_zero]
· ext k h
simp only [getLsbD_setWidth, h, decide_true, Bool.true_and, getLsbD_or, getLsbD_and]
simp only [getElem_setWidth, getLsbD_setWidth, h, getLsbD_eq_getElem, getElem_or, getElem_and,
getElem_twoPow]
by_cases hik : i = k
· subst hik
simp [h]
· simp only [getLsbD_twoPow, hik, decide_false, Bool.and_false, Bool.or_false]
by_cases hik' : k < (i + 1)
· by_cases hik' : k < (i + 1)
· have hik'' : k < i := by omega
simp [hik', hik'']
omega
· have hik'' : ¬ (k < i) := by omega
simp [hik', hik'']
omega
· ext k
simp only [and_twoPow, getLsbD_and, getLsbD_setWidth, Fin.is_lt, decide_true, Bool.true_and,
getLsbD_zero, and_eq_false_imp, and_eq_true, decide_eq_true_eq, and_imp]
@@ -1280,4 +1283,17 @@ theorem getMsbD_umod {n d : BitVec w}:
simp [BitVec.getMsbD_eq_getLsbD, hi]
· simp [show w i by omega]
/-! ### Mappings to and from BitVec -/
theorem eq_iff_eq_of_inv (f : α BitVec w) (g : BitVec w α) (h : x, g (f x) = x) :
x y, x = y f x = f y := by
intro x y
constructor
· intro h'
rw [h']
· intro h'
have := congrArg g h'
simpa [h] using this
end BitVec

View File

@@ -101,14 +101,14 @@ Correctness theorem for `iunfoldr`.
theorem iunfoldr_replace
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)
(init : state 0 = a)
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value[i.val])) :
iunfoldr f a = (state w, value) := by
simp [iunfoldr.eq_test state value a init step]
theorem iunfoldr_replace_snd
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)
(init : state 0 = a)
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value[i.val])) :
(iunfoldr f a).snd = value := by
simp [iunfoldr.eq_test state value a init step]

File diff suppressed because it is too large Load Diff

View File

@@ -370,14 +370,14 @@ theorem and_or_inj_left_iff :
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
def toNat (b : Bool) : Nat := cond b 1 0
@[simp, bv_toNat] theorem toNat_false : false.toNat = 0 := rfl
@[simp, bitvec_to_nat] theorem toNat_false : false.toNat = 0 := rfl
@[simp, bv_toNat] theorem toNat_true : true.toNat = 1 := rfl
@[simp, bitvec_to_nat] theorem toNat_true : true.toNat = 1 := rfl
theorem toNat_le (c : Bool) : c.toNat 1 := by
cases c <;> trivial
@[bv_toNat]
@[bitvec_to_nat]
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
Nat.lt_succ_of_le (toNat_le _)
@@ -580,17 +580,13 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
decide (p q) = (decide p == decide q) := by
cases dp with | _ p => simp [p]
@[boolToPropSimps]
theorem and_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p && q) = decide (p q) := by
cases dp with | _ p => simp [p]
@[bool_to_prop]
theorem and_eq_decide (p q : Bool) : (p && q) = decide (p q) := by simp
@[boolToPropSimps]
theorem or_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p || q) = decide (p q) := by
cases dp with | _ p => simp [p]
@[bool_to_prop]
theorem or_eq_decide (p q : Bool) : (p || q) = decide (p q) := by simp
@[boolToPropSimps]
@[bool_to_prop]
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(decide p == decide q) = decide (p q) := by
cases dp with | _ p => simp [p]

View File

@@ -47,7 +47,7 @@ def uget : (a : @& ByteArray) → (i : USize) → (h : i.toNat < a.size := by ge
@[extern "lean_byte_array_get"]
def get! : (@& ByteArray) (@& Nat) UInt8
| bs, i => bs.get! i
| bs, i => bs[i]!
@[extern "lean_byte_array_fget"]
def get : (a : @& ByteArray) (i : @& Nat) (h : i < a.size := by get_elem_tactic) UInt8
@@ -56,7 +56,7 @@ def get : (a : @& ByteArray) → (i : @& Nat) → (h : i < a.size := by get_elem
instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
getElem xs i h := xs.get i
instance : GetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
instance : GetElem ByteArray USize UInt8 fun xs i => i.toFin < xs.size where
getElem xs i h := xs.uget i h
@[extern "lean_byte_array_set"]

View File

@@ -40,12 +40,12 @@ theorem isValidUInt32 (n : Nat) (h : isValidCharNat n) : n < UInt32.size := by
apply Nat.lt_trans h₂
decide
theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNat' n (isValidUInt32 n h)) :=
theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNatLT n (isValidUInt32 n h)) :=
match h with
| Or.inl h =>
Or.inl (UInt32.ofNat'_lt_of_lt _ (by decide) h)
Or.inl (UInt32.ofNatLT_lt_of_lt _ (by decide) h)
| Or.inr h₁, h₂ =>
Or.inr UInt32.lt_ofNat'_of_lt _ (by decide) h₁, UInt32.ofNat'_lt_of_lt _ (by decide) h₂
Or.inr UInt32.lt_ofNatLT_of_lt _ (by decide) h₁, UInt32.ofNatLT_lt_of_lt _ (by decide) h₂
theorem isValidChar_zero : isValidChar 0 :=
Or.inl (by decide)

View File

@@ -51,6 +51,14 @@ Returns `a` modulo `n + 1` as a `Fin n.succ`.
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
-- `i.toNat` to `i.val`.
@[inline, inherit_doc val]
protected def toNat (i : Fin n) : Nat :=
i.val
@[simp] theorem toNat_eq_val {i : Fin n} : i.toNat = i.val := rfl
private theorem mlt {b : Nat} : {a : Nat} a < n b % n < n
| 0, h => Nat.mod_lt _ h
| _+1, h =>

View File

@@ -51,7 +51,7 @@ def get : (ds : @& FloatArray) → (i : @& Nat) → (h : i < ds.size := by get_e
@[extern "lean_float_array_get"]
def get! : (@& FloatArray) (@& Nat) Float
| ds, i => ds.get! i
| ds, i => ds[i]!
def get? (ds : FloatArray) (i : Nat) : Option Float :=
if h : i < ds.size then
@@ -62,7 +62,7 @@ def get? (ds : FloatArray) (i : Nat) : Option Float :=
instance : GetElem FloatArray Nat Float fun xs i => i < xs.size where
getElem xs i h := xs.get i h
instance : GetElem FloatArray USize Float fun xs i => i.val < xs.size where
instance : GetElem FloatArray USize Float fun xs i => i.toNat < xs.size where
getElem xs i h := xs.uget i h
@[extern "lean_float_array_uset"]

View File

@@ -14,3 +14,5 @@ import Init.Data.Int.LemmasAux
import Init.Data.Int.Order
import Init.Data.Int.Pow
import Init.Data.Int.Cooper
import Init.Data.Int.Linear
import Init.Data.Int.Cutsat

View File

@@ -0,0 +1,68 @@
/-
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
-/
prelude
import Init.Data.AC
import Init.Data.Int.Gcd
namespace Int.Linear
/-!
Helper theorems for solving divisibility constraints.
The two theorems are used to justify the `Div-Solve` rule
in the section "Strong Conflict Resolution" in the paper
"Cutting to the Chase: Solving Linear Integer Arithmetic".
-/
theorem dvd_solve_1 {x : Int} {d₁ a₁ p₁ : Int} {d₂ a₂ p₂ : Int} {α β d : Int}
(h : α*a₁*d₂ + β*a₂*d₁ = d)
(h₁ : d₁ a₁*x + p₁)
(h₂ : d₂ a₂*x + p₂)
: d₁*d₂ d*x + α*d₂*p₁ + β*d₁*p₂ := by
rcases h₁ with k₁, h₁
replace h₁ : α*a₁*d₂*x + α*d₂*p₁ = d₁*d₂*(α*k₁) := by
have ac₁ : d₁*d₂*(α*k₁) = α*d₂*(d₁*k₁) := by ac_rfl
have ac₂ : α * a₁ * d₂ * x = α * d₂ * (a₁ * x) := by ac_rfl
rw [ac₁, h₁, Int.mul_add, ac₂]
rcases h₂ with k₂, h₂
replace h₂ : β*a₂*d₁*x + β*d₁*p₂ = d₁*d₂*(β*k₂) := by
have ac₁ : d₁*d₂*(β*k₂) = β*d₁*(d₂*k₂) := by ac_rfl
have ac₂ : β * a₂ * d₁ * x = β * d₁ * (a₂ * x) := by ac_rfl
rw [ac₁, h₂, Int.mul_add, ac₂]
replace h₁ : d₁*d₂ α*a₁*d₂*x + α*d₂*p₁ := α*k₁, h₁
replace h₂ : d₁*d₂ β*a₂*d₁*x + β*d₁*p₂ := β*k₂, h₂
have h' := Int.dvd_add h₁ h₂; clear h₁ h₂ k₁ k₂
replace h : d*x = α*a₁*d₂*x + β*a₂*d₁*x := by
rw [h, Int.add_mul]
have ac :
α * a₁ * d₂ * x + α * d₂ * p₁ + (β * a₂ * d₁ * x + β * d₁ * p₂)
=
α * a₁ * d₂ * x + β * a₂ * d₁ * x + α * d₂ * p₁ + β * d₁ * p₂ := by ac_rfl
rw [h, ac]
assumption
theorem dvd_solve_2 {x : Int} {d₁ a₁ p₁ : Int} {d₂ a₂ p₂ : Int} {d : Int}
(h : d = Int.gcd (a₁*d₂) (a₂*d₁))
(h₁ : d₁ a₁*x + p₁)
(h₂ : d₂ a₂*x + p₂)
: d a₂*p₁ - a₁*p₂ := by
rcases h₁ with k₁, h₁
rcases h₂ with k₂, h₂
have h₃ : d a₁*d₂ := by
rw [h]; apply Int.gcd_dvd_left
have h₄ : d a₂*d₁ := by
rw [h]; apply Int.gcd_dvd_right
rcases h₃ with k₃, h₃
rcases h₄ with k₄, h₄
have : a₂*p₁ - a₁*p₂ = a₂*d₁*k₁ - a₁*d₂*k₂ := by
have ac₁ : a₂*d₁*k₁ = a₂*(d₁*k₁) := by ac_rfl
have ac₂ : a₁*d₂*k₂ = a₁*(d₂*k₂) := by ac_rfl
have ac₃ : a₁*(a₂*x) = a₂*(a₁*x) := by ac_rfl
rw [ac₁, ac₂, h₁, h₂, Int.mul_add, Int.mul_add, ac₃, Int.sub_sub, Int.add_comm, Int.add_sub_assoc]
simp
rw [h₃, h₄, Int.mul_assoc, Int.mul_assoc, Int.mul_sub] at this
exact k₄ * k₁ - k₃ * k₂, this
end Int.Linear

View File

@@ -22,11 +22,11 @@ namespace Int
protected theorem dvd_def (a b : Int) : (a b) = Exists (fun c => b = a * c) := rfl
protected theorem dvd_zero (n : Int) : n 0 := 0, (Int.mul_zero _).symm
@[simp] protected theorem dvd_zero (n : Int) : n 0 := 0, (Int.mul_zero _).symm
protected theorem dvd_refl (n : Int) : n n := 1, (Int.mul_one _).symm
@[simp] protected theorem dvd_refl (n : Int) : n n := 1, (Int.mul_one _).symm
protected theorem one_dvd (n : Int) : 1 n := n, (Int.one_mul n).symm
@[simp] protected theorem one_dvd (n : Int) : 1 n := n, (Int.one_mul n).symm
protected theorem dvd_trans : {a b c : Int}, a b b c a c
| _, _, _, d, rfl, e, rfl => Exists.intro (d * e) (by rw [Int.mul_assoc])
@@ -1347,3 +1347,14 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
theorem bmod_neg_bmod : bmod (-(bmod x n)) n = bmod (-x) n := by
apply (bmod_add_cancel_right x).mp
rw [Int.add_left_neg, add_bmod_bmod, Int.add_left_neg]
/-! Helper theorems for `dvd` simproc -/
protected theorem dvd_eq_true_of_mod_eq_zero {a b : Int} (h : b % a == 0) : (a b) = True := by
simp [Int.dvd_of_emod_eq_zero, eq_of_beq h]
protected theorem dvd_eq_false_of_mod_ne_zero {a b : Int} (h : b % a != 0) : (a b) = False := by
simp [eq_of_beq] at h
simp [Int.dvd_iff_emod_eq_zero, h]
end Int

View File

@@ -225,7 +225,7 @@ attribute [local simp] subNatNat_self
@[local simp] protected theorem add_right_neg (a : Int) : a + -a = 0 := by
rw [Int.add_comm, Int.add_left_neg]
@[simp] protected theorem neg_eq_of_add_eq_zero {a b : Int} (h : a + b = 0) : -a = b := by
protected theorem neg_eq_of_add_eq_zero {a b : Int} (h : a + b = 0) : -a = b := by
rw [ Int.add_zero (-a), h, Int.add_assoc, Int.add_left_neg, Int.zero_add]
protected theorem eq_neg_of_eq_neg {a b : Int} (h : a = -b) : b = -a := by
@@ -326,26 +326,26 @@ theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
· exact (Nat.add_sub_cancel_left ..).symm
· dsimp; rw [Nat.add_assoc, Nat.sub_eq_zero_of_le (Nat.le_add_right ..)]; rfl
theorem toNat_of_nonpos : {z : Int}, z 0 z.toNat = 0
| 0, _ => rfl
| -[_+1], _ => rfl
/- ## add/sub injectivity -/
@[simp]
protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by
apply Iff.intro
· intro p
rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p]
· exact congrArg (· + k)
@[simp]
protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) i = j := by
simp [Int.add_comm k]
simp [Int.add_comm k, Int.add_left_inj]
@[simp]
protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) i = j := by
simp [Int.sub_eq_add_neg, Int.neg_inj]
simp [Int.sub_eq_add_neg, Int.neg_inj, Int.add_right_inj]
@[simp]
protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) i = j := by
simp [Int.sub_eq_add_neg]
simp [Int.sub_eq_add_neg, Int.add_left_inj]
/- ## Ring properties -/

View File

@@ -0,0 +1,724 @@
/-
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
-/
prelude
import Init.ByCases
import Init.Data.Prod
import Init.Data.Int.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.DivModLemmas
import Init.Data.Int.Gcd
import Init.Data.RArray
import Init.Data.AC
namespace Int.Linear
/-! Helper definitions and theorems for constructing linear arithmetic proofs. -/
abbrev Var := Nat
abbrev Context := Lean.RArray Int
def Var.denote (ctx : Context) (v : Var) : Int :=
ctx.get v
inductive Expr where
| num (v : Int)
| var (i : Var)
| add (a b : Expr)
| sub (a b : Expr)
| neg (a : Expr)
| mulL (k : Int) (a : Expr)
| mulR (a : Expr) (k : Int)
deriving Inhabited, BEq
def Expr.denote (ctx : Context) : Expr Int
| .add a b => Int.add (denote ctx a) (denote ctx b)
| .sub a b => Int.sub (denote ctx a) (denote ctx b)
| .neg a => Int.neg (denote ctx a)
| .num k => k
| .var v => v.denote ctx
| .mulL k e => Int.mul k (denote ctx e)
| .mulR e k => Int.mul (denote ctx e) k
inductive Poly where
| num (k : Int)
| add (k : Int) (v : Var) (p : Poly)
deriving BEq
def Poly.denote (ctx : Context) (p : Poly) : Int :=
match p with
| .num k => k
| .add k v p => Int.add (Int.mul k (v.denote ctx)) (denote ctx p)
/--
Similar to `Poly.denote`, but produces a denotation better for `simp +arith`.
Remark: we used to convert `Poly` back into `Expr` to achieve that.
-/
def Poly.denote' (ctx : Context) (p : Poly) : Int :=
match p with
| .num k => k
| .add 1 v p => go (v.denote ctx) p
| .add k v p => go (Int.mul k (v.denote ctx)) p
where
go (r : Int) (p : Poly) : Int :=
match p with
| .num 0 => r
| .num k => Int.add r k
| .add 1 v p => go (Int.add r (v.denote ctx)) p
| .add k v p => go (Int.add r (Int.mul k (v.denote ctx))) p
theorem Poly.denote'_go_eq_denote (ctx : Context) (p : Poly) (r : Int) : denote'.go ctx r p = p.denote ctx + r := by
induction r, p using denote'.go.induct ctx <;> simp [denote'.go, denote]
next => rw [Int.add_comm]
next ih => simp [denote'.go] at ih; rw [ih]; ac_rfl
next ih => simp [denote'.go] at ih; rw [ih]; ac_rfl
theorem Poly.denote'_eq_denote (ctx : Context) (p : Poly) : p.denote' ctx = p.denote ctx := by
unfold denote' <;> split <;> simp [denote, denote'_go_eq_denote] <;> ac_rfl
def Poly.addConst (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k+k')
| .add k' v' p => .add k' v' (addConst p k)
def Poly.insert (k : Int) (v : Var) (p : Poly) : Poly :=
match p with
| .num k' => .add k v (.num k')
| .add k' v' p =>
bif Nat.blt v' v then
.add k v <| .add k' v' p
else bif Nat.beq v v' then
if Int.add k k' == 0 then
p
else
.add (Int.add k k') v' p
else
.add k' v' (insert k v p)
/-- Normalizes the given polynomial by fusing monomial and constants. -/
def Poly.norm (p : Poly) : Poly :=
match p with
| .num k => .num k
| .add k v p => (norm p).insert k v
/-- Converts the given expression into a polynomial. -/
def Expr.toPoly' (e : Expr) : Poly :=
go 1 e (.num 0)
where
go (coeff : Int) : Expr (Poly Poly)
| .num k => bif k == 0 then id else (Poly.addConst · (Int.mul coeff k))
| .var v => (.add coeff v ·)
| .add a b => go coeff a go coeff b
| .sub a b => go coeff a go (-coeff) b
| .mulL k a
| .mulR a k => bif k == 0 then id else go (Int.mul coeff k) a
| .neg a => go (-coeff) a
/-- Converts the given expression into a polynomial, and then normalizes it. -/
def Expr.toPoly (e : Expr) : Poly :=
e.toPoly'.norm
/-- Relational contraints: equality and inequality. -/
inductive RelCnstr where
| /-- `p = 0` constraint. -/
eq (p : Poly)
| /-- `p ≤ 0` contraint. -/
le (p : Poly)
deriving BEq
def RelCnstr.denote (ctx : Context) : RelCnstr Prop
| .eq p => p.denote ctx = 0
| .le p => p.denote ctx 0
def RelCnstr.denote' (ctx : Context) : RelCnstr Prop
| .eq p => p.denote' ctx = 0
| .le p => p.denote' ctx 0
theorem RelCnstr.denote'_eq_denote (ctx : Context) (c : RelCnstr) : c.denote' ctx = c.denote ctx := by
cases c <;> simp [denote, denote', Poly.denote'_eq_denote]
/--
Returns the ceiling of the division `a / b`. That is, the result is equivalent to `⌈a / b⌉`.
Examples:
- `cdiv 7 3` returns `3`
- `cdiv (-7) 3` returns `-2`.
-/
def cdiv (a b : Int) : Int :=
-((-a)/b)
/--
Returns the ceiling-compatible remainder of the division `a / b`.
This function ensures that the remainder is consistent with `cdiv`, meaning:
```
a = b * cdiv a b + cmod a b
```
See theorem `cdiv_add_cmod`. We also have
```
-b < cmod a b ≤ 0
```
-/
def cmod (a b : Int) : Int :=
-((-a)%b)
theorem cdiv_add_cmod (a b : Int) : b*(cdiv a b) + cmod a b = a := by
unfold cdiv cmod
have := Int.ediv_add_emod (-a) b
have := congrArg (Neg.neg) this
simp at this
conv => rhs; rw[ this]
rw [Int.neg_add, Int.neg_mul, Int.neg_mul_comm]
theorem cmod_gt_of_pos (a : Int) {b : Int} (h : 0 < b) : cmod a b > -b :=
Int.neg_lt_neg (Int.emod_lt_of_pos (-a) h)
theorem cmod_nonpos (a : Int) {b : Int} (h : b 0) : cmod a b 0 := by
have := Int.neg_le_neg (Int.emod_nonneg (-a) h)
simp at this
assumption
theorem cmod_eq_zero_iff_emod_eq_zero (a b : Int) : cmod a b = 0 a%b = 0 := by
unfold cmod
have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a
simp at this
simp [Int.neg_emod, this, Eq.comm]
private abbrev div_mul_cancel_of_mod_zero :=
@Int.ediv_mul_cancel_of_emod_eq_zero
theorem cdiv_eq_div_of_divides {a b : Int} (h : a % b = 0) : a/b = cdiv a b := by
replace h := div_mul_cancel_of_mod_zero h
have hz : a % b = 0 := by
have := Int.ediv_add_emod a b
conv at this => rhs; rw [ Int.add_zero a]
rw [Int.mul_comm, h] at this
exact Int.add_left_cancel this
have hcz : cmod a b = 0 := cmod_eq_zero_iff_emod_eq_zero a b |>.mpr hz
have : (cdiv a b)*b = a := by
have := cdiv_add_cmod a b
simp [hcz] at this
rw [Int.mul_comm] at this
assumption
have : (a/b)*b = (cdiv a b)*b := Eq.trans h this.symm
by_cases h : b = 0
next => simp[cdiv, h]
next => rw [Int.mul_eq_mul_right_iff h] at this; assumption
/-- Returns the constant of the given linear polynomial. -/
def Poly.getConst : Poly Int
| .num k => k
| .add _ _ p => getConst p
/--
`p.div k` divides all coefficients of the polynomial `p` by `k`, but
rounds up the constant using `cdiv`.
Notes:
- We only use this function with `k`s that divides all coefficients.
- We use `cdiv` for the constant to implement the inequality tightening rule.
-/
def Poly.div (k : Int) : Poly Poly
| .num k' => .num (cdiv k' k)
| .add k' x p => .add (k'/k) x (div k p)
/--
Returns `true` if `k` divides all coefficients and the constant of the given
linear polynomial.
-/
def Poly.divAll (k : Int) : Poly Bool
| .num k' => k' % k == 0
| .add k' _ p => k' % k == 0 && divAll k p
/--
Returns `true` if `k` divides all coefficients of the given linear polynomial.
-/
def Poly.divCoeffs (k : Int) : Poly Bool
| .num _ => true
| .add k' _ p => k' % k == 0 && divCoeffs k p
/--
`p.mul k` multiplies all coefficients and constant of the polynomial `p` by `k`.
-/
def Poly.mul (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k*k')
| .add k' v p => .add (k*k') v (mul p k)
@[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
induction p <;> simp [mul, denote, *]
rw [Int.mul_assoc, Int.mul_add]
/-- Normalizes the polynomial of the given relational constraint. -/
def RelCnstr.norm : RelCnstr RelCnstr
| .eq p => .eq p.norm
| .le p => .le p.norm
/-- Returns `true` if `k` divides all coefficients and constant of the given relational constraint. -/
def RelCnstr.divAll (k : Int) : RelCnstr Bool
| .eq p | .le p => p.divAll k
/-- Returns `true` if `k` divides all coefficients of the given relational constraint. -/
def RelCnstr.divCoeffs (k : Int) : RelCnstr Bool
| .eq p | .le p => p.divCoeffs k
/-- Returns `true` if the given relational constraint is an inequality constraint of the form `p ≤ 0`. -/
def RelCnstr.isLe : RelCnstr Bool
| .eq _ => false
| .le _ => true
/--
Divides all coefficients and constants in the linear polynomial of the given constraint by `k`.
We rounds up the constant using `cdiv`.
-/
def RelCnstr.div (k : Int) : RelCnstr RelCnstr
| .eq p => .eq <| p.div k
| .le p => .le <| p.div k
/--
Multiplies all coefficients and constants in the linear polynomial of the given constraint by `k`.
-/
def RelCnstr.mul (k : Int) : RelCnstr RelCnstr
| .eq p => .eq <| p.mul k
| .le p => .le <| p.mul k
@[simp] theorem RelCnstr.denote_mul (ctx : Context) (c : RelCnstr) (k : Int) (h : k > 0) : (c.mul k).denote ctx = c.denote ctx := by
cases c <;> simp [mul, denote]
next =>
constructor
· intro h₁; cases (Int.mul_eq_zero.mp h₁)
next hz => simp [hz] at h
next => assumption
· intro h'; simp [*]
next =>
constructor
· intro h₁
conv at h₁ => rhs; rw [ Int.mul_zero k]
exact Int.le_of_mul_le_mul_left h₁ h
· intro h₂
have := Int.mul_le_mul_of_nonneg_left h₂ (Int.le_of_lt h)
simp at this; assumption
/-- Raw relational constraint. They are later converted into `RelCnstr`. -/
inductive RawRelCnstr where
| eq (p₁ p₂ : Expr)
| le (p₁ p₂ : Expr)
deriving Inhabited, BEq
/-- Returns `true` if the given relational constraint is an inequality constraint of the form `e₁ ≤ e₂`. -/
def RawRelCnstr.isLe : RawRelCnstr Bool
| .eq .. => false
| .le .. => true
def RawRelCnstr.denote (ctx : Context) : RawRelCnstr Prop
| .eq e₁ e₂ => e₁.denote ctx = e₂.denote ctx
| .le e₁ e₂ => e₁.denote ctx e₂.denote ctx
def RawRelCnstr.norm : RawRelCnstr RelCnstr
| .eq e₁ e₂ => .eq (e₁.sub e₂).toPoly.norm
| .le e₁ e₂ => .le (e₁.sub e₂).toPoly.norm
/-- A certificate for normalizing the coefficients of a raw relational constraint. -/
def divBy (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : Bool :=
k > 0 && c.norm == c'.mul k
attribute [local simp] Int.add_comm Int.add_assoc Int.add_left_comm Int.add_mul Int.mul_add
attribute [local simp] Poly.insert Poly.denote Poly.norm Poly.addConst
theorem Poly.denote_addConst (ctx : Context) (p : Poly) (k : Int) : (p.addConst k).denote ctx = p.denote ctx + k := by
induction p <;> simp [*]
attribute [local simp] Poly.denote_addConst
theorem Poly.denote_insert (ctx : Context) (k : Int) (v : Var) (p : Poly) :
(p.insert k v).denote ctx = p.denote ctx + k * v.denote ctx := by
induction p <;> simp [*]
next k' v' p' ih =>
by_cases h₁ : Nat.blt v' v <;> simp [*]
by_cases h₂ : Nat.beq v v' <;> simp [*]
by_cases h₃ : k + k' = 0 <;> simp [*, Nat.eq_of_beq_eq_true h₂]
rw [ Int.add_mul]
simp [*]
attribute [local simp] Poly.denote_insert
theorem Poly.denote_norm (ctx : Context) (p : Poly) : p.norm.denote ctx = p.denote ctx := by
induction p <;> simp [*]
attribute [local simp] Poly.denote_norm
theorem sub_fold (a b : Int) : a.sub b = a - b := rfl
theorem neg_fold (a : Int) : a.neg = -a := rfl
attribute [local simp] sub_fold neg_fold
attribute [local simp] Poly.div Poly.divAll RelCnstr.denote
theorem Poly.denote_div_eq_of_divAll (ctx : Context) (p : Poly) (k : Int) : p.divAll k (p.div k).denote ctx * k = p.denote ctx := by
induction p with
| num _ => simp; intro h; rw [ cdiv_eq_div_of_divides h]; exact div_mul_cancel_of_mod_zero h
| add k' v p ih =>
simp; intro h₁ h₂
replace h₁ := div_mul_cancel_of_mod_zero h₁
have ih := ih h₂
simp [ih]
apply congrArg (denote ctx p + ·)
rw [Int.mul_right_comm, h₁]
attribute [local simp] Poly.divCoeffs Poly.getConst
theorem Poly.denote_div_eq_of_divCoeffs (ctx : Context) (p : Poly) (k : Int) : p.divCoeffs k (p.div k).denote ctx * k + cmod p.getConst k = p.denote ctx := by
induction p with
| num k' => simp; rw [Int.mul_comm, cdiv_add_cmod]
| add k' v p ih =>
simp; intro h₁ h₂
replace h₁ := div_mul_cancel_of_mod_zero h₁
rw [ ih h₂]
rw [Int.mul_right_comm, h₁, Int.add_assoc]
attribute [local simp] RawRelCnstr.denote RawRelCnstr.norm Expr.denote
theorem Expr.denote_toPoly'_go (ctx : Context) (e : Expr) :
(toPoly'.go k e p).denote ctx = k * e.denote ctx + p.denote ctx := by
induction k, e using Expr.toPoly'.go.induct generalizing p with
| case1 k k' =>
simp only [toPoly'.go]
by_cases h : k' == 0
· simp [h, eq_of_beq h]
· simp [h, Var.denote]
| case2 k i => simp [toPoly'.go]
| case3 k a b iha ihb => simp [toPoly'.go, iha, ihb]
| case4 k a b iha ihb =>
simp [toPoly'.go, iha, ihb, Int.mul_sub]
rw [Int.sub_eq_add_neg, Int.neg_mul, Int.add_assoc]
| case5 k k' a ih
| case6 k a k' ih =>
simp only [toPoly'.go]
by_cases h : k' == 0
· simp [h, eq_of_beq h]
· simp [h, cond_false, Int.mul_assoc]
simp at ih
rw [ih]
rw [Int.mul_assoc, Int.mul_comm k']
| case7 k a ih => simp [toPoly'.go, ih]
theorem Expr.denote_toPoly (ctx : Context) (e : Expr) : e.toPoly.denote ctx = e.denote ctx := by
simp [toPoly, toPoly', Expr.denote_toPoly'_go]
attribute [local simp] Expr.denote_toPoly RelCnstr.denote
theorem RawRelCnstr.denote_norm (ctx : Context) (c : RawRelCnstr) : c.norm.denote ctx = c.denote ctx := by
cases c <;> simp
· rw [Int.sub_eq_zero]
· constructor
· exact Int.le_of_sub_nonpos
· exact Int.sub_nonpos_of_le
instance : LawfulBEq Poly where
eq_of_beq {a} := by
induction a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
next ih =>
intro _ _ h
exact ih h
rfl := by
intro a
induction a <;> simp! [BEq.beq]
assumption
instance : LawfulBEq RelCnstr where
eq_of_beq {a b} := by
cases a <;> cases b <;> rename_i p₁ p₂ <;> simp_all! [BEq.beq]
· show (p₁ == p₂) = true _
simp
· show (p₁ == p₂) = true _
simp
rfl {a} := by
cases a <;> rename_i p <;> show (p == p) = true
<;> simp
theorem Expr.eq_of_toPoly_eq (ctx : Context) (e e' : Expr) (h : e.toPoly == e'.toPoly) : e.denote ctx = e'.denote ctx := by
have h := congrArg (Poly.denote ctx) (eq_of_beq h)
simp [Poly.norm] at h
assumption
theorem RawRelCnstr.eq_of_norm_eq (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (h : c.norm == c') : c.denote ctx = c'.denote' ctx := by
have h := congrArg (RelCnstr.denote ctx) (eq_of_beq h)
rw [denote_norm] at h
rw [RelCnstr.denote'_eq_denote, h]
theorem RawRelCnstr.eq_of_norm_eq_var (ctx : Context) (x y : Var) (c : RawRelCnstr) (h : c.norm == .eq (.add 1 x (.add (-1) y (.num 0))))
: c.denote ctx = (x.denote ctx = y.denote ctx) := by
have h := congrArg (RelCnstr.denote ctx) (eq_of_beq h)
rw [denote_norm] at h
rw [h]; simp
rw [ Int.sub_eq_add_neg, Int.sub_eq_zero]
theorem RawRelCnstr.eq_of_norm_eq_const (ctx : Context) (x : Var) (k : Int) (c : RawRelCnstr) (h : c.norm == .eq (.add 1 x (.num (-k))))
: c.denote ctx = (x.denote ctx = k) := by
have h := congrArg (RelCnstr.denote ctx) (eq_of_beq h)
rw [denote_norm] at h
rw [h]; simp
rw [Int.add_comm, Int.sub_eq_add_neg, Int.sub_eq_zero]
attribute [local simp] RelCnstr.divAll RelCnstr.div RelCnstr.mul
theorem RawRelCnstr.eq_of_norm_eq_mul (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (k : Int) (hz : k > 0) (h : c.norm = c'.mul k) : c.denote ctx = c'.denote ctx := by
replace h := congrArg (RelCnstr.denote ctx) h
simp only [RawRelCnstr.denote_norm, RelCnstr.denote_mul, *] at h
assumption
theorem RawRelCnstr.eq_of_divBy (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : divBy c c' k c.denote ctx = c'.denote' ctx := by
intro h
simp only [RelCnstr.denote'_eq_denote]
simp only [divBy, Bool.and_eq_true, bne_iff_ne, ne_eq, beq_iff_eq, decide_eq_true_eq] at h
have h₁, h₂ := h
exact eq_of_norm_eq_mul ctx c c' k h₁ h₂
private theorem mul_add_cmod_le_iff {a k b : Int} (h : k > 0) : a*k + cmod b k 0 a 0 := by
constructor
· intro h'
have h₁ : a*k -cmod b k := by
have := Int.le_sub_right_of_add_le h'
simp at this
assumption
have h₂ : -cmod b k < k := by
have := cmod_gt_of_pos b h
have := Int.neg_lt_neg this
simp at this
assumption
have h₃ : a*k < k := Int.lt_of_le_of_lt h₁ h₂
have h₄ : a < 1 := by
conv at h₃ => rhs; rw [ Int.one_mul k]
have := Int.lt_of_mul_lt_mul_right h₃ (Int.le_of_lt h)
assumption
exact Int.le_of_lt_add_one (h₄ : a < 0 + 1)
· intro h'
have : a * k 0 := Int.mul_nonpos_of_nonpos_of_nonneg h' (Int.le_of_lt h)
have := Int.add_le_add this (cmod_nonpos b (Int.ne_of_gt h))
simp at this
assumption
theorem RawRelCnstr.eq_of_norm_eq_of_divCoeffs (ctx : Context) (c₁ : RawRelCnstr) (c₂ : RelCnstr) (c₃ : RelCnstr) (k : Int)
: k > 0 c₂.divCoeffs k c₂.isLe c₁.norm = c₂ c₃ = c₂.div k c₁.denote ctx = c₃.denote ctx := by
intro h₀ h₁ h₂ h₃ h₄
have hz : k 0 := Int.ne_of_gt h₀
cases c₂ <;> simp [RelCnstr.isLe] at h₂
clear h₂
next p =>
simp [RelCnstr.divCoeffs] at h₁
replace h₁ := Poly.denote_div_eq_of_divCoeffs ctx p k h₁
replace h₃ := congrArg (RelCnstr.denote ctx) h₃
simp only [RelCnstr.denote.eq_2, h₁] at h₃
replace h₄ := congrArg (RelCnstr.denote ctx) h₄
simp only [RelCnstr.denote.eq_2, RelCnstr.div] at h₄
rw [denote_norm] at h₃
rw [h₃, h₄]
apply propext
apply mul_add_cmod_le_iff
exact h₀
/-- Certificate for normalizing the coefficients of inequality constraint with bound tightening. -/
def divByLe (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : Bool :=
k > 0 && c.isLe && c.norm.divCoeffs k && c' == c.norm.div k
theorem RawRelCnstr.eq_of_divByLe (ctx : Context) (c : RawRelCnstr) (c' : RelCnstr) (k : Int) : divByLe c c' k c.denote ctx = c'.denote' ctx := by
intro h
simp only [RelCnstr.denote'_eq_denote]
simp only [divByLe, Bool.and_eq_true, bne_iff_ne, ne_eq, beq_iff_eq, decide_eq_true_eq] at h
have h₀, h₁, h₂, h₃ := h
have hle : c.norm.isLe := by
cases c <;> simp [RawRelCnstr.isLe] at h₁
simp [RelCnstr.isLe]
apply eq_of_norm_eq_of_divCoeffs ctx c c.norm c' k h₀ h₂ hle rfl h₃
def RelCnstr.isUnsat : RelCnstr Bool
| .eq (.num k) => k != 0
| .eq _ => false
| .le (.num k) => k > 0
| .le _ => false
theorem RelCnstr.eq_false_of_isUnsat (ctx : Context) (c : RelCnstr) : c.isUnsat c.denote ctx = False := by
unfold isUnsat <;> split <;> simp <;> try contradiction
apply Int.not_le_of_gt
theorem RawRelCnstr.eq_false_of_isUnsat (ctx : Context) (c : RawRelCnstr) (h : c.norm.isUnsat) : c.denote ctx = False := by
have := RelCnstr.eq_false_of_isUnsat ctx c.norm h
rw [RawRelCnstr.denote_norm] at this
assumption
def RelCnstr.isUnsatCoeff (k : Int) : RelCnstr Bool
| .eq p => p.divCoeffs k && k > 0 && cmod p.getConst k < 0
| .le _ => false
private theorem contra {a b k : Int} (h₀ : 0 < k) (h₁ : -k < b) (h₂ : b < 0) (h₃ : a*k + b = 0) : False := by
have : b = -a*k := by
rw [ Int.neg_eq_of_add_eq_zero h₃, Int.neg_mul]
rw [this, Int.neg_mul] at h₁ h₂
replace h₁ := Int.lt_of_neg_lt_neg h₁
replace h₂ : -(a*k) < -0 := h₂
replace h₂ := Int.lt_of_neg_lt_neg h₂
replace h₁ : a * k < 1 * k := by simp [h₁]
replace h₁ : a < 1 := Int.lt_of_mul_lt_mul_right h₁ (Int.le_of_lt h₀)
replace h₂ : 0 * k < a * k := by simp [h₂]
replace h₂ : 0 < a := Int.lt_of_mul_lt_mul_right h₂ (Int.le_of_lt h₀)
replace h₂ : 1 a := h₂
have : (1 : Int) < 1 := Int.lt_of_le_of_lt h₂ h₁
contradiction
private theorem RelCnstr.eq_false (ctx : Context) (p : Poly) (k : Int) : p.divCoeffs k k > 0 cmod p.getConst k < 0 (RelCnstr.eq p).denote ctx = False := by
simp
intro h₁ h₂ h₃ h
have hnz : k 0 := by intro h; rw [h] at h₂; contradiction
have := Poly.denote_div_eq_of_divCoeffs ctx p k h₁
rw [h] at this
have low := cmod_gt_of_pos p.getConst h₂
have high := h₃
exact contra h₂ low high this
theorem RawRelCnstr.eq_false_of_isUnsat_coeff (ctx : Context) (c : RawRelCnstr) (k : Int) : c.norm.isUnsatCoeff k c.denote ctx = False := by
intro h
cases c <;> simp [norm, RelCnstr.isUnsatCoeff] at h
next e₁ e₂ =>
have h₁, h₂, h₃ := h
have := RelCnstr.eq_false ctx _ _ h₁ h₂ h₃
simp at this
simp
intro he
simp [he] at this
def RelCnstr.isValid : RelCnstr Bool
| .eq (.num k) => k == 0
| .eq _ => false
| .le (.num k) => k 0
| .le _ => false
theorem RelCnstr.eq_true_of_isValid (ctx : Context) (c : RelCnstr) : c.isValid c.denote ctx = True := by
unfold isValid <;> split <;> simp
theorem RawRelCnstr.eq_true_of_isValid (ctx : Context) (c : RawRelCnstr) (h : c.norm.isValid) : c.denote ctx = True := by
have := RelCnstr.eq_true_of_isValid ctx c.norm h
rw [RawRelCnstr.denote_norm] at this
assumption
private def gcd (a b : Int) : Int :=
Int.ofNat <| Int.gcd a b
private theorem gcd_dvd_left (a b : Int) : gcd a b a := by
simp [gcd, Int.gcd_dvd_left]
private theorem gcd_dvd_right (a b : Int) : gcd a b b := by
simp [gcd, Int.gcd_dvd_right]
private theorem gcd_dvd_step {k a b x : Int} (h : k a*x + b) : gcd a k b := by
have h₁ : gcd a k a*x + b := Int.dvd_trans (gcd_dvd_right a k) h
have h₂ : gcd a k a*x := Int.dvd_trans (gcd_dvd_left a k) (Int.dvd_mul_right a x)
exact Int.dvd_iff_dvd_of_dvd_add h₁ |>.mp h₂
def Poly.gcdCoeffs : Poly Int Int
| .num _, k => k
| .add k' _ p, k => gcdCoeffs p (gcd k' k)
theorem Poly.gcd_dvd_const {ctx : Context} {p : Poly} {k : Int} (h : k p.denote ctx) : p.gcdCoeffs k p.getConst := by
induction p generalizing k <;> simp_all [gcdCoeffs]
next k' x p ih =>
rw [Int.add_comm] at h
exact ih (gcd_dvd_step h)
/-- Divibility constraint of the form `k p`. -/
structure DvdCnstr where
k : Int
p : Poly
def DvdCnstr.denote (ctx : Context) (c : DvdCnstr) : Prop :=
c.k c.p.denote ctx
def DvdCnstr.denote' (ctx : Context) (c : DvdCnstr) : Prop :=
c.k c.p.denote' ctx
theorem DvdCnstr.denote'_eq_denote (ctx : Context) (c : DvdCnstr) : c.denote' ctx = c.denote ctx := by
simp [denote', denote, Poly.denote'_eq_denote]
def DvdCnstr.isUnsat (c : DvdCnstr) : Bool :=
c.p.getConst % c.p.gcdCoeffs c.k != 0
def DvdCnstr.isEqv (c₁ c₂ : DvdCnstr) (k : Int) : Bool :=
k != 0 && c₁.k == k*c₂.k && c₁.p == c₂.p.mul k
def DvdCnstr.div (k' : Int) : DvdCnstr DvdCnstr
| { k, p } => { k := k / k', p := p.div k' }
private theorem not_dvd_of_not_mod_zero {a b : Int} (h : ¬ b % a = 0) : ¬ a b := by
intro h; have := Int.emod_eq_zero_of_dvd h; contradiction
def DvdCnstr.eq_false_of_isUnsat (ctx : Context) (c : DvdCnstr) : c.isUnsat c.denote ctx = False := by
rcases c with a, p
simp [isUnsat, denote]
intro h₁ h₂
have := Poly.gcd_dvd_const h₂
have := not_dvd_of_not_mod_zero h₁
contradiction
@[local simp] private theorem mul_dvd_mul_eq {a b c : Int} (hnz : a 0) : a * b a * c b c := by
constructor
· intro h
rcases h with k, h
rw [Int.mul_assoc a] at h
replace h := Int.eq_of_mul_eq_mul_left hnz h
exists k
· intro h
rcases h with k, h
exists k
rw [h, Int.mul_assoc]
@[local simp] theorem DvdCnstr.eq_of_isEqv (ctx : Context) (c₁ c₂ : DvdCnstr) (k : Int) (h : isEqv c₁ c₂ k) : c₁.denote ctx = c₂.denote ctx := by
rcases c₁ with a₁, e₁
rcases c₂ with a₂, e₂
simp [isEqv] at h
rcases h with h₁, h₂, h₃
replace h₃ := congrArg (Poly.denote ctx) h₃
simp at h₃
simp [denote, *]
/-- Raw divisibility constraint of the form `k e`. -/
structure RawDvdCnstr where
k : Int
e : Expr
deriving BEq
def RawDvdCnstr.denote (ctx : Context) (c : RawDvdCnstr) : Prop :=
c.k c.e.denote ctx
def RawDvdCnstr.norm (c : RawDvdCnstr) : DvdCnstr :=
{ k := c.k, p := c.e.toPoly }
@[simp] theorem RawDvdCnstr.denote_norm_eq (ctx : Context) (c : RawDvdCnstr) : c.denote ctx = c.norm.denote ctx := by
simp [norm, denote, DvdCnstr.denote]
def RawDvdCnstr.isEqv (c : RawDvdCnstr) (c' : DvdCnstr) (k : Int) : Bool :=
c.norm.isEqv c' k
def RawDvdCnstr.isUnsat (c : RawDvdCnstr) : Bool :=
c.norm.isUnsat
theorem RawDvdCnstr.eq_of_isEqv (ctx : Context) (c : RawDvdCnstr) (c' : DvdCnstr) (k : Int) (h : isEqv c c' k) : c.denote ctx = c'.denote' ctx := by
simp [DvdCnstr.eq_of_isEqv ctx c.norm c' k h, DvdCnstr.denote'_eq_denote]
theorem RawDvdCnstr.eq_false_of_isUnsat (ctx : Context) (c : RawDvdCnstr) (h : c.isUnsat) : c.denote ctx = False := by
simp [DvdCnstr.eq_false_of_isUnsat ctx c.norm h]
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by
apply propext; constructor
· intro h; exact Int.add_one_le_of_lt (Int.lt_of_not_ge h)
· exact Int.not_le_of_gt
theorem Int.not_ge_eq (a b : Int) : (¬a b) = (a + 1 b) := by
apply Int.not_le_eq
theorem Int.not_lt_eq (a b : Int) : (¬a < b) = (b a) := by
apply propext; constructor
· intro h; simp [Int.not_lt] at h; assumption
· intro h; apply Int.not_le_of_gt; simp [Int.lt_add_one_iff, *]
theorem Int.not_gt_eq (a b : Int) : (¬a > b) = (a b) := by
apply Int.not_lt_eq

View File

@@ -6,6 +6,7 @@ Authors: Mario Carneiro
prelude
import Init.Data.List.Count
import Init.Data.Subtype
import Init.BinderNameHint
namespace List
@@ -238,6 +239,8 @@ theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h
· simp_all
· simp_all
set_option linter.deprecated false in
@[deprecated List.getElem?_pmap (since := "2025-02-12")]
theorem get?_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) (n : Nat) :
get? (pmap f l h) n = Option.pmap f (get? l n) fun x H => h x (mem_of_get? H) := by
simp only [get?_eq_getElem?]
@@ -258,6 +261,7 @@ theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h
· simp
· simp [hl]
@[deprecated getElem_pmap (since := "2025-02-13")]
theorem get_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) {n : Nat}
(hn : n < (pmap f l h).length) :
get (pmap f l h) n, hn =
@@ -416,7 +420,12 @@ theorem attachWith_map {l : List α} (f : α → β) {P : β → Prop} {H : ∀
fun x, h => f x, h := by
induction l <;> simp [*]
theorem map_attachWith {l : List α} {P : α Prop} {H : (a : α), a l P a}
@[simp] theorem map_attachWith {l : List α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f = l.attach.map fun x, h => f x, H _ h := by
induction l <;> simp_all
theorem map_attachWith_eq_pmap {l : List α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f =
l.pmap (fun a (h : a l P a) => f a, H _ h.1) (fun a h => h, H a h) := by
@@ -428,7 +437,7 @@ theorem map_attachWith {l : List α} {P : α → Prop} {H : ∀ (a : α), a ∈
simp
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
theorem map_attach {l : List α} (f : { x // x l } β) :
theorem map_attach_eq_pmap {l : List α} (f : { x // x l } β) :
l.attach.map f = l.pmap (fun a h => f a, h) (fun _ => id) := by
induction l with
| nil => rfl
@@ -437,6 +446,9 @@ theorem map_attach {l : List α} (f : { x // x ∈ l } → β) :
apply pmap_congr_left
simp
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
abbrev map_attach := @map_attach_eq_pmap
theorem attach_filterMap {l : List α} {f : α Option β} :
(l.filterMap f).attach = l.attach.filterMap
fun x, h => (f x).pbind (fun b m => some b, mem_filterMap.mpr x, h, m) := by
@@ -788,4 +800,66 @@ and simplifies these to the function directly taking the value.
(List.replicate n x).unattach = List.replicate n x.1 := by
simp [unattach, -map_subtype]
/-! ### Well-founded recursion preprocessing setup -/
@[wf_preprocess] theorem map_wfParam (xs : List α) (f : α β) :
(wfParam xs).map f = xs.attach.unattach.map f := by
simp [wfParam]
@[wf_preprocess] theorem map_unattach (P : α Prop) (xs : List (Subtype P)) (f : α β) :
xs.unattach.map f = xs.map fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[wf_preprocess] theorem foldl_wfParam (xs : List α) (f : β α β) (x : β) :
(wfParam xs).foldl f x = xs.attach.unattach.foldl f x := by
simp [wfParam]
@[wf_preprocess] theorem foldl_unattach (P : α Prop) (xs : List (Subtype P)) (f : β α β) (x : β):
xs.unattach.foldl f x = xs.foldl (fun s x, h =>
binderNameHint s f <| binderNameHint x (f s) <| binderNameHint h () <| f s (wfParam x)) x := by
simp [wfParam]
@[wf_preprocess] theorem foldr_wfParam (xs : List α) (f : α β β) (x : β) :
(wfParam xs).foldr f x = xs.attach.unattach.foldr f x := by
simp [wfParam]
@[wf_preprocess] theorem foldr_unattach (P : α Prop) (xs : List (Subtype P)) (f : α β β) (x : β):
xs.unattach.foldr f x = xs.foldr (fun x, h s =>
binderNameHint x f <| binderNameHint s (f x) <| binderNameHint h () <| f (wfParam x) s) x := by
simp [wfParam]
@[wf_preprocess] theorem filter_wfParam (xs : List α) (f : α Bool) :
(wfParam xs).filter f = xs.attach.unattach.filter f:= by
simp [wfParam]
@[wf_preprocess] theorem filter_unattach (P : α Prop) (xs : List (Subtype P)) (f : α Bool) :
xs.unattach.filter f = (xs.filter (fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x))).unattach := by
simp [wfParam]
@[wf_preprocess] theorem reverse_wfParam (xs : List α) :
(wfParam xs).reverse = xs.attach.unattach.reverse := by simp [wfParam]
@[wf_preprocess] theorem reverse_unattach (P : α Prop) (xs : List (Subtype P)) :
xs.unattach.reverse = xs.reverse.unattach := by simp
@[wf_preprocess] theorem filterMap_wfParam (xs : List α) (f : α Option β) :
(wfParam xs).filterMap f = xs.attach.unattach.filterMap f := by
simp [wfParam]
@[wf_preprocess] theorem filterMap_unattach (P : α Prop) (xs : List (Subtype P)) (f : α Option β) :
xs.unattach.filterMap f = xs.filterMap fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[wf_preprocess] theorem flatMap_wfParam (xs : List α) (f : α List β) :
(wfParam xs).flatMap f = xs.attach.unattach.flatMap f := by
simp [wfParam]
@[wf_preprocess] theorem flatMap_unattach (P : α Prop) (xs : List (Subtype P)) (f : α List β) :
xs.unattach.flatMap f = xs.flatMap fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
end List

View File

@@ -225,54 +225,27 @@ def lex [BEq α] (l₁ l₂ : List α) (lt : αα → Bool := by exact (·
| _, [] => false
| a :: as, b :: bs => lt a b || (a == b && lex as bs lt)
@[simp] theorem lex_nil_nil [BEq α] : lex ([] : List α) [] lt = false := rfl
@[simp] theorem lex_nil_cons [BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true := rfl
@[simp] theorem lex_cons_nil [BEq α] {a} {as : List α} : lex (a :: as) [] lt = false := rfl
@[simp] theorem lex_cons_cons [BEq α] {a b} {as bs : List α} :
theorem nil_lex_nil [BEq α] : lex ([] : List α) [] lt = false := rfl
@[simp] theorem nil_lex_cons [BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true := rfl
theorem cons_lex_nil [BEq α] {a} {as : List α} : lex (a :: as) [] lt = false := rfl
@[simp] theorem cons_lex_cons [BEq α] {a b} {as bs : List α} :
lex (a :: as) (b :: bs) lt = (lt a b || (a == b && lex as bs lt)) := rfl
@[simp] theorem lex_nil [BEq α] {as : List α} : lex as [] lt = false := by
cases as <;> simp [nil_lex_nil, cons_lex_nil]
@[deprecated nil_lex_nil (since := "2025-02-10")]
theorem lex_nil_nil [BEq α] : lex ([] : List α) [] lt = false := rfl
@[deprecated nil_lex_cons (since := "2025-02-10")]
theorem lex_nil_cons [BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true := rfl
@[deprecated cons_lex_nil (since := "2025-02-10")]
theorem lex_cons_nil [BEq α] {a} {as : List α} : lex (a :: as) [] lt = false := rfl
@[deprecated cons_lex_cons (since := "2025-02-10")]
theorem lex_cons_cons [BEq α] {a b} {as bs : List α} :
lex (a :: as) (b :: bs) lt = (lt a b || (a == b && lex as bs lt)) := rfl
/-! ## Alternative getters -/
/-! ### get? -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
Also see `get`, `getD` and `get!`.
-/
def get? : (as : List α) (i : Nat) Option α
| a::_, 0 => some a
| _::as, n+1 => get? as n
| _, _ => none
@[simp] theorem get?_nil : @get? α [] n = none := rfl
@[simp] theorem get?_cons_zero : @get? α (a::l) 0 = some a := rfl
@[simp] theorem get?_cons_succ : @get? α (a::l) (n+1) = get? l n := rfl
theorem ext_get? : {l₁ l₂ : List α}, ( n, l₁.get? n = l₂.get? n) l₁ = l₂
| [], [], _ => rfl
| _ :: _, [], h => nomatch h 0
| [], _ :: _, h => nomatch h 0
| a :: l₁, a' :: l₂, h => by
have h0 : some a = some a' := h 0
injection h0 with aa; simp only [aa, ext_get? fun n => h (n+1)]
/-! ### getD -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
See also `get?` and `get!`.
-/
def getD (as : List α) (i : Nat) (fallback : α) : α :=
(as.get? i).getD fallback
@[simp] theorem getD_nil : getD [] n d = d := rfl
@[simp] theorem getD_cons_zero : getD (x :: xs) 0 d = x := rfl
@[simp] theorem getD_cons_succ : getD (x :: xs) (n + 1) d = getD xs n d := rfl
/-! ### getLast -/
/--

View File

@@ -14,6 +14,53 @@ namespace List
/-! ## Alternative getters -/
/-! ### get? -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
Also see `get`, `getD` and `get!`.
-/
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
def get? : (as : List α) (i : Nat) Option α
| a::_, 0 => some a
| _::as, n+1 => get? as n
| _, _ => none
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_nil : @get? α [] n = none := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_cons_zero : @get? α (a::l) 0 = some a := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_cons_succ : @get? α (a::l) (n+1) = get? l n := rfl
set_option linter.deprecated false in
@[deprecated "Use `List.ext_getElem?`." (since := "2025-02-12")]
theorem ext_get? : {l₁ l₂ : List α}, ( n, l₁.get? n = l₂.get? n) l₁ = l₂
| [], [], _ => rfl
| _ :: _, [], h => nomatch h 0
| [], _ :: _, h => nomatch h 0
| a :: l₁, a' :: l₂, h => by
have h0 : some a = some a' := h 0
injection h0 with aa; simp only [aa, ext_get? fun n => h (n+1)]
/-! ### getD -/
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
See also `get?` and `get!`.
-/
def getD (as : List α) (i : Nat) (fallback : α) : α :=
as[i]?.getD fallback
@[simp] theorem getD_nil : getD [] n d = d := rfl
/-! ### get! -/
/--
@@ -22,14 +69,21 @@ Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function panics when executed, and returns
`default`. See `get?` and `getD` for safer alternatives.
-/
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
def get! [Inhabited α] : (as : List α) (i : Nat) α
| a::_, 0 => a
| _::as, n+1 => get! as n
| _, _ => panic! "invalid index"
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_nil [Inhabited α] (n : Nat) : [].get! n = (default : α) := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_cons_succ [Inhabited α] (l : List α) (a : α) (n : Nat) :
(a::l).get! (n+1) = get! l n := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a := rfl
/-! ### getLast! -/
@@ -170,23 +224,24 @@ theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length ≤ i
induction as generalizing i with
| nil => trivial
| cons a as ih =>
cases i with simp [get, Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h₁
cases i with simp [Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h₁
| succ i => apply ih; simp [h₁]
@[deprecated "Deprecated without replacement." (since := "2025-02-13")]
theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.length) : (as ++ [a] : List _).get i = a := by
cases i; rename_i i h'
induction as generalizing i with
| nil => cases i with
| zero => simp [List.get]
| succ => simp_arith at h'
| succ => simp +arith at h'
| cons a as ih =>
cases i with simp at h
| succ i => apply ih; simp [h]
theorem sizeOf_lt_of_mem [SizeOf α] {as : List α} (h : a as) : sizeOf a < sizeOf as := by
induction h with
| head => simp_arith
| tail _ _ ih => exact Nat.lt_trans ih (by simp_arith)
| head => simp +arith
| tail _ _ ih => exact Nat.lt_trans ih (by simp +arith)
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that
`sizeOf a < sizeOf as` when `a ∈ as`, which is useful for well founded recursions
@@ -197,7 +252,7 @@ macro "sizeOf_list_dec" : tactic =>
| with_reducible
apply Nat.lt_of_lt_of_le (sizeOf_lt_of_mem ?h)
case' h => assumption
simp_arith)
simp +arith)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| sizeOf_list_dec)
@@ -211,8 +266,8 @@ theorem append_cancel_left {as bs cs : List α} (h : as ++ bs = as ++ cs) : bs =
theorem append_cancel_right {as bs cs : List α} (h : as ++ bs = cs ++ bs) : as = cs := by
match as, cs with
| [], [] => rfl
| [], c::cs => have aux := congrArg length h; simp_arith at aux
| a::as, [] => have aux := congrArg length h; simp_arith at aux
| [], c::cs => have aux := congrArg length h; simp +arith at aux
| a::as, [] => have aux := congrArg length h; simp +arith at aux
| a::as, c::cs => injection h with h₁ h₂; subst h₁; rw [append_cancel_right h₂]
@[simp] theorem append_cancel_left_eq (as bs cs : List α) : (as ++ bs = as ++ cs) = (bs = cs) := by
@@ -227,11 +282,11 @@ theorem append_cancel_right {as bs cs : List α} (h : as ++ bs = cs ++ bs) : as
theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
match as, i with
| a::as, 0, _ => simp_arith [get]
| a::as, 0, _ => simp +arith [get]
| a::as, i+1, h =>
have ih := sizeOf_get as i, Nat.le_of_succ_le_succ h
apply Nat.lt_trans ih
simp_arith
simp +arith
theorem not_lex_antisymm [DecidableEq α] {r : α α Prop} [DecidableRel r]
(antisymm : x y : α, ¬ r x y ¬ r y x x = y)

View File

@@ -128,7 +128,7 @@ Applies the monadic function `f` on every element `x` in the list, left-to-right
results `y` for which `f x` returns `some y`.
-/
@[inline]
def filterMapM {m : Type u Type v} [Monad m] {α β : Type u} (f : α m (Option β)) (as : List α) : m (List β) :=
def filterMapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (Option β)) (as : List α) : m (List β) :=
let rec @[specialize] loop
| [], bs => pure bs.reverse
| a :: as, bs => do
@@ -161,7 +161,7 @@ foldlM f x₀ [a, b, c] = do
```
-/
@[specialize]
protected def foldlM {m : Type u Type v} [Monad m] {s : Type u} {α : Type w} : (f : s α m s) (init : s) List α m s
def foldlM {m : Type u Type v} [Monad m] {s : Type u} {α : Type w} : (f : s α m s) (init : s) List α m s
| _, s, [] => pure s
| f, s, a :: as => do
let s' f s a

View File

@@ -577,10 +577,6 @@ theorem findIdx_getElem {xs : List α} {w : xs.findIdx p < xs.length} :
p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
@[deprecated findIdx_of_getElem?_eq_some (since := "2024-08-12")]
theorem findIdx_of_get?_eq_some {xs : List α} (w : xs.get? (xs.findIdx p) = some y) : p y :=
findIdx_of_getElem?_eq_some (by simpa using w)
@[deprecated findIdx_getElem (since := "2024-08-12")]
theorem findIdx_get {xs : List α} {w : xs.findIdx p < xs.length} :
p (xs.get xs.findIdx p, w) :=
@@ -603,11 +599,6 @@ theorem findIdx_getElem?_eq_getElem_of_exists {xs : List α} (h : ∃ x ∈ xs,
xs[xs.findIdx p]? = some (xs[xs.findIdx p]'(xs.findIdx_lt_length_of_exists h)) :=
getElem?_eq_getElem (findIdx_lt_length_of_exists h)
@[deprecated findIdx_getElem?_eq_getElem_of_exists (since := "2024-08-12")]
theorem findIdx_get?_eq_get_of_exists {xs : List α} (h : x xs, p x) :
xs.get? (xs.findIdx p) = some (xs.get xs.findIdx p, xs.findIdx_lt_length_of_exists h) :=
get?_eq_get (findIdx_lt_length_of_exists h)
@[simp]
theorem findIdx_eq_length {p : α Bool} {xs : List α} :
xs.findIdx p = xs.length x xs, p x = false := by

View File

@@ -167,51 +167,38 @@ We simplify `l.get i` to `l[i.1]'i.2` and `l.get? i` to `l[i]?`.
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_none : {l : List α} {n}, length l n l.get? n = none
| [], _, _ => rfl
| _ :: l, _+1, h => get?_eq_none (l := l) <| Nat.le_of_succ_le_succ h
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_get : {l : List α} {n} (h : n < l.length), l.get? n = some (get l n, h)
| _ :: _, 0, _ => rfl
| _ :: l, _+1, _ => get?_eq_get (l := l) _
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_some_iff : l.get? n = some a h, get l n, h = a :=
fun e =>
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_eq_none hn e
this, by rwa [get?_eq_get this, Option.some.injEq] at e,
fun _, e => e get?_eq_get _
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_none_iff : l.get? n = none length l n :=
fun e => Nat.ge_of_not_lt (fun h' => by cases e get?_eq_some_iff.2 h', rfl), get?_eq_none
@[simp] theorem get?_eq_getElem? (l : List α) (i : Nat) : l.get? i = l[i]? := by
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), simp]
theorem get?_eq_getElem? (l : List α) (i : Nat) : l.get? i = l[i]? := by
simp only [getElem?_def]; split
· exact (get?_eq_get _)
· exact (get?_eq_none_iff.2 <| Nat.not_lt.1 _)
/-! ### getD
We simplify away `getD`, replacing `getD l n a` with `(l[n]?).getD a`.
Because of this, there is only minimal API for `getD`.
-/
@[simp] theorem getD_eq_getElem?_getD (l) (i) (a : α) : getD l i a = (l[i]?).getD a := by
simp [getD]
/-! ### get!
We simplify `l.get! i` to `l[i]!`.
-/
theorem get!_eq_getD [Inhabited α] : (l : List α) i, l.get! i = l.getD i default
| [], _ => rfl
| _a::_, 0 => rfl
| _a::l, n+1 => get!_eq_getD l n
@[simp] theorem get!_eq_getElem! [Inhabited α] (l : List α) (i) : l.get! i = l[i]! := by
simp [get!_eq_getD]
rfl
/-! ### getElem!
We simplify `l[i]!` to `(l[i]?).getD default`.
@@ -226,8 +213,26 @@ We simplify `l[i]!` to `(l[i]?).getD default`.
/-! ### getElem? and getElem -/
@[simp] theorem getElem?_eq_none_iff : l[i]? = none length l i := by
simp only [ get?_eq_getElem?, get?_eq_none_iff]
@[simp] theorem getElem?_nil {i : Nat} : ([] : List α)[i]? = none := rfl
theorem getElem_cons {l : List α} (w : i < (a :: l).length) :
(a :: l)[i] =
if h : i = 0 then a else l[i-1]'(match i, h with | i+1, _ => succ_lt_succ_iff.mp w) := by
cases i <;> simp
theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by
simp [getElem?]
@[simp] theorem getElem?_cons_succ {l : List α} : (a::l)[i+1]? = l[i]? := by
simp [getElem?, decidableGetElem?, Nat.succ_lt_succ_iff]
theorem getElem?_cons : (a :: l)[i]? = if i = 0 then some a else l[i-1]? := by
cases i <;> simp [getElem?_cons_zero]
@[simp] theorem getElem?_eq_none_iff : l[i]? = none length l i :=
match l with
| [] => by simp
| _ :: l => by simp
@[simp] theorem none_eq_getElem?_iff {l : List α} {i : Nat} : none = l[i]? length l i := by
simp [eq_comm (a := none)]
@@ -237,8 +242,15 @@ theorem getElem?_eq_none (h : length l ≤ i) : l[i]? = none := getElem?_eq_none
@[simp] theorem getElem?_eq_getElem {l : List α} {i} (h : i < l.length) : l[i]? = some l[i] :=
getElem?_pos ..
theorem getElem?_eq_some_iff {l : List α} : l[i]? = some a h : i < l.length, l[i] = a := by
simp only [ get?_eq_getElem?, get?_eq_some_iff, get_eq_getElem]
theorem getElem?_eq_some_iff {l : List α} : l[i]? = some a h : i < l.length, l[i] = a :=
match l with
| [] => by simp
| _ :: l => by
simp only [getElem?_cons, length_cons]
split <;> rename_i h
· simp_all
· match i, h with
| i + 1, h => simp [getElem?_eq_some_iff, Nat.succ_lt_succ_iff]
theorem some_eq_getElem?_iff {l : List α} : some a = l[i]? h : i < l.length, l[i] = a := by
rw [eq_comm, getElem?_eq_some_iff]
@@ -267,22 +279,6 @@ theorem getD_getElem? (l : List α) (i : Nat) (d : α) :
have p : i l.length := Nat.le_of_not_gt h
simp [getElem?_eq_none p, h]
@[simp] theorem getElem?_nil {i : Nat} : ([] : List α)[i]? = none := rfl
theorem getElem_cons {l : List α} (w : i < (a :: l).length) :
(a :: l)[i] =
if h : i = 0 then a else l[i-1]'(match i, h with | i+1, _ => succ_lt_succ_iff.mp w) := by
cases i <;> simp
theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
@[simp] theorem getElem?_cons_succ {l : List α} : (a::l)[i+1]? = l[i]? := by
simp only [ get?_eq_getElem?]
rfl
theorem getElem?_cons : (a :: l)[i]? = if i = 0 then some a else l[i-1]? := by
cases i <;> simp
@[simp] theorem getElem_singleton (a : α) (h : i < 1) : [a][i] = a :=
match i, h with
| 0, _ => rfl
@@ -304,7 +300,13 @@ theorem getElem_zero {l : List α} (h : 0 < l.length) : l[0] = l.head (length_po
| _ :: _, _ => rfl
@[ext] theorem ext_getElem? {l₁ l₂ : List α} (h : i : Nat, l₁[i]? = l₂[i]?) : l₁ = l₂ :=
ext_get? fun n => by simp_all
match l₁, l₂, h with
| [], [], _ => rfl
| _ :: _, [], h => by simpa using h 0
| [], _ :: _, h => by simpa using h 0
| a :: l₁, a' :: l₂, h => by
have h0 : some a = some a' := by simpa using h 0
injection h0 with aa; simp only [aa, ext_getElem? fun n => by simpa using h (n+1)]
theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
(h : (i : Nat) (h₁ : i < l₁.length) (h₂ : i < l₂.length), l₁[i]'h₁ = l₂[i]'h₂) : l₁ = l₂ :=
@@ -322,6 +324,35 @@ theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
theorem getElem?_concat_length (l : List α) (a : α) : (l ++ [a])[l.length]? = some a := by
simp
/-! ### getD
We simplify away `getD`, replacing `getD l n a` with `(l[n]?).getD a`.
Because of this, there is only minimal API for `getD`.
-/
@[simp] theorem getD_eq_getElem?_getD (l) (i) (a : α) : getD l i a = (l[i]?).getD a := by
simp [getD]
theorem getD_cons_zero : getD (x :: xs) 0 d = x := by simp
theorem getD_cons_succ : getD (x :: xs) (n + 1) d = getD xs n d := by simp
/-! ### get!
We simplify `l.get! i` to `l[i]!`.
-/
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_eq_getD [Inhabited α] : (l : List α) i, l.get! i = l.getD i default
| [], _ => rfl
| _a::_, 0 => by simp [get!]
| _a::l, n+1 => by simpa using get!_eq_getD l n
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12"), simp]
theorem get!_eq_getElem! [Inhabited α] (l : List α) (i) : l.get! i = l[i]! := by
simp [get!_eq_getD]
/-! ### mem -/
@[simp] theorem not_mem_nil (a : α) : ¬ a [] := nofun
@@ -771,7 +802,7 @@ theorem getLast_eq_getElem : ∀ (l : List α) (h : l ≠ []),
| a :: l => exact Nat.le_refl _)
| [_], _ => rfl
| _ :: _ :: _, _ => by
simp [getLast, get, Nat.succ_sub_succ, getLast_eq_getElem]
simp [getLast, Nat.succ_sub_succ, getLast_eq_getElem]
theorem getElem_length_sub_one_eq_getLast (l : List α) (h : l.length - 1 < l.length) :
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
@@ -844,10 +875,6 @@ theorem getLast?_cons {a : α} : (a::l).getLast? = l.getLast?.getD a := by
@[simp] theorem getLast?_cons_cons : (a :: b :: l).getLast? = (b :: l).getLast? := by
simp [getLast?_cons]
@[deprecated getLast?_eq_getElem? (since := "2024-07-07")]
theorem getLast?_eq_get? (l : List α) : getLast? l = l.get? (l.length - 1) := by
simp [getLast?_eq_getElem?]
theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
simp [getLast?_eq_getElem?, Nat.succ_sub_succ]
@@ -1344,6 +1371,11 @@ theorem head_filter_of_pos {p : α → Bool} {l : List α} (w : l ≠ []) (h : p
theorem filterMap_eq_map (f : α β) : filterMap (some f) = map f := by
funext l; induction l <;> simp [*, filterMap_cons]
/-- Variant of `filterMap_eq_map` with `some ∘ f` expanded out to a lambda. -/
@[simp]
theorem filterMap_eq_map' (f : α β) : filterMap (fun x => some (f x)) = map f :=
filterMap_eq_map f
@[simp] theorem filterMap_some_fun : filterMap (some : α Option α) = id := by
funext l
erw [filterMap_eq_map]
@@ -1558,7 +1590,7 @@ theorem getElem_append_right' (l₁ : List α) {l₂ : List α} {i : Nat} (hi :
rw [getElem_append_right] <;> simp [*, le_add_left]
theorem getElem_of_append {l : List α} (eq : l = l₁ ++ a :: l₂) (h : l₁.length = i) :
l[i]'(eq h by simp_arith) = a := Option.some.inj <| by
l[i]'(eq h by simp +arith) = a := Option.some.inj <| by
rw [ getElem?_eq_getElem, eq, getElem?_append_right (h Nat.le_refl _), h]
simp
@@ -2363,6 +2395,9 @@ theorem mem_reverseAux {x : α} : ∀ {as bs}, x ∈ reverseAux as bs ↔ x ∈
theorem reverse_ne_nil_iff {xs : List α} : xs.reverse [] xs [] :=
not_congr reverse_eq_nil_iff
@[simp] theorem isEmpty_reverse {xs : List α} : xs.reverse.isEmpty = xs.isEmpty := by
cases xs <;> simp
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
theorem getElem?_reverse' : {l : List α} (i j), i + j + 1 = length l
l.reverse[i]? = l[j]?
@@ -2516,12 +2551,35 @@ theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
/-! ### foldl and foldr -/
@[simp] theorem foldr_cons_eq_append (l : List α) : l.foldr cons l' = l ++ l' := by
@[simp] theorem foldr_cons_eq_append (l : List α) (f : α β) (l' : List β) :
l.foldr (fun x y => f x :: y) l' = l.map f ++ l' := by
induction l <;> simp [*]
/-- Variant of `foldr_cons_eq_append` specalized to `f = id`. -/
@[simp] theorem foldr_cons_eq_append' (l l' : List β) :
l.foldr cons l' = l ++ l' := by
induction l <;> simp [*]
@[deprecated foldr_cons_eq_append (since := "2024-08-22")] abbrev foldr_self_append := @foldr_cons_eq_append
@[simp] theorem foldl_flip_cons_eq_append (l : List α) : l.foldl (fun x y => y :: x) l' = l.reverse ++ l' := by
@[simp] theorem foldl_flip_cons_eq_append (l : List α) (f : α β) (l' : List β) :
l.foldl (fun x y => f y :: x) l' = (l.map f).reverse ++ l' := by
induction l generalizing l' <;> simp [*]
@[simp] theorem foldr_append_eq_append (l : List α) (f : α List β) (l' : List β) :
l.foldr (f · ++ ·) l' = (l.map f).flatten ++ l' := by
induction l <;> simp [*]
@[simp] theorem foldl_append_eq_append (l : List α) (f : α List β) (l' : List β) :
l.foldl (· ++ f ·) l' = l' ++ (l.map f).flatten := by
induction l generalizing l'<;> simp [*]
@[simp] theorem foldr_flip_append_eq_append (l : List α) (f : α List β) (l' : List β) :
l.foldr (fun x y => y ++ f x) l' = l' ++ (l.map f).reverse.flatten := by
induction l generalizing l' <;> simp [*]
@[simp] theorem foldl_flip_append_eq_append (l : List α) (f : α List β) (l' : List β) :
l.foldl (fun x y => f y ++ x) l' = (l.map f).reverse.flatten ++ l' := by
induction l generalizing l' <;> simp [*]
theorem foldr_cons_nil (l : List α) : l.foldr cons [] = l := by simp
@@ -3371,6 +3429,8 @@ theorem get_cons_succ' {as : List α} {i : Fin as.length} :
theorem get_mk_zero : {l : List α} (h : 0 < l.length), l.get 0, h = l.head (length_pos.mp h)
| _::_, _ => rfl
set_option linter.deprecated false in
@[deprecated "Use `a[0]?` instead." (since := "2025-02-12")]
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
/--
@@ -3382,10 +3442,14 @@ such a rewrite, with `rw [get_of_eq h]`.
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
get l i = get l' i, h i.2 := by cases h; rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
theorem get!_of_get? [Inhabited α] : {l : List α} {n}, get? l n = some a get! l n = a
| _a::_, 0, rfl => rfl
| _::l, _+1, e => get!_of_get? (l := l) e
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead." (since := "2025-02-12")]
theorem get!_len_le [Inhabited α] : {l : List α} {n}, length l n l.get! n = (default : α)
| [], _, _ => rfl
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
@@ -3415,6 +3479,8 @@ theorem get_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, get l n = a := by
obtain n, h, e := getElem_of_mem h
exact n, h, e
set_option linter.deprecated false in
@[deprecated getElem?_of_mem (since := "2025-02-12")]
theorem get?_of_mem {a} {l : List α} (h : a l) : n, l.get? n = some a :=
let n, _, e := get_of_mem h; n, e get?_eq_get _
@@ -3422,12 +3488,16 @@ theorem get_mem : ∀ (l : List α) n, get l n ∈ l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (get_mem l ..)
set_option linter.deprecated false in
@[deprecated mem_of_getElem? (since := "2025-02-12")]
theorem mem_of_get? {l : List α} {n a} (e : l.get? n = some a) : a l :=
let _, e := get?_eq_some_iff.1 e; e get_mem ..
theorem mem_iff_get {a} {l : List α} : a l n, get l n = a :=
get_of_mem, fun _, e => e get_mem ..
set_option linter.deprecated false in
@[deprecated mem_iff_getElem? (since := "2025-02-12")]
theorem mem_iff_get? {a} {l : List α} : a l n, l.get? n = some a := by
simp [getElem?_eq_some_iff, Fin.exists_iff, mem_iff_get]
@@ -3449,7 +3519,6 @@ theorem join_map_filter (p : α → Bool) (l : List (List α)) :
@[deprecated flatten_eq_cons_iff (since := "2024-09-05")] abbrev join_eq_cons := @flatten_eq_cons_iff
@[deprecated flatten_eq_append_iff (since := "2024-09-05")] abbrev join_eq_append := @flatten_eq_append_iff
@[deprecated mem_of_getElem? (since := "2024-09-06")] abbrev getElem?_mem := @mem_of_getElem?
@[deprecated mem_of_get? (since := "2024-09-06")] abbrev get?_mem := @mem_of_get?
@[deprecated getElem_set_self (since := "2024-09-04")] abbrev getElem_set_eq := @getElem_set_self
@[deprecated getElem?_set_self (since := "2024-09-04")] abbrev getElem?_set_eq := @getElem?_set_self
@[deprecated set_eq_nil_iff (since := "2024-09-05")] abbrev set_eq_nil := @set_eq_nil_iff
@@ -3510,11 +3579,11 @@ theorem join_map_filter (p : α → Bool) (l : List (List α)) :
@[deprecated any_flatMap (since := "2024-10-16")] abbrev any_bind := @any_flatMap
@[deprecated all_flatMap (since := "2024-10-16")] abbrev all_bind := @all_flatMap
@[deprecated get?_eq_none (since := "2024-11-29")] abbrev get?_len_le := @get?_eq_none
@[deprecated get?_eq_none (since := "2024-11-29")] abbrev get?_len_le := @getElem?_eq_none
@[deprecated getElem?_eq_some_iff (since := "2024-11-29")]
abbrev getElem?_eq_some := @getElem?_eq_some_iff
@[deprecated get?_eq_some_iff (since := "2024-11-29")]
abbrev get?_eq_some := @get?_eq_some_iff
abbrev get?_eq_some := @getElem?_eq_some_iff
@[deprecated LawfulGetElem.getElem?_def (since := "2024-11-29")]
theorem getElem?_eq (l : List α) (i : Nat) :
l[i]? = if h : i < l.length then some l[i] else none :=

View File

@@ -48,7 +48,9 @@ instance ltIrrefl [LT α] [Std.Irrefl (· < · : αα → Prop)] : Std.Irre
@[simp] theorem le_nil [LT α] (l : List α) : l [] l = [] := not_nil_lex_iff
@[simp] theorem nil_lex_cons : Lex r [] (a :: l) := Lex.nil
-- This is named with a prime to avoid conflict with `lex [] (b :: bs) lt = true`.
-- Better naming for the `Lex` vs `lex` distinction would be welcome.
@[simp] theorem nil_lex_cons' : Lex r [] (a :: l) := Lex.nil
@[simp] theorem nil_lt_cons [LT α] (a : α) (l : List α) : [] < a :: l := Lex.nil
@@ -333,7 +335,7 @@ theorem lex_eq_true_iff_exists [BEq α] (lt : αα → Bool) :
cases l₂ with
| nil => simp [lex]
| cons b l₂ =>
simp [lex_cons_cons, Bool.or_eq_true, Bool.and_eq_true, ih, isEqv, length_cons]
simp [cons_lex_cons, Bool.or_eq_true, Bool.and_eq_true, ih, isEqv, length_cons]
constructor
· rintro (hab | hab, h₁, h₂ | i, h₁, h₂, w₁, w₂)
· exact .inr 0, by simp [hab]
@@ -397,7 +399,7 @@ theorem lex_eq_false_iff_exists [BEq α] [PartialEquivBEq α] (lt : αα
cases l₂ with
| nil => simp [lex]
| cons b l₂ =>
simp [lex_cons_cons, Bool.or_eq_false_iff, Bool.and_eq_false_imp, ih, isEqv,
simp [cons_lex_cons, Bool.or_eq_false_iff, Bool.and_eq_false_imp, ih, isEqv,
Bool.and_eq_true, length_cons]
constructor
· rintro hab, h

View File

@@ -11,6 +11,9 @@ import Init.Data.List.Attach
# Lemmas about `List.mapM` and `List.forM`.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
@@ -422,11 +425,21 @@ and simplifies these to the function directly taking the value.
| nil => simp
| cons a l ih => simp [ih, hf]
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : List α) (f : β α m β) :
(wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
simp [wfParam]
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : List (Subtype P)) (f : β α m β) :
xs.unattach.foldlM f = xs.foldlM fun b x, h =>
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
f b (wfParam x) := by
simp [wfParam]
/--
This lemma identifies monadic folds over lists of subtypes, where the function only depends on the value, not the proposition,
and simplifies these to the function directly taking the value.
-/
@[simp] theorem foldrM_subtype [Monad m] [LawfulMonad m]{p : α Prop} {l : List { x // p x }}
@[simp] theorem foldrM_subtype [Monad m] [LawfulMonad m] {p : α Prop} {l : List { x // p x }}
{f : { x // p x } β m β} {g : α β m β} {x : β}
(hf : x h b, f x, h b = g x b) :
l.foldrM f x = l.unattach.foldrM g x := by
@@ -439,6 +452,16 @@ and simplifies these to the function directly taking the value.
funext b
simp [hf]
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α β m β) :
(wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
simp [wfParam]
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : List (Subtype P)) (f : α β m β) :
xs.unattach.foldrM f = xs.foldrM fun x, h b =>
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
f (wfParam x) b := by
simp [wfParam]
/--
This lemma identifies monadic maps over lists of subtypes, where the function only depends on the value, not the proposition,
and simplifies these to the function directly taking the value.
@@ -452,6 +475,15 @@ and simplifies these to the function directly taking the value.
| nil => simp
| cons a l ih => simp [ih, hf]
@[wf_preprocess] theorem mapM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α m β) :
(wfParam xs).mapM f = xs.attach.unattach.mapM f := by
simp [wfParam]
@[wf_preprocess] theorem mapM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : List (Subtype P)) (f : α m β) :
xs.unattach.mapM f = xs.mapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[simp] theorem filterMapM_subtype [Monad m] [LawfulMonad m] {p : α Prop} {l : List { x // p x }}
{f : { x // p x } m (Option β)} {g : α m (Option β)} (hf : x h, f x, h = g x) :
l.filterMapM f = l.unattach.filterMapM g := by
@@ -460,6 +492,17 @@ and simplifies these to the function directly taking the value.
| nil => simp
| cons a l ih => simp [ih, hf, filterMapM_cons]
@[wf_preprocess] theorem filterMapM_wfParam [Monad m] [LawfulMonad m]
(xs : List α) (f : α m (Option β)) :
(wfParam xs).filterMapM f = xs.attach.unattach.filterMapM f := by
simp [wfParam]
@[wf_preprocess] theorem filterMapM_unattach [Monad m] [LawfulMonad m]
(P : α Prop) (xs : List (Subtype P)) (f : α m (Option β)) :
xs.unattach.filterMapM f = xs.filterMapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
@[simp] theorem flatMapM_subtype [Monad m] [LawfulMonad m] {p : α Prop} {l : List { x // p x }}
{f : { x // p x } m (List β)} {g : α m (List β)} (hf : x h, f x, h = g x) :
(l.flatMapM f) = l.unattach.flatMapM g := by
@@ -468,4 +511,15 @@ and simplifies these to the function directly taking the value.
| nil => simp
| cons a l ih => simp [ih, hf]
@[wf_preprocess] theorem flatMapM_wfParam [Monad m] [LawfulMonad m]
(xs : List α) (f : α m (List β)) :
(wfParam xs).flatMapM f = xs.attach.unattach.flatMapM f := by
simp [wfParam]
@[wf_preprocess] theorem flatMapM_unattach [Monad m] [LawfulMonad m]
(P : α Prop) (xs : List (Subtype P)) (f : α m (List β)) :
xs.unattach.flatMapM f = xs.flatMapM fun x, h =>
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
simp [wfParam]
end List

View File

@@ -132,7 +132,7 @@ theorem getElem_insertIdx_of_lt {l : List α} {x : α} {n k : Nat} (hn : k < n)
| nil => simp
| cons _ _=>
cases k
· simp [get]
· simp
· rw [Nat.succ_lt_succ_iff] at hn
simpa using ih hn _

View File

@@ -368,7 +368,7 @@ theorem mk_mem_zipIdx_iff_le_and_getElem?_sub {k i : Nat} {x : α} {l : List α}
simp [mk_add_mem_zipIdx_iff_getElem?, Nat.add_sub_cancel_left]
else
have : m, k + m i := by rintro _ rfl; simp at h
simp [h, mem_iff_get?, this]
simp [h, mem_iff_getElem?, this]
/-- Variant of `mk_mem_zipIdx_iff_le_and_getElem?_sub` specialized at `k = 0`,
to avoid the inequality and the subtraction. -/

View File

@@ -16,6 +16,8 @@ These are in a separate file from most of the list lemmas
as they required importing more lemmas about natural numbers, and use `omega`.
-/
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
@@ -146,20 +148,23 @@ theorem take_append {l₁ l₂ : List α} (i : Nat) :
rw [take_append_eq_append_take, take_of_length_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
@[simp]
theorem take_eq_take :
theorem take_eq_take_iff :
{l : List α} {i j : Nat}, l.take i = l.take j min i l.length = min j l.length
| [], i, j => by simp [Nat.min_zero]
| _ :: xs, 0, 0 => by simp
| x :: xs, i + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, 0, j + 1 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, i + 1, j + 1 => by simp [succ_min_succ, take_eq_take]
| x :: xs, i + 1, j + 1 => by simp [succ_min_succ, take_eq_take_iff]
@[deprecated take_eq_take_iff (since := "2025-02-16")]
abbrev take_eq_take := @take_eq_take_iff
theorem take_add (l : List α) (i j : Nat) : l.take (i + j) = l.take i ++ (l.drop i).take j := by
suffices take (i + j) (take i l ++ drop i l) = take i l ++ take j (drop i l) by
rw [take_append_drop] at this
assumption
rw [take_append_eq_append_take, take_of_length_le, append_right_inj]
· simp only [take_eq_take, length_take, length_drop]
· simp only [take_eq_take_iff, length_take, length_drop]
omega
apply Nat.le_trans (m := i)
· apply length_take_le
@@ -170,7 +175,7 @@ theorem take_one {l : List α} : l.take 1 = l.head?.toList := by
theorem take_eq_append_getElem_of_pos {i} {l : List α} (h₁ : 0 < i) (h₂ : i < l.length) : l.take i = l.take (i - 1) ++ [l[i - 1]] :=
match i, h₁ with
| i + 1, _ => take_succ_eq_append_getElem (n := i) (by omega)
| i + 1, _ => take_succ_eq_append_getElem (by omega)
theorem dropLast_take {i : Nat} {l : List α} (h : i < l.length) :
(l.take i).dropLast = l.take (i - 1) := by
@@ -348,11 +353,6 @@ theorem set_eq_take_append_cons_drop (l : List α) (i : Nat) (a : α) :
· rw [set_eq_of_length_le]
omega
theorem exists_of_set {i : Nat} {a' : α} {l : List α} (h : i < l.length) :
l₁ l₂, l = l₁ ++ l[i] :: l₂ l₁.length = i l.set i a' = l₁ ++ a' :: l₂ := by
refine l.take i, l.drop (i + 1), by simp, length_take_of_le (Nat.le_of_lt h), ?_
simp [set_eq_take_append_cons_drop, h]
theorem drop_set_of_lt (a : α) {i j : Nat} (l : List α)
(hnm : i < j) : drop j (l.set i a) = l.drop j :=
ext_getElem? fun k => by simpa only [getElem?_drop] using getElem?_set_ne (by omega)
@@ -472,6 +472,16 @@ theorem false_of_mem_take_findIdx {xs : List α} {p : α → Bool} (h : x ∈ xs
· simp
· rw [Nat.add_min_add_right]
@[simp] theorem min_findIdx_findIdx {xs : List α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx_cons, cond_eq_if, Bool.not_eq_eq_eq_not, Bool.not_true]
split <;> split <;> simp_all [Nat.add_min_add_right]
/-! ### findIdx? -/
@[simp] theorem findIdx?_take {xs : List α} {i : Nat} {p : α Bool} :
(xs.take i).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun j => j < i)) := by
induction xs generalizing i with
@@ -484,14 +494,6 @@ theorem false_of_mem_take_findIdx {xs : List α} {p : α → Bool} (h : x ∈ xs
· simp
· simp [ih, Option.guard_comp, Option.bind_map]
@[simp] theorem min_findIdx_findIdx {xs : List α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx_cons, cond_eq_if, Bool.not_eq_eq_eq_not, Bool.not_true]
split <;> split <;> simp_all [Nat.add_min_add_right]
/-! ### takeWhile -/
theorem takeWhile_eq_take_findIdx_not {xs : List α} {p : α Bool} :

View File

@@ -11,6 +11,9 @@ import Init.Data.Nat.Div.Basic
-/
set_option linter.missingDocs true -- keep it documented
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
open Decidable List
/--

View File

@@ -11,6 +11,9 @@ import Init.Data.Fin.Fold
# Theorems about `List.ofFn`
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
/--

View File

@@ -11,6 +11,9 @@ import Init.Data.List.Attach
# Lemmas about `List.Pairwise` and `List.Nodup`.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
@@ -169,7 +172,7 @@ theorem pairwise_flatten {L : List (List α)} :
simp only [flatten, pairwise_append, IH, mem_flatten, exists_imp, and_imp, forall_mem_cons,
pairwise_cons, and_assoc, and_congr_right_iff]
rw [and_comm, and_congr_left_iff]
intros; exact fun h a b c d e => h c d e a b, fun h c d e a b => h a b c d e
intros; exact fun h l' b c d e => h c d e l' b, fun h c d e l' b => h l' b c d e
@[deprecated pairwise_flatten (since := "2024-10-14")] abbrev pairwise_join := @pairwise_flatten
@@ -206,10 +209,10 @@ theorem pairwise_reverse {l : List α} :
simp
· exact fun _ => h, Or.inr h
theorem Pairwise.drop {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.drop n) :=
theorem Pairwise.drop {l : List α} {i : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.drop i) :=
h.sublist (drop_sublist _ _)
theorem Pairwise.take {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.take n) :=
theorem Pairwise.take {l : List α} {i : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.take i) :=
h.sublist (take_sublist _ _)
theorem pairwise_iff_forall_sublist : l.Pairwise R ( {a b}, [a,b] <+ l R a b) := by
@@ -231,9 +234,9 @@ theorem pairwise_iff_forall_sublist : l.Pairwise R ↔ (∀ {a b}, [a,b] <+ l
apply h; exact hab.cons _
theorem Pairwise.rel_of_mem_take_of_mem_drop
{l : List α} (h : l.Pairwise R) (hx : x l.take n) (hy : y l.drop n) : R x y := by
{l : List α} (h : l.Pairwise R) (hx : x l.take i) (hy : y l.drop i) : R x y := by
apply pairwise_iff_forall_sublist.mp h
rw [ take_append_drop n l, sublist_append_iff]
rw [ take_append_drop i l, sublist_append_iff]
refine [x], [y], rfl, by simpa, by simpa
theorem Pairwise.rel_of_mem_append
@@ -298,11 +301,10 @@ theorem getElem?_inj {xs : List α}
| i+1, 0 => ?_
| 0, j+1 => ?_
all_goals
simp only [get?_eq_getElem?, getElem?_cons_zero, getElem?_cons_succ] at h₂
simp only [getElem?_cons_zero, getElem?_cons_succ] at h₂
cases h₁; rename_i h' h
have := h x ?_ rfl; cases this
rw [mem_iff_get?]
simp only [get?_eq_getElem?]
rw [mem_iff_getElem?]
exact _, h₂; exact _ , h₂.symm
@[simp] theorem nodup_replicate {n : Nat} {a : α} :

View File

@@ -18,6 +18,9 @@ another.
The notation `~` is used for permutation equivalence.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
open Nat
namespace List
@@ -90,8 +93,8 @@ theorem Perm.append_left {t₁ t₂ : List α} : ∀ l : List α, t₁ ~ t₂
theorem Perm.append {l₁ l₂ t₁ t₂ : List α} (p₁ : l₁ ~ l₂) (p₂ : t₁ ~ t₂) : l₁ ++ t₁ ~ l₂ ++ t₂ :=
(p₁.append_right t₁).trans (p₂.append_left l₂)
theorem Perm.append_cons (a : α) {h h t t : List α} (p₁ : h ~ h) (p₂ : t ~ t) :
h ++ a :: t ~ h ++ a :: t := p₁.append (p₂.cons a)
theorem Perm.append_cons (a : α) {l l r r : List α} (p₁ : l ~ l) (p₂ : r ~ r) :
l ++ a :: r ~ l ++ a :: r := p₁.append (p₂.cons a)
@[simp] theorem perm_middle {a : α} : {l₁ l₂ : List α}, l₁ ++ a :: l₂ ~ a :: (l₁ ++ l₂)
| [], _ => .refl _
@@ -167,7 +170,7 @@ theorem Perm.singleton_eq (h : [a] ~ l) : [a] = l := singleton_perm.mp h
theorem singleton_perm_singleton {a b : α} : [a] ~ [b] a = b := by simp
theorem perm_cons_erase [DecidableEq α] {a : α} {l : List α} (h : a l) : l ~ a :: l.erase a :=
let _l₁, _l₂, _, e₁, e₂ := exists_erase_eq h
let _, _, _, e₁, e₂ := exists_erase_eq h
e₂ e₁ perm_middle
theorem Perm.filterMap (f : α Option β) {l₁ l₂ : List α} (p : l₁ ~ l₂) :
@@ -216,7 +219,7 @@ theorem exists_perm_sublist {l₁ l₂ l₂' : List α} (s : l₁ <+ l₂) (p :
| .cons₂ _ (.cons _ s) => exact y :: _, .rfl, (s.cons₂ _).cons _
| .cons₂ _ (.cons₂ _ s) => exact x :: y :: _, .swap .., (s.cons₂ _).cons₂ _
| trans _ _ IH₁ IH₂ =>
let m₁, pm, sm := IH₁ s
let _, pm, sm := IH₁ s
let r₁, pr, sr := IH₂ sm
exact r₁, pr.trans pm, sr
@@ -510,15 +513,15 @@ theorem Perm.eraseP (f : α → Bool) {l₁ l₂ : List α}
refine (IH₁ H).trans (IH₂ ((p₁.pairwise_iff ?_).1 H))
exact fun h h₁ h₂ => h h₂ h₁
theorem perm_insertIdx {α} (x : α) (l : List α) {n} (h : n l.length) :
insertIdx n x l ~ x :: l := by
induction l generalizing n with
theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i l.length) :
insertIdx i x l ~ x :: l := by
induction l generalizing i with
| nil =>
cases n with
cases i with
| zero => rfl
| succ => cases h
| cons _ _ ih =>
cases n with
cases i with
| zero => simp [insertIdx]
| succ =>
simp only [insertIdx, modifyTailIdx]

View File

@@ -14,6 +14,9 @@ Most of the results are deferred to `Data.Init.List.Nat.Range`, where more resul
natural arithmetic are available.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
@@ -71,7 +74,7 @@ theorem mem_range' : ∀{n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step *
rw [exists_comm]; simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
theorem getElem?_range' (s step) :
{m n : Nat}, m < n (range' s n step)[m]? = some (s + step * m)
{i j : Nat}, i < j (range' s j step)[i]? = some (s + step * i)
| 0, n + 1, _ => by simp [range'_succ]
| m + 1, n + 1, h => by
simp only [range'_succ, getElem?_cons_succ]
@@ -144,10 +147,10 @@ theorem range_loop_range' : ∀ s n : Nat, range.loop s (range' s n) = range' 0
theorem range_eq_range' (n : Nat) : range n = range' 0 n :=
(range_loop_range' n 0).trans <| by rw [Nat.zero_add]
theorem getElem?_range {m n : Nat} (h : m < n) : (range n)[m]? = some m := by
theorem getElem?_range {i j : Nat} (h : i < j) : (range j)[i]? = some i := by
simp [range_eq_range', getElem?_range' _ _ h]
@[simp] theorem getElem_range {n : Nat} (m) (h : m < (range n).length) : (range n)[m] = m := by
@[simp] theorem getElem_range {i : Nat} (j) (h : j < (range i).length) : (range i)[j] = j := by
simp [range_eq_range']
theorem range_succ_eq_map (n : Nat) : range (n + 1) = 0 :: map succ (range n) := by
@@ -220,7 +223,7 @@ theorem zipIdx_eq_nil_iff {l : List α} {n : Nat} : List.zipIdx l n = [] ↔ l =
@[simp]
theorem getElem?_zipIdx :
(l : List α) n m, (zipIdx l n)[m]? = l[m]?.map fun a => (a, n + m)
(l : List α) i j, (zipIdx l i)[j]? = l[j]?.map fun a => (a, i + j)
| [], _, _ => rfl
| _ :: _, _, 0 => by simp
| _ :: l, n, m + 1 => by
@@ -300,7 +303,7 @@ theorem enumFrom_length : ∀ {n} {l : List α}, (enumFrom n l).length = l.lengt
@[deprecated getElem?_zipIdx (since := "2025-01-21"), simp]
theorem getElem?_enumFrom :
n (l : List α) m, (enumFrom n l)[m]? = l[m]?.map fun a => (n + m, a)
i (l : List α) j, (enumFrom i l)[j]? = l[j]?.map fun a => (i + j, a)
| _, [], _ => rfl
| _, _ :: _, 0 => by simp
| n, _ :: l, m + 1 => by

View File

@@ -152,8 +152,8 @@ theorem cons_merge_cons (s : αα → Bool) (a b l r) :
| a::l, b::r =>
rw [cons_merge_cons]
split
· simp_arith [length_merge s l (b::r)]
· simp_arith [length_merge s (a::l) r]
· simp +arith [length_merge s l (b::r)]
· simp +arith [length_merge s (a::l) r]
/--
The elements of `merge le xs ys` are exactly the elements of `xs` and `ys`.

View File

@@ -11,6 +11,9 @@ import Init.Data.List.TakeDrop
# Lemmas about `List.Subset`, `List.Sublist`, `List.IsPrefix`, `List.IsSuffix`, and `List.IsInfix`.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
@@ -22,14 +25,14 @@ variable [BEq α]
@[simp] theorem isPrefixOf_cons₂_self [LawfulBEq α] {a : α} :
isPrefixOf (a::as) (a::bs) = isPrefixOf as bs := by simp [isPrefixOf_cons₂]
@[simp] theorem isPrefixOf_length_pos_nil {L : List α} (h : 0 < L.length) : isPrefixOf L [] = false := by
cases L <;> simp_all [isPrefixOf]
@[simp] theorem isPrefixOf_length_pos_nil {l : List α} (h : 0 < l.length) : isPrefixOf l [] = false := by
cases l <;> simp_all [isPrefixOf]
@[simp] theorem isPrefixOf_replicate {a : α} :
isPrefixOf l (replicate n a) = (decide (l.length n) && l.all (· == a)) := by
induction l generalizing n with
| nil => simp
| cons h t ih =>
| cons _ _ ih =>
cases n
· simp
· simp [replicate_succ, isPrefixOf_cons₂, ih, Nat.succ_le_succ_iff, Bool.and_left_comm]
@@ -568,9 +571,9 @@ theorem flatten_sublist_iff {L : List (List α)} {l} :
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <+ l₂) :=
decidable_of_iff (l₁.isSublist l₂) isSublist_iff_sublist
protected theorem Sublist.drop : {l₁ l₂ : List α}, l₁ <+ l₂ n, l₁.drop n <+ l₂.drop n
protected theorem Sublist.drop : {l₁ l₂ : List α}, l₁ <+ l₂ i, l₁.drop i <+ l₂.drop i
| _, _, h, 0 => h
| _, _, h, n + 1 => by rw [ drop_tail, drop_tail]; exact h.tail.drop n
| _, _, h, i + 1 => by rw [ drop_tail, drop_tail]; exact h.tail.drop i
/-! ### IsPrefix / IsSuffix / IsInfix -/
@@ -604,10 +607,10 @@ theorem infix_refl (l : List α) : l <:+: l := prefix_rfl.isInfix
@[simp] theorem suffix_cons (a : α) : l, l <:+ a :: l := suffix_append [a]
theorem infix_cons : l₁ <:+: l₂ l₁ <:+: a :: l₂ := fun L₁, L₂, h => a :: L₁, L₂, h rfl
theorem infix_cons : l₁ <:+: l₂ l₁ <:+: a :: l₂ := fun l₁', l₂', h => a :: l₁', l₂', h rfl
theorem infix_concat : l₁ <:+: l₂ l₁ <:+: concat l₂ a := fun L₁, L₂, h =>
L₁, concat L₂ a, by simp [ h, concat_eq_append, append_assoc]
theorem infix_concat : l₁ <:+: l₂ l₁ <:+: concat l₂ a := fun l₁', l₂', h =>
l₁', concat l₂' a, by simp [ h, concat_eq_append, append_assoc]
theorem IsPrefix.trans : {l₁ l₂ l₃ : List α}, l₁ <+: l₂ l₂ <+: l₃ l₁ <+: l₃
| _, _, _, r₁, rfl, r₂, rfl => r₁ ++ r₂, (append_assoc _ _ _).symm
@@ -646,13 +649,13 @@ theorem eq_nil_of_infix_nil (h : l <:+: []) : l = [] := infix_nil.mp h
theorem eq_nil_of_prefix_nil (h : l <+: []) : l = [] := prefix_nil.mp h
theorem eq_nil_of_suffix_nil (h : l <:+ []) : l = [] := suffix_nil.mp h
theorem IsPrefix.ne_nil {x y : List α} (h : x <+: y) (hx : x []) : y [] := by
theorem IsPrefix.ne_nil {xs ys : List α} (h : xs <+: ys) (hx : xs []) : ys [] := by
rintro rfl; exact hx <| List.prefix_nil.mp h
theorem IsSuffix.ne_nil {x y : List α} (h : x <:+ y) (hx : x []) : y [] := by
theorem IsSuffix.ne_nil {xs ys : List α} (h : xs <:+ ys) (hx : xs []) : ys [] := by
rintro rfl; exact hx <| List.suffix_nil.mp h
theorem IsInfix.ne_nil {x y : List α} (h : x <:+: y) (hx : x []) : y [] := by
theorem IsInfix.ne_nil {xs ys : List α} (h : xs <:+: ys) (hx : xs []) : ys [] := by
rintro rfl; exact hx <| List.infix_nil.mp h
theorem IsInfix.length_le (h : l₁ <:+: l₂) : l₁.length l₂.length :=
@@ -664,10 +667,10 @@ theorem IsPrefix.length_le (h : l₁ <+: l₂) : l₁.length ≤ l₂.length :=
theorem IsSuffix.length_le (h : l₁ <:+ l₂) : l₁.length l₂.length :=
h.sublist.length_le
theorem IsPrefix.getElem {x y : List α} (h : x <+: y) {n} (hn : n < x.length) :
x[n] = y[n]'(Nat.le_trans hn h.length_le) := by
theorem IsPrefix.getElem {xs ys : List α} (h : xs <+: ys) {i} (hi : i < xs.length) :
xs[i] = ys[i]'(Nat.le_trans hi h.length_le) := by
obtain _, rfl := h
exact (List.getElem_append_left hn).symm
exact (List.getElem_append_left hi).symm
-- See `Init.Data.List.Nat.Sublist` for `IsSuffix.getElem`.
@@ -702,13 +705,13 @@ theorem IsSuffix.reverse : l₁ <:+ l₂ → reverse l₁ <+: reverse l₂ :=
theorem IsPrefix.reverse : l₁ <+: l₂ reverse l₁ <:+ reverse l₂ :=
reverse_suffix.2
theorem IsPrefix.head {x y : List α} (h : x <+: y) (hx : x []) :
x.head hx = y.head (h.ne_nil hx) := by
cases x <;> cases y <;> simp only [head_cons, ne_eq, not_true_eq_false] at hx
theorem IsPrefix.head {l₁ l₂ : List α} (h : l₁ <+: l₂) (hx : l₁ []) :
l₁.head hx = l₂.head (h.ne_nil hx) := by
cases l₁ <;> cases l₂ <;> simp only [head_cons, ne_eq, not_true_eq_false] at hx
all_goals (obtain _, h := h; injection h)
theorem IsSuffix.getLast {x y : List α} (h : x <:+ y) (hx : x []) :
x.getLast hx = y.getLast (h.ne_nil hx) := by
theorem IsSuffix.getLast {l₁ l₂ : List α} (h : l₁ <:+ l₂) (hx : l₁ []) :
l₁.getLast hx = l₂.getLast (h.ne_nil hx) := by
rw [ head_reverse (by simpa), h.reverse.head,
head_reverse (by rintro h; simp only [reverse_eq_nil_iff] at h; simp_all)]
@@ -839,8 +842,8 @@ theorem isPrefix_iff : l₁ <+: l₂ ↔ ∀ i (h : i < l₁.length), l₂[i]? =
simp [Nat.succ_lt_succ_iff, eq_comm]
theorem isPrefix_iff_getElem {l₁ l₂ : List α} :
l₁ <+: l₂ (h : l₁.length l₂.length), x (hx : x < l₁.length),
l₁[x] = l₂[x]'(Nat.lt_of_lt_of_le hx h) where
l₁ <+: l₂ (h : l₁.length l₂.length), i (hx : i < l₁.length),
l₁[i] = l₂[i]'(Nat.lt_of_lt_of_le hx h) where
mp h := h.length_le, fun _ h' h.getElem h'
mpr h := by
obtain hl, h := h
@@ -951,40 +954,40 @@ theorem infix_of_mem_flatten : ∀ {L : List (List α)}, l ∈ L → l <:+: flat
theorem prefix_cons_inj (a) : a :: l₁ <+: a :: l₂ l₁ <+: l₂ :=
prefix_append_right_inj [a]
theorem take_prefix (n) (l : List α) : take n l <+: l :=
theorem take_prefix (i) (l : List α) : take i l <+: l :=
_, take_append_drop _ _
theorem drop_suffix (n) (l : List α) : drop n l <:+ l :=
theorem drop_suffix (i) (l : List α) : drop i l <:+ l :=
_, take_append_drop _ _
theorem take_sublist (n) (l : List α) : take n l <+ l :=
(take_prefix n l).sublist
theorem take_sublist (i) (l : List α) : take i l <+ l :=
(take_prefix i l).sublist
theorem drop_sublist (n) (l : List α) : drop n l <+ l :=
(drop_suffix n l).sublist
theorem drop_sublist (i) (l : List α) : drop i l <+ l :=
(drop_suffix i l).sublist
theorem take_subset (n) (l : List α) : take n l l :=
(take_sublist n l).subset
theorem take_subset (i) (l : List α) : take i l l :=
(take_sublist i l).subset
theorem drop_subset (n) (l : List α) : drop n l l :=
(drop_sublist n l).subset
theorem drop_subset (i) (l : List α) : drop i l l :=
(drop_sublist i l).subset
theorem mem_of_mem_take {l : List α} (h : a l.take n) : a l :=
take_subset n l h
theorem mem_of_mem_take {l : List α} (h : a l.take i) : a l :=
take_subset _ _ h
theorem mem_of_mem_drop {n} {l : List α} (h : a l.drop n) : a l :=
theorem mem_of_mem_drop {i} {l : List α} (h : a l.drop i) : a l :=
drop_subset _ _ h
theorem drop_suffix_drop_left (l : List α) {m n : Nat} (h : m n) : drop n l <:+ drop m l := by
theorem drop_suffix_drop_left (l : List α) {i j : Nat} (h : i j) : drop j l <:+ drop i l := by
rw [ Nat.sub_add_cancel h, Nat.add_comm, drop_drop]
apply drop_suffix
-- See `Init.Data.List.Nat.TakeDrop` for `take_prefix_take_left`.
theorem drop_sublist_drop_left (l : List α) {m n : Nat} (h : m n) : drop n l <+ drop m l :=
theorem drop_sublist_drop_left (l : List α) {i j : Nat} (h : i j) : drop j l <+ drop i l :=
(drop_suffix_drop_left l h).sublist
theorem drop_subset_drop_left (l : List α) {m n : Nat} (h : m n) : drop n l drop m l :=
theorem drop_subset_drop_left (l : List α) {i j : Nat} (h : i j) : drop j l drop i l :=
(drop_sublist_drop_left l h).subset
theorem takeWhile_prefix (p : α Bool) : l.takeWhile p <+: l :=

View File

@@ -10,6 +10,9 @@ import Init.Data.List.Lemmas
# Lemmas about `List.take` and `List.drop`.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
@@ -20,19 +23,19 @@ Further results on `List.take` and `List.drop`, which rely on stronger automatio
are given in `Init.Data.List.TakeDrop`.
-/
theorem take_cons {l : List α} (h : 0 < n) : take n (a :: l) = a :: take (n - 1) l := by
cases n with
theorem take_cons {l : List α} (h : 0 < i) : take i (a :: l) = a :: take (i - 1) l := by
cases i with
| zero => exact absurd h (Nat.lt_irrefl _)
| succ n => rfl
| succ i => rfl
@[simp]
theorem drop_one : l : List α, drop 1 l = tail l
| [] | _ :: _ => rfl
@[simp] theorem take_append_drop : (n : Nat) (l : List α), take n l ++ drop n l = l
@[simp] theorem take_append_drop : (i : Nat) (l : List α), take i l ++ drop i l = l
| 0, _ => rfl
| _+1, [] => rfl
| n+1, x :: xs => congrArg (cons x) <| take_append_drop n xs
| i+1, x :: xs => congrArg (cons x) <| take_append_drop i xs
@[simp] theorem length_drop : (i : Nat) (l : List α), length (drop i l) = length l - i
| 0, _ => rfl
@@ -44,14 +47,14 @@ theorem drop_one : ∀ l : List α, drop 1 l = tail l
theorem drop_of_length_le {l : List α} (h : l.length i) : drop i l = [] :=
length_eq_zero.1 (length_drop .. Nat.sub_eq_zero_of_le h)
theorem length_lt_of_drop_ne_nil {l : List α} {n} (h : drop n l []) : n < l.length :=
theorem length_lt_of_drop_ne_nil {l : List α} {i} (h : drop i l []) : i < l.length :=
gt_of_not_le (mt drop_of_length_le h)
theorem take_of_length_le {l : List α} (h : l.length i) : take i l = l := by
have := take_append_drop i l
rw [drop_of_length_le h, append_nil] at this; exact this
theorem lt_length_of_take_ne_self {l : List α} {n} (h : l.take n l) : n < l.length :=
theorem lt_length_of_take_ne_self {l : List α} {i} (h : l.take i l) : i < l.length :=
gt_of_not_le (mt take_of_length_le h)
@[deprecated drop_of_length_le (since := "2024-07-07")] abbrev drop_length_le := @drop_of_length_le
@@ -67,66 +70,66 @@ theorem getElem_cons_drop : ∀ (l : List α) (i : Nat) (h : i < l.length),
| _::_, 0, _ => rfl
| _::_, i+1, h => getElem_cons_drop _ i (Nat.add_one_lt_add_one_iff.mp h)
theorem drop_eq_getElem_cons {n} {l : List α} (h : n < l.length) : drop n l = l[n] :: drop (n + 1) l :=
(getElem_cons_drop _ n h).symm
theorem drop_eq_getElem_cons {i} {l : List α} (h : i < l.length) : drop i l = l[i] :: drop (i + 1) l :=
(getElem_cons_drop _ i h).symm
@[simp]
theorem getElem?_take_of_lt {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
induction n generalizing l m with
theorem getElem?_take_of_lt {l : List α} {i j : Nat} (h : i < j) : (l.take j)[i]? = l[i]? := by
induction j generalizing l i with
| zero =>
exact absurd h (Nat.not_lt_of_le m.zero_le)
| succ _ hn =>
exact absurd h (Nat.not_lt_of_le i.zero_le)
| succ _ hj =>
cases l with
| nil => simp only [take_nil]
| cons hd tl =>
cases m
cases i
· simp
· simpa using hn (Nat.lt_of_succ_lt_succ h)
· simpa using hj (Nat.lt_of_succ_lt_succ h)
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? := by simp
theorem getElem?_take_of_succ {l : List α} {i : Nat} : (l.take (i + 1))[i]? = l[i]? := by simp
@[simp] theorem drop_drop (n : Nat) : (m) (l : List α), drop n (drop m l) = drop (m + n) l
| m, [] => by simp
@[simp] theorem drop_drop (i : Nat) : (j) (l : List α), drop i (drop j l) = drop (j + i) l
| j, [] => by simp
| 0, l => by simp
| m + 1, a :: l =>
| j + 1, a :: l =>
calc
drop n (drop (m + 1) (a :: l)) = drop n (drop m l) := rfl
_ = drop (m + n) l := drop_drop n m l
_ = drop ((m + 1) + n) (a :: l) := by rw [Nat.add_right_comm]; rfl
drop i (drop (j + 1) (a :: l)) = drop i (drop j l) := rfl
_ = drop (j + i) l := drop_drop i j l
_ = drop ((j + 1) + i) (a :: l) := by rw [Nat.add_right_comm]; rfl
theorem drop_add_one_eq_tail_drop (l : List α) : l.drop (i + 1) = (l.drop i).tail := by
rw [ drop_drop, drop_one]
theorem take_drop : (m n : Nat) (l : List α), take n (drop m l) = drop m (take (m + n) l)
| 0, _, _ => by simp
theorem take_drop : (i j : Nat) (l : List α), take i (drop j l) = drop j (take (j + i) l)
| _, 0, _ => by simp
| _, _, [] => by simp
| _+1, _, _ :: _ => by simpa [Nat.succ_add, take_succ_cons, drop_succ_cons] using take_drop ..
| _, _+1, _ :: _ => by simpa [Nat.succ_add, take_succ_cons, drop_succ_cons] using take_drop ..
@[simp]
theorem tail_drop (l : List α) (n : Nat) : (l.drop n).tail = l.drop (n + 1) := by
induction l generalizing n with
theorem tail_drop (l : List α) (i : Nat) : (l.drop i).tail = l.drop (i + 1) := by
induction l generalizing i with
| nil => simp
| cons hd tl hl =>
cases n
cases i
· simp
· simp [hl]
@[simp]
theorem drop_tail (l : List α) (n : Nat) : l.tail.drop n = l.drop (n + 1) := by
theorem drop_tail (l : List α) (i : Nat) : l.tail.drop i = l.drop (i + 1) := by
rw [Nat.add_comm, drop_drop, drop_one]
@[simp]
theorem drop_eq_nil_iff {l : List α} {k : Nat} : l.drop k = [] l.length k := by
theorem drop_eq_nil_iff {l : List α} {i : Nat} : l.drop i = [] l.length i := by
refine fun h => ?_, drop_eq_nil_of_le
induction k generalizing l with
induction i generalizing l with
| zero =>
simp only [drop] at h
simp [h]
| succ k hk =>
| succ i hi =>
cases l
· simp
· simp only [drop] at h
simpa [Nat.succ_le_succ_iff] using hk h
simpa [Nat.succ_le_succ_iff] using hi h
@[deprecated drop_eq_nil_iff (since := "2024-09-10")] abbrev drop_eq_nil_iff_le := @drop_eq_nil_iff
@@ -146,33 +149,36 @@ theorem take_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.take i =
theorem ne_nil_of_take_ne_nil {as : List α} {i : Nat} (h : as.take i []) : as [] :=
mt take_eq_nil_of_eq_nil h
theorem set_take {l : List α} {n m : Nat} {a : α} :
(l.set m a).take n = (l.take n).set m a := by
induction n generalizing l m with
theorem take_set {l : List α} {i j : Nat} {a : α} :
(l.set j a).take i = (l.take i).set j a := by
induction i generalizing l j with
| zero => simp
| succ _ hn =>
| succ _ hi =>
cases l with
| nil => simp
| cons hd tl => cases m <;> simp_all
| cons hd tl => cases j <;> simp_all
theorem drop_set {l : List α} {n m : Nat} {a : α} :
(l.set m a).drop n = if m < n then l.drop n else (l.drop n).set (m - n) a := by
induction n generalizing l m with
@[deprecated take_set (since := "2025-02-17")]
abbrev set_take := @take_set
theorem drop_set {l : List α} {i j : Nat} {a : α} :
(l.set j a).drop i = if j < i then l.drop i else (l.drop i).set (j - i) a := by
induction i generalizing l j with
| zero => simp
| succ _ hn =>
| succ _ hi =>
cases l with
| nil => simp
| cons hd tl =>
cases m
cases j
· simp_all
· simp only [hn, set_cons_succ, drop_succ_cons, succ_lt_succ_iff]
· simp only [hi, set_cons_succ, drop_succ_cons, succ_lt_succ_iff]
congr 2
exact (Nat.add_sub_add_right ..).symm
theorem set_drop {l : List α} {n m : Nat} {a : α} :
(l.drop n).set m a = (l.set (n + m) a).drop n := by
rw [drop_set, if_neg, add_sub_self_left n m]
exact (Nat.not_lt).2 (le_add_right n m)
theorem set_drop {l : List α} {i j : Nat} {a : α} :
(l.drop i).set j a = (l.set (i + j) a).drop i := by
rw [drop_set, if_neg, add_sub_self_left]
exact (Nat.not_lt).2 (le_add_right ..)
theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
(l.take i).concat l[i] = l.take (i+1) :=
@@ -183,7 +189,7 @@ theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
(l.take i) ++ [l[i]] = l.take (i+1) := by
simpa using take_concat_get l i h
theorem take_succ_eq_append_getElem {n} {l : List α} (h : n < l.length) : l.take (n + 1) = l.take n ++ [l[n]] :=
theorem take_succ_eq_append_getElem {i} {l : List α} (h : i < l.length) : l.take (i + 1) = l.take i ++ [l[i]] :=
(take_append_getElem _ _ h).symm
@[simp] theorem take_append_getLast (l : List α) (h : l []) :
@@ -204,7 +210,7 @@ theorem take_succ_eq_append_getElem {n} {l : List α} (h : n < l.length) : l.tak
theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
@[deprecated take_of_length_le (since := "2024-07-25")]
theorem take_all_of_le {n} {l : List α} (h : length l n) : take n l = l :=
theorem take_all_of_le {l : List α} {i} (h : length l i) : take i l = l :=
take_of_length_le h
theorem drop_left : l₁ l₂ : List α, drop (length l₁) (l₁ ++ l₂) = l₂
@@ -212,7 +218,7 @@ theorem drop_left : ∀ l₁ l₂ : List α, drop (length l₁) (l₁ ++ l₂) =
| _ :: l₁, l₂ => drop_left l₁ l₂
@[simp]
theorem drop_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : drop n (l₁ ++ l₂) = l₂ := by
theorem drop_left' {l₁ l₂ : List α} {i} (h : length l₁ = i) : drop i (l₁ ++ l₂) = l₂ := by
rw [ h]; apply drop_left
theorem take_left : l₁ l₂ : List α, take (length l₁) (l₁ ++ l₂) = l₁
@@ -220,24 +226,24 @@ theorem take_left : ∀ l₁ l₂ : List α, take (length l₁) (l₁ ++ l₂) =
| a :: l₁, l₂ => congrArg (cons a) (take_left l₁ l₂)
@[simp]
theorem take_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : take n (l₁ ++ l₂) = l₁ := by
theorem take_left' {l₁ l₂ : List α} {i} (h : length l₁ = i) : take i (l₁ ++ l₂) = l₁ := by
rw [ h]; apply take_left
theorem take_succ {l : List α} {n : Nat} : l.take (n + 1) = l.take n ++ l[n]?.toList := by
induction l generalizing n with
theorem take_succ {l : List α} {i : Nat} : l.take (i + 1) = l.take i ++ l[i]?.toList := by
induction l generalizing i with
| nil =>
simp only [take_nil, Option.toList, getElem?_nil, append_nil]
| cons hd tl hl =>
cases n
cases i
· simp only [take, Option.toList, getElem?_cons_zero, nil_append]
· simp only [take, hl, getElem?_cons_succ, cons_append]
@[deprecated "Deprecated without replacement." (since := "2024-07-25")]
theorem drop_sizeOf_le [SizeOf α] (l : List α) (n : Nat) : sizeOf (l.drop n) sizeOf l := by
induction l generalizing n with
theorem drop_sizeOf_le [SizeOf α] (l : List α) (i : Nat) : sizeOf (l.drop i) sizeOf l := by
induction l generalizing i with
| nil => rw [drop_nil]; apply Nat.le_refl
| cons _ _ lih =>
induction n with
induction i with
| zero => apply Nat.le_refl
| succ n =>
exact Trans.trans (lih _) (Nat.le_add_left _ _)
@@ -249,18 +255,18 @@ theorem dropLast_eq_take (l : List α) : l.dropLast = l.take (l.length - 1) := b
induction l generalizing x <;> simp_all [dropLast]
@[simp] theorem map_take (f : α β) :
(L : List α) (i : Nat), (L.take i).map f = (L.map f).take i
(l : List α) (i : Nat), (l.take i).map f = (l.map f).take i
| [], i => by simp
| _, 0 => by simp
| h :: t, n + 1 => by dsimp; rw [map_take f t n]
| _ :: tl, n + 1 => by dsimp; rw [map_take f tl n]
@[simp] theorem map_drop (f : α β) :
(L : List α) (i : Nat), (L.drop i).map f = (L.map f).drop i
(l : List α) (i : Nat), (l.drop i).map f = (l.map f).drop i
| [], i => by simp
| L, 0 => by simp
| h :: t, n + 1 => by
| l, 0 => by simp
| _ :: tl, n + 1 => by
dsimp
rw [map_drop f t]
rw [map_drop f tl]
/-! ### takeWhile and dropWhile -/
@@ -393,7 +399,7 @@ theorem dropWhile_append {xs ys : List α} :
if (xs.dropWhile p).isEmpty then ys.dropWhile p else xs.dropWhile p ++ ys := by
induction xs with
| nil => simp
| cons h t ih =>
| cons _ _ ih =>
simp only [cons_append, dropWhile_cons]
split <;> simp_all
@@ -428,23 +434,23 @@ theorem dropWhile_replicate (p : α → Bool) :
simp only [dropWhile_replicate_eq_filter_not, filter_replicate]
split <;> simp_all
theorem take_takeWhile {l : List α} (p : α Bool) n :
(l.takeWhile p).take n = (l.take n).takeWhile p := by
induction l generalizing n with
theorem take_takeWhile {l : List α} (p : α Bool) i :
(l.takeWhile p).take i = (l.take i).takeWhile p := by
induction l generalizing i with
| nil => simp
| cons x xs ih =>
by_cases h : p x <;> cases n <;> simp [takeWhile_cons, h, ih, take_succ_cons]
by_cases h : p x <;> cases i <;> simp [takeWhile_cons, h, ih, take_succ_cons]
@[simp] theorem all_takeWhile {l : List α} : (l.takeWhile p).all p = true := by
induction l with
| nil => rfl
| cons h t ih => by_cases p h <;> simp_all
| cons h _ ih => by_cases p h <;> simp_all
@[simp] theorem any_dropWhile {l : List α} :
(l.dropWhile p).any (fun x => !p x) = !l.all p := by
induction l with
| nil => rfl
| cons h t ih => by_cases p h <;> simp_all
| cons h _ ih => by_cases p h <;> simp_all
theorem replace_takeWhile [BEq α] [LawfulBEq α] {l : List α} {p : α Bool} (h : p a = p b) :
(l.takeWhile p).replace a b = (l.replace a b).takeWhile p := by
@@ -460,7 +466,7 @@ theorem replace_takeWhile [BEq α] [LawfulBEq α] {l : List α} {p : α → Bool
/-! ### splitAt -/
@[simp] theorem splitAt_eq (n : Nat) (l : List α) : splitAt n l = (l.take n, l.drop n) := by
@[simp] theorem splitAt_eq (i : Nat) (l : List α) : splitAt i l = (l.take i, l.drop i) := by
rw [splitAt, splitAt_go, reverse_nil, nil_append]
split <;> simp_all [take_of_length_le, drop_of_length_le]

View File

@@ -15,17 +15,20 @@ import Init.Data.Array.Lex.Basic
We prefer to pull `List.toArray` outwards past `Array` operations.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array
@[simp] theorem toList_set (a : Array α) (i x h) :
(a.set i x).toList = a.toList.set i x := rfl
@[simp] theorem toList_set (xs : Array α) (i x h) :
(xs.set i x).toList = xs.toList.set i x := rfl
theorem swap_def (a : Array α) (i j : Nat) (hi hj) :
a.swap i j hi hj = (a.set i a[j]).set j a[i] (by simpa using hj) := by
theorem swap_def (xs : Array α) (i j : Nat) (hi hj) :
xs.swap i j hi hj = (xs.set i xs[j]).set j xs[i] (by simpa using hj) := by
simp [swap]
@[simp] theorem toList_swap (a : Array α) (i j : Nat) (hi hj) :
(a.swap i j hi hj).toList = (a.toList.set i a[j]).set j a[i] := by simp [swap_def]
@[simp] theorem toList_swap (xs : Array α) (i j : Nat) (hi hj) :
(xs.swap i j hi hj).toList = (xs.toList.set i xs[j]).set j xs[i] := by simp [swap_def]
end Array
@@ -33,16 +36,16 @@ namespace List
open Array
theorem toArray_inj {a b : List α} (h : a.toArray = b.toArray) : a = b := by
cases a with
theorem toArray_inj {as bs : List α} (h : as.toArray = bs.toArray) : as = bs := by
cases as with
| nil => simpa using h
| cons a as =>
cases b with
cases bs with
| nil => simp at h
| cons b bs => simpa using h
@[simp] theorem size_toArrayAux {a : List α} {b : Array α} :
(a.toArrayAux b).size = b.size + a.length := by
@[simp] theorem size_toArrayAux {as : List α} {xs : Array α} :
(as.toArrayAux xs).size = xs.size + as.length := by
simp [size]
-- This is not a `@[simp]` lemma because it is pushing `toArray` inwards.
@@ -70,6 +73,10 @@ theorem toArray_cons (a : α) (l : List α) : (a :: l).toArray = #[a] ++ l.toArr
@[simp] theorem back?_toArray (l : List α) : l.toArray.back? = l.getLast? := by
simp [back?, List.getLast?_eq_getElem?]
@[simp] theorem back_toArray (l : List α) (h) :
l.toArray.back = l.getLast (by simp at h; exact ne_nil_of_length_pos h) := by
simp [back, List.getLast_eq_getElem]
@[simp] theorem set_toArray (l : List α) (i : Nat) (a : α) (h : i < l.length) :
(l.toArray.set i a) = (l.set i a).toArray := rfl
@@ -365,8 +372,8 @@ theorem isPrefixOfAux_toArray_zero [BEq α] (l₁ l₂ : List α) (hle : l₁.le
rw [ih]
simp_all
theorem zipWithAux_toArray_succ (as : List α) (bs : List β) (f : α β γ) (i : Nat) (cs : Array γ) :
zipWithAux as.toArray bs.toArray f (i + 1) cs = zipWithAux as.tail.toArray bs.tail.toArray f i cs := by
theorem zipWithAux_toArray_succ (as : List α) (bs : List β) (f : α β γ) (i : Nat) (xs : Array γ) :
zipWithAux as.toArray bs.toArray f (i + 1) xs = zipWithAux as.tail.toArray bs.tail.toArray f i xs := by
rw [zipWithAux]
conv => rhs; rw [zipWithAux]
simp only [size_toArray, getElem_toArray, length_tail, getElem_tail]
@@ -377,16 +384,16 @@ theorem zipWithAux_toArray_succ (as : List α) (bs : List β) (f : α → β →
rw [dif_neg (by omega)]
· rw [dif_neg (by omega)]
theorem zipWithAux_toArray_succ' (as : List α) (bs : List β) (f : α β γ) (i : Nat) (cs : Array γ) :
zipWithAux as.toArray bs.toArray f (i + 1) cs = zipWithAux (as.drop (i+1)).toArray (bs.drop (i+1)).toArray f 0 cs := by
induction i generalizing as bs cs with
theorem zipWithAux_toArray_succ' (as : List α) (bs : List β) (f : α β γ) (i : Nat) (xs : Array γ) :
zipWithAux as.toArray bs.toArray f (i + 1) xs = zipWithAux (as.drop (i+1)).toArray (bs.drop (i+1)).toArray f 0 xs := by
induction i generalizing as bs xs with
| zero => simp [zipWithAux_toArray_succ]
| succ i ih =>
rw [zipWithAux_toArray_succ, ih]
simp
theorem zipWithAux_toArray_zero (f : α β γ) (as : List α) (bs : List β) (cs : Array γ) :
zipWithAux as.toArray bs.toArray f 0 cs = cs ++ (List.zipWith f as bs).toArray := by
theorem zipWithAux_toArray_zero (f : α β γ) (as : List α) (bs : List β) (xs : Array γ) :
zipWithAux as.toArray bs.toArray f 0 xs = xs ++ (List.zipWith f as bs).toArray := by
rw [Array.zipWithAux]
match as, bs with
| [], _ => simp
@@ -403,8 +410,8 @@ theorem zipWithAux_toArray_zero (f : α → β → γ) (as : List α) (bs : List
Array.zip as.toArray bs.toArray = (List.zip as bs).toArray := by
simp [Array.zip, zipWith_toArray, zip]
theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α Option β γ) (i : Nat) (cs : Array γ) :
zipWithAll.go f as.toArray bs.toArray i cs = cs ++ (List.zipWithAll f (as.drop i) (bs.drop i)).toArray := by
theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α Option β γ) (i : Nat) (xs : Array γ) :
zipWithAll.go f as.toArray bs.toArray i xs = xs ++ (List.zipWithAll f (as.drop i) (bs.drop i)).toArray := by
unfold zipWithAll.go
split <;> rename_i h
· rw [zipWithAll_go_toArray]
@@ -482,6 +489,21 @@ theorem takeWhile_go_toArray (p : α → Bool) (l : List α) (i : Nat) :
l.toArray.takeWhile p = (l.takeWhile p).toArray := by
simp [Array.takeWhile, takeWhile_go_toArray]
private theorem popWhile_toArray_aux (p : α Bool) (l : List α) :
l.reverse.toArray.popWhile p = (l.dropWhile p).reverse.toArray := by
induction l with
| nil => simp
| cons a l ih =>
unfold popWhile
simp [ih, dropWhile_cons]
split
· rfl
· simp
@[simp] theorem popWhile_toArray (p : α Bool) (l : List α) :
l.toArray.popWhile p = (l.reverse.dropWhile p).reverse.toArray := by
simp [ popWhile_toArray_aux]
@[simp] theorem setIfInBounds_toArray (l : List α) (i : Nat) (a : α) :
l.toArray.setIfInBounds i a = (l.set i a).toArray := by
apply ext'
@@ -500,12 +522,12 @@ abbrev _root_.Array.mkArray_eq_toArray_replicate := @toArray_replicate
theorem flatMap_toArray_cons {β} (f : α Array β) (a : α) (as : List α) :
(a :: as).toArray.flatMap f = f a ++ as.toArray.flatMap f := by
simp [Array.flatMap]
suffices cs, List.foldl (fun bs a => bs ++ f a) (f a ++ cs) as =
f a ++ List.foldl (fun bs a => bs ++ f a) cs as by
suffices xs, List.foldl (fun ys a => ys ++ f a) (f a ++ xs) as =
f a ++ List.foldl (fun ys a => ys ++ f a) xs as by
erw [empty_append] -- Why doesn't this work via `simp`?
simpa using this #[]
intro cs
induction as generalizing cs <;> simp_all
intro xs
induction as generalizing xs <;> simp_all
@[simp] theorem flatMap_toArray {β} (f : α Array β) (as : List α) :
as.toArray.flatMap f = (as.flatMap (fun a => (f a).toList)).toArray := by
@@ -566,21 +588,15 @@ private theorem insertIdx_loop_toArray (i : Nat) (l : List α) (j : Nat) (hj : j
simp only [append_assoc, cons_append]
rw [insertIdx_loop_toArray _ _ _ _ (by omega)]
simp only [swap_toArray, w, append_assoc, cons_append, mk.injEq]
rw [List.take_set_of_le _ _ (by omega)]
rw [List.drop_eq_getElem_cons (n := j) (by simpa)]
rw [List.getElem_set_self]
rw [List.drop_set_of_lt _ _ (by omega)]
rw [List.drop_set_of_lt _ _ (by omega)]
rw [List.getElem_set_ne (by omega)]
rw [List.getElem_set_self]
rw [List.take_set_of_le (j := j - 1) _ _ (by omega)]
rw [List.take_set_of_le (j := j - 1) _ _ (by omega)]
rw [List.take_eq_append_getElem_of_pos (i := j) (l := l) (by omega) hj]
rw [List.drop_append_of_le_length (by simp; omega)]
rw [take_set_of_le _ _ (by omega), drop_eq_getElem_cons (i := j) (by simpa), getElem_set_self,
drop_set_of_lt _ _ (by omega), drop_set_of_lt _ _ (by omega), getElem_set_ne (by omega),
getElem_set_self, take_set_of_le (j := j - 1) _ _ (by omega),
take_set_of_le (j := j - 1) _ _ (by omega), take_eq_append_getElem_of_pos (by omega) hj,
drop_append_of_le_length (by simp; omega)]
simp only [append_assoc, cons_append, nil_append, append_cancel_right_eq]
cases i with
| zero => simp
| succ i => rw [List.take_set_of_le _ _ (by omega)]
| succ i => rw [take_set_of_le _ _ (by omega)]
· simp only [Nat.not_lt] at h'
have : i = j := by omega
subst this

View File

@@ -6,18 +6,21 @@ Authors: Henrik Böving
prelude
import Init.Data.List.Basic
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
/--
Auxiliary definition for `List.toArray`.
`List.toArrayAux as r = r ++ as.toArray`
-/
@[inline_if_reduce]
def List.toArrayAux : List α Array α Array α
| nil, r => r
| cons a as, r => toArrayAux as (r.push a)
| nil, xs => xs
| cons a as, xs => toArrayAux as (xs.push a)
/-- Convert a `List α` into an `Array α`. This is O(n) in the length of the list. -/
-- This function is exported to C, where it is called by `Array.mk`
-- (the constructor) to implement this functionality.
@[inline, match_pattern, pp_nodot, export lean_list_to_array]
def List.toArrayImpl (as : List α) : Array α :=
as.toArrayAux (Array.mkEmpty as.length)
def List.toArrayImpl (xs : List α) : Array α :=
xs.toArrayAux (Array.mkEmpty xs.length)

View File

@@ -11,6 +11,9 @@ import Init.Data.Function
# Lemmas about `List.zip`, `List.zipWith`, `List.zipWithAll`, and `List.unzip`.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
@@ -20,7 +23,7 @@ open Nat
/-! ### zipWith -/
theorem zipWith_comm (f : α β γ) :
(la : List α) (lb : List β), zipWith f la lb = zipWith (fun b a => f a b) lb la
(as : List α) (bs : List β), zipWith f as bs = zipWith (fun b a => f a b) bs as
| [], _ => List.zipWith_nil_right.symm
| _ :: _, [] => rfl
| _ :: as, _ :: bs => congrArg _ (zipWith_comm f as bs)
@@ -57,7 +60,7 @@ theorem getElem?_zipWith' {f : α → β → γ} {i : Nat} :
(zipWith f l₁ l₂)[i]? = (l₁[i]?.map f).bind fun g => l₂[i]?.map g := by
induction l₁ generalizing l₂ i with
| nil => rw [zipWith] <;> simp
| cons head tail =>
| cons _ _ =>
cases l₂
· simp
· cases i <;> simp_all
@@ -122,25 +125,25 @@ theorem map_zipWith {δ : Type _} (f : α → β) (g : γ → δ → α) (l : Li
· simp
· simp [hl]
theorem take_zipWith : (zipWith f l l').take n = zipWith f (l.take n) (l'.take n) := by
induction l generalizing l' n with
theorem take_zipWith : (zipWith f l l').take i = zipWith f (l.take i) (l'.take i) := by
induction l generalizing l' i with
| nil => simp
| cons hd tl hl =>
cases l'
· simp
· cases n
· cases i
· simp
· simp [hl]
@[deprecated take_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_take := @take_zipWith
theorem drop_zipWith : (zipWith f l l').drop n = zipWith f (l.drop n) (l'.drop n) := by
induction l generalizing l' n with
theorem drop_zipWith : (zipWith f l l').drop i = zipWith f (l.drop i) (l'.drop i) := by
induction l generalizing l' i with
| nil => simp
| cons hd tl hl =>
· cases l'
· simp
· cases n
· cases i
· simp
· simp [hl]
@@ -152,17 +155,17 @@ theorem tail_zipWith : (zipWith f l l').tail = zipWith f l.tail l'.tail := by
@[deprecated tail_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_tail := @tail_zipWith
theorem zipWith_append (f : α β γ) (l la : List α) (l' lb : List β)
(h : l.length = l'.length) :
zipWith f (l ++ la) (l' ++ lb) = zipWith f l l' ++ zipWith f la lb := by
induction l generalizing l' with
theorem zipWith_append (f : α β γ) (l l₁' : List α) (l l₂' : List β)
(h : l.length = l.length) :
zipWith f (l ++ l₁') (l ++ l₂') = zipWith f l l ++ zipWith f l₁' l₂' := by
induction l generalizing l with
| nil =>
have : l' = [] := eq_nil_of_length_eq_zero (by simpa using h.symm)
have : l = [] := eq_nil_of_length_eq_zero (by simpa using h.symm)
simp [this]
| cons hl tl ih =>
cases l' with
cases l with
| nil => simp at h
| cons head tail =>
| cons _ _ =>
simp only [length_cons, Nat.succ.injEq] at h
simp [ih _ h]
@@ -199,7 +202,7 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {l₁ : List α} {l₂ : Li
· simp only [zipWith_nil_right, nil_eq, append_eq_nil_iff, exists_and_left, and_imp]
rintro rfl rfl
exact [], x₁ :: l₁, [], by simp
· rintro w, x, y, z, h₁, _, h₃, rfl, rfl
· rintro _, _, _, _, h₁, _, h₃, rfl, rfl
simp only [nil_eq, append_eq_nil_iff] at h₃
obtain rfl, rfl := h₃
simp
@@ -207,21 +210,21 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {l₁ : List α} {l₂ : Li
simp only [zipWith_cons_cons]
rw [cons_eq_append_iff]
constructor
· rintro (rfl, rfl | l₁'', rfl, h)
· rintro (rfl, rfl | _, rfl, h)
· exact [], x₁ :: l₁, [], x₂ :: l₂, by simp
· rw [ih₁] at h
obtain w, x, y, z, h, rfl, rfl, h', rfl := h
refine x₁ :: w, x, x₂ :: y, z, by simp [h, h']
· rintro w, x, y, z, h₁, h₂, h₃, rfl, rfl
obtain ws, xs, ys, zs, h, rfl, rfl, h', rfl := h
refine x₁ :: ws, xs, x₂ :: ys, zs, by simp [h, h']
· rintro _, _, _, _, h₁, h₂, h₃, rfl, rfl
rw [cons_eq_append_iff] at h₂
rw [cons_eq_append_iff] at h₃
obtain (rfl, rfl | w', rfl, rfl) := h₂
obtain (rfl, rfl | _, rfl, rfl) := h₂
· simp only [zipWith_nil_left, true_and, nil_eq, reduceCtorEq, false_and, exists_const,
or_false]
obtain (rfl, rfl | y', rfl, rfl) := h₃
obtain (rfl, rfl | _, rfl, rfl) := h₃
· simp
· simp_all
· obtain (rfl, rfl | y', rfl, rfl) := h₃
· obtain (rfl, rfl | _, rfl, rfl) := h₃
· simp_all
· simp_all [zipWith_append, Nat.succ_inj']
@@ -274,9 +277,9 @@ theorem zip_map_right (f : β → γ) (l₁ : List α) (l₂ : List β) :
theorem zip_append :
{l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
| [], r₁, l₂, r₂, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
| l₁, r₁, [], r₂, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
| a :: l₁, r₁, b :: l₂, r₂, h => by
| [], _, _, _, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
| _, _, [], _, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
| _ :: _, _, _ :: _, _, h => by
simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
theorem zip_map' (f : α β) (g : α γ) :
@@ -448,9 +451,9 @@ theorem unzip_zip {l₁ : List α} {l₂ : List β} (h : length l₁ = length l
· rw [unzip_zip_left (Nat.le_of_eq h)]
· rw [unzip_zip_right (Nat.le_of_eq h.symm)]
theorem zip_of_prod {l : List α} {l' : List β} {lp : List (α × β)} (hl : lp.map Prod.fst = l)
(hr : lp.map Prod.snd = l') : lp = l.zip l' := by
rw [ hl, hr, zip_unzip lp, unzip_fst, unzip_snd, zip_unzip, zip_unzip]
theorem zip_of_prod {l : List α} {l' : List β} {xs : List (α × β)} (hl : xs.map Prod.fst = l)
(hr : xs.map Prod.snd = l') : xs = l.zip l' := by
rw [ hl, hr, zip_unzip xs, unzip_fst, unzip_snd, zip_unzip, zip_unzip]
theorem tail_zip_fst {l : List (α × β)} : l.unzip.1.tail = l.tail.unzip.1 := by
simp

View File

@@ -780,16 +780,19 @@ protected theorem max_def {n m : Nat} : max n m = if n ≤ m then m else n := rf
/-! # Auxiliary theorems for well-founded recursion -/
theorem not_eq_zero_of_lt (h : b < a) : a 0 := by
protected theorem ne_zero_of_lt (h : b < a) : a 0 := by
cases a
exact absurd h (Nat.not_lt_zero _)
apply Nat.noConfusion
@[deprecated Nat.ne_zero_of_lt (since := "2025-02-06")]
theorem not_eq_zero_of_lt (h : b < a) : a 0 := Nat.ne_zero_of_lt h
theorem pred_lt_of_lt {n m : Nat} (h : m < n) : pred n < n :=
pred_lt (not_eq_zero_of_lt h)
pred_lt (Nat.ne_zero_of_lt h)
theorem sub_one_lt_of_lt {n m : Nat} (h : m < n) : n - 1 < n :=
sub_one_lt (not_eq_zero_of_lt h)
sub_one_lt (Nat.ne_zero_of_lt h)
/-! # pred theorems -/
@@ -854,7 +857,7 @@ theorem zero_lt_sub_of_lt (h : i < a) : 0 < a - i := by
theorem sub_succ_lt_self (a i : Nat) (h : i < a) : a - (i + 1) < a - i := by
rw [Nat.add_succ, Nat.sub_succ]
apply Nat.pred_lt
apply Nat.not_eq_zero_of_lt
apply Nat.ne_zero_of_lt
apply Nat.zero_lt_sub_of_lt
assumption

View File

@@ -9,9 +9,9 @@ import Init.Meta
namespace Nat
protected theorem dvd_refl (a : Nat) : a a := 1, by simp
@[simp] protected theorem dvd_refl (a : Nat) : a a := 1, by simp
protected theorem dvd_zero (a : Nat) : a 0 := 0, by simp
@[simp] protected theorem dvd_zero (a : Nat) : a 0 := 0, by simp
protected theorem dvd_mul_left (a b : Nat) : a b * a := b, Nat.mul_comm b a
protected theorem dvd_mul_right (a b : Nat) : a a * b := b, rfl
@@ -129,4 +129,13 @@ protected theorem mul_div_assoc (m : Nat) (H : k n) : m * n / k = m * (n / k
have h1 : m * n / k = m * (n / k * k) / k := by rw [Nat.div_mul_cancel H]
rw [h1, Nat.mul_assoc, Nat.mul_div_cancel _ hpos]
/-! Helper theorems for `dvd` simproc -/
protected theorem dvd_eq_true_of_mod_eq_zero {m n : Nat} (h : n % m == 0) : (m n) = True := by
simp [Nat.dvd_of_mod_eq_zero, eq_of_beq h]
protected theorem dvd_eq_false_of_mod_ne_zero {m n : Nat} (h : n % m != 0) : (m n) = False := by
simp [eq_of_beq] at h
simp [dvd_iff_mod_eq_zero, h]
end Nat

View File

@@ -1011,7 +1011,7 @@ theorem mul_add_div {m : Nat} (m_pos : m > 0) (x y : Nat) : (m * x + y) / m = x
| 0 => simp
| x + 1 =>
rw [Nat.mul_succ, Nat.add_assoc _ m, mul_add_div m_pos x (m+y), div_eq]
simp_arith [m_pos]; rw [Nat.add_comm, Nat.add_sub_cancel]
simp +arith [m_pos]; rw [Nat.add_comm, Nat.add_sub_cancel]
theorem mul_add_mod (m x y : Nat) : (m * x + y) % m = y % m := by
match x with

View File

@@ -35,11 +35,11 @@ inductive Expr where
deriving Inhabited
def Expr.denote (ctx : Context) : Expr Nat
| Expr.add a b => Nat.add (denote ctx a) (denote ctx b)
| Expr.num k => k
| Expr.var v => v.denote ctx
| Expr.mulL k e => Nat.mul k (denote ctx e)
| Expr.mulR e k => Nat.mul (denote ctx e) k
| .add a b => Nat.add (denote ctx a) (denote ctx b)
| .num k => k
| .var v => v.denote ctx
| .mulL k e => Nat.mul k (denote ctx e)
| .mulR e k => Nat.mul (denote ctx e) k
abbrev Poly := List (Nat × Var)
@@ -66,18 +66,6 @@ where
| [] => r
| (k, v) :: p => go p (r.insert k v)
def Poly.mul (k : Nat) (p : Poly) : Poly :=
bif k == 0 then
[]
else bif k == 1 then
p
else
go p
where
go : Poly Poly
| [] => []
| (k', v) :: p => (Nat.mul k k', v) :: go p
def Poly.cancelAux (fuel : Nat) (m₁ m₂ r₁ r₂ : Poly) : Poly × Poly :=
match fuel with
| 0 => (r₁.reverse ++ m₁, r₂.reverse ++ m₂)
@@ -122,41 +110,23 @@ def Poly.denote_eq (ctx : Context) (mp : Poly × Poly) : Prop := mp.1.denote ctx
def Poly.denote_le (ctx : Context) (mp : Poly × Poly) : Prop := mp.1.denote ctx mp.2.denote ctx
def Poly.combineAux (fuel : Nat) (p₁ p₂ : Poly) : Poly :=
match fuel with
| 0 => p₁ ++ p₂
| fuel + 1 =>
match p₁, p₂ with
| p₁, [] => p₁
| [], p₂ => p₂
| (k₁, v₁) :: p₁, (k₂, v₂) :: p₂ =>
bif Nat.blt v₁ v₂ then
(k₁, v₁) :: combineAux fuel p₁ ((k₂, v₂) :: p₂)
else bif Nat.blt v₂ v₁ then
(k₂, v₂) :: combineAux fuel ((k₁, v₁) :: p₁) p₂
else
(Nat.add k₁ k₂, v₁) :: combineAux fuel p₁ p₂
def Poly.combine (p₁ p₂ : Poly) : Poly :=
combineAux hugeFuel p₁ p₂
def Expr.toPoly (e : Expr) :=
go 1 e []
where
-- Implementation note: This assembles the result using difference lists
-- to avoid `++` on lists.
go (coeff : Nat) : Expr (Poly Poly)
| Expr.num k => bif k == 0 then id else ((coeff * k, fixedVar) :: ·)
| Expr.var i => ((coeff, i) :: ·)
| Expr.add a b => go coeff a go coeff b
| Expr.mulL k a
| Expr.mulR a k => bif k == 0 then id else go (coeff * k) a
| .num k => bif k == 0 then id else ((coeff * k, fixedVar) :: ·)
| .var i => ((coeff, i) :: ·)
| .add a b => go coeff a go coeff b
| .mulL k a
| .mulR a k => bif k == 0 then id else go (coeff * k) a
def Expr.toNormPoly (e : Expr) : Poly :=
e.toPoly.norm
def Expr.inc (e : Expr) : Expr :=
Expr.add e (Expr.num 1)
.add e (.num 1)
structure PolyCnstr where
eq : Bool
@@ -178,13 +148,6 @@ instance : LawfulBEq PolyCnstr where
show (eq == eq && (lhs == lhs && rhs == rhs)) = true
simp [LawfulBEq.rfl]
def PolyCnstr.mul (k : Nat) (c : PolyCnstr) : PolyCnstr :=
{ c with lhs := c.lhs.mul k, rhs := c.rhs.mul k }
def PolyCnstr.combine (c₁ c₂ : PolyCnstr) : PolyCnstr :=
let (lhs, rhs) := Poly.cancel (c₁.lhs.combine c₂.lhs) (c₁.rhs.combine c₂.rhs)
{ eq := c₁.eq && c₂.eq, lhs, rhs }
structure ExprCnstr where
eq : Bool
lhs : Expr
@@ -225,47 +188,29 @@ def ExprCnstr.toNormPoly (c : ExprCnstr) : PolyCnstr :=
let (lhs, rhs) := Poly.cancel c.lhs.toNormPoly c.rhs.toNormPoly
{ c with lhs, rhs }
abbrev Certificate := List (Nat × ExprCnstr)
def Certificate.combineHyps (c : PolyCnstr) (hs : Certificate) : PolyCnstr :=
match hs with
| [] => c
| (k, c') :: hs => combineHyps (PolyCnstr.combine c (c'.toNormPoly.mul (Nat.add k 1))) hs
def Certificate.combine (hs : Certificate) : PolyCnstr :=
match hs with
| [] => { eq := true, lhs := [], rhs := [] }
| (k, c) :: hs => combineHyps (c.toNormPoly.mul (Nat.add k 1)) hs
def Certificate.denote (ctx : Context) (c : Certificate) : Prop :=
match c with
| [] => False
| (_, c)::hs => c.denote ctx denote ctx hs
def monomialToExpr (k : Nat) (v : Var) : Expr :=
bif v == fixedVar then
Expr.num k
.num k
else bif k == 1 then
Expr.var v
.var v
else
Expr.mulL k (Expr.var v)
.mulL k (.var v)
def Poly.toExpr (p : Poly) : Expr :=
match p with
| [] => Expr.num 0
| [] => .num 0
| (k, v) :: p => go (monomialToExpr k v) p
where
go (e : Expr) (p : Poly) : Expr :=
match p with
| [] => e
| (k, v) :: p => go (Expr.add e (monomialToExpr k v)) p
| (k, v) :: p => go (.add e (monomialToExpr k v)) p
def PolyCnstr.toExpr (c : PolyCnstr) : ExprCnstr :=
{ c with lhs := c.lhs.toExpr, rhs := c.rhs.toExpr }
attribute [local simp] Nat.add_comm Nat.add_assoc Nat.add_left_comm Nat.right_distrib Nat.left_distrib Nat.mul_assoc Nat.mul_comm
attribute [local simp] Poly.denote Expr.denote Poly.insert Poly.norm Poly.norm.go Poly.cancelAux
attribute [local simp] Poly.mul Poly.mul.go
theorem Poly.denote_insert (ctx : Context) (k : Nat) (v : Var) (p : Poly) :
(p.insert k v).denote ctx = p.denote ctx + k * v.denote ctx := by
@@ -320,21 +265,11 @@ theorem Poly.denote_reverse (ctx : Context) (p : Poly) : denote ctx (List.revers
attribute [local simp] Poly.denote_reverse
theorem Poly.denote_mul (ctx : Context) (k : Nat) (p : Poly) : (p.mul k).denote ctx = k * p.denote ctx := by
simp
by_cases h : k == 0 <;> simp [h]; simp [eq_of_beq h]
by_cases h : k == 1 <;> simp [h]; simp [eq_of_beq h]
induction p with
| nil => simp
| cons kv m ih => cases kv with | _ k' v => simp [ih]
private theorem eq_of_not_blt_eq_true (h₁ : ¬ (Nat.blt x y = true)) (h₂ : ¬ (Nat.blt y x = true)) : x = y :=
have h₁ : ¬ x < y := fun h => h₁ (Nat.blt_eq.mpr h)
have h₂ : ¬ y < x := fun h => h₂ (Nat.blt_eq.mpr h)
Nat.le_antisymm (Nat.ge_of_not_lt h₂) (Nat.ge_of_not_lt h₁)
attribute [local simp] Poly.denote_mul
theorem Poly.denote_eq_cancelAux (ctx : Context) (fuel : Nat) (m₁ m₂ r₁ r₂ : Poly)
(h : denote_eq ctx (r₁.reverse ++ m₁, r₂.reverse ++ m₂)) : denote_eq ctx (cancelAux fuel m₁ m₂ r₁ r₂) := by
induction fuel generalizing m₁ m₂ r₁ r₂ with
@@ -493,21 +428,6 @@ theorem Poly.denote_le_cancel_eq (ctx : Context) (m₁ m₂ : Poly) : denote_le
attribute [local simp] Poly.denote_le_cancel_eq
theorem Poly.denote_combineAux (ctx : Context) (fuel : Nat) (p₁ p₂ : Poly) : (p₁.combineAux fuel p₂).denote ctx = p₁.denote ctx + p₂.denote ctx := by
induction fuel generalizing p₁ p₂ with simp [combineAux]
| succ fuel ih =>
split <;> simp
rename_i k₁ v₁ p₁ k₂ v₂ p₂
by_cases hltv : Nat.blt v₁ v₂ <;> simp [hltv, ih]
by_cases hgtv : Nat.blt v₂ v₁ <;> simp [hgtv, ih]
have heqv : v₁ = v₂ := eq_of_not_blt_eq_true hltv hgtv
simp [heqv]
theorem Poly.denote_combine (ctx : Context) (p₁ p₂ : Poly) : (p₁.combine p₂).denote ctx = p₁.denote ctx + p₂.denote ctx := by
simp [combine, denote_combineAux]
attribute [local simp] Poly.denote_combine
theorem Expr.denote_toPoly_go (ctx : Context) (e : Expr) :
(toPoly.go k e p).denote ctx = k * e.denote ctx + p.denote ctx := by
induction k, e using Expr.toPoly.go.induct generalizing p with
@@ -572,51 +492,6 @@ theorem ExprCnstr.denote_toNormPoly (ctx : Context) (c : ExprCnstr) : c.toNormPo
attribute [local simp] ExprCnstr.denote_toNormPoly
theorem Poly.mul.go_denote (ctx : Context) (k : Nat) (p : Poly) : (Poly.mul.go k p).denote ctx = k * p.denote ctx := by
match p with
| [] => rfl
| (k', v) :: p => simp [Poly.mul.go, go_denote]
attribute [local simp] Poly.mul.go_denote
section
attribute [-simp] Nat.right_distrib Nat.left_distrib
theorem PolyCnstr.denote_mul (ctx : Context) (k : Nat) (c : PolyCnstr) : (c.mul (k+1)).denote ctx = c.denote ctx := by
cases c; rename_i eq lhs rhs
have : k 0 k + 1 1 := by intro h; match k with | 0 => contradiction | k+1 => simp [Nat.succ.injEq]
have : ¬ (k == 0) (k + 1 == 1) = false := fun h => beq_false_of_ne (this (ne_of_beq_false (Bool.of_not_eq_true h)))
have : ¬ ((k + 1 == 0) = true) := fun h => absurd (eq_of_beq h) (Nat.succ_ne_zero k)
by_cases he : eq = true <;> simp [he, PolyCnstr.mul, PolyCnstr.denote, Poly.denote_le, Poly.denote_eq]
<;> by_cases hk : k == 0 <;> (try simp [eq_of_beq hk]) <;> simp [*] <;> apply Iff.intro <;> intro h
· exact Nat.eq_of_mul_eq_mul_left (Nat.zero_lt_succ _) h
· rw [h]
· exact Nat.le_of_mul_le_mul_left h (Nat.zero_lt_succ _)
· apply Nat.mul_le_mul_left _ h
end
attribute [local simp] PolyCnstr.denote_mul
theorem PolyCnstr.denote_combine {ctx : Context} {c₁ c₂ : PolyCnstr} (h₁ : c₁.denote ctx) (h₂ : c₂.denote ctx) : (c₁.combine c₂).denote ctx := by
cases c₁; cases c₂; rename_i eq₁ lhs₁ rhs₁ eq₂ lhs₂ rhs₂
simp [denote] at h₁ h₂
simp [PolyCnstr.combine, denote]
by_cases he₁ : eq₁ = true <;> by_cases he₂ : eq₂ = true <;> simp [he₁, he₂] at h₁ h₂ |-
· rw [Poly.denote_eq_cancel_eq]; simp [Poly.denote_eq] at h₁ h₂ |-; simp [h₁, h₂]
· rw [Poly.denote_le_cancel_eq]; simp [Poly.denote_eq, Poly.denote_le] at h₁ h₂ |-; rw [h₁]; apply Nat.add_le_add_left h₂
· rw [Poly.denote_le_cancel_eq]; simp [Poly.denote_eq, Poly.denote_le] at h₁ h₂ |-; rw [h₂]; apply Nat.add_le_add_right h₁
· rw [Poly.denote_le_cancel_eq]; simp [Poly.denote_eq, Poly.denote_le] at h₁ h₂ |-; apply Nat.add_le_add h₁ h₂
attribute [local simp] PolyCnstr.denote_combine
theorem Poly.isNum?_eq_some (ctx : Context) {p : Poly} {k : Nat} : p.isNum? = some k p.denote ctx = k := by
simp [isNum?]
split
next => intro h; injection h
next k v => by_cases h : v == fixedVar <;> simp [h]; intros; simp [Var.denote, eq_of_beq h]; assumption
next => intros; contradiction
theorem Poly.of_isZero (ctx : Context) {p : Poly} (h : isZero p = true) : p.denote ctx = 0 := by
simp [isZero] at h
split at h
@@ -637,8 +512,8 @@ theorem PolyCnstr.eq_false_of_isUnsat (ctx : Context) {c : PolyCnstr} : c.isUnsa
simp [isUnsat]
by_cases he : eq = true <;> simp [he, denote, Poly.denote_eq, Poly.denote_le, -and_imp]
· intro
| Or.inl h₁, h₂ => simp [Poly.of_isZero, h₁]; have := Nat.not_eq_zero_of_lt (Poly.of_isNonZero ctx h₂); simp [this.symm]
| Or.inr h₁, h₂ => simp [Poly.of_isZero, h₂]; have := Nat.not_eq_zero_of_lt (Poly.of_isNonZero ctx h₁); simp [this]
| Or.inl h₁, h₂ => simp [Poly.of_isZero, h₁]; have := Nat.ne_zero_of_lt (Poly.of_isNonZero ctx h₂); simp [this.symm]
| Or.inr h₁, h₂ => simp [Poly.of_isZero, h₂]; have := Nat.ne_zero_of_lt (Poly.of_isNonZero ctx h₁); simp [this]
· intro h₁, h₂
simp [Poly.of_isZero, h₂]
exact Poly.of_isNonZero ctx h₁
@@ -662,50 +537,6 @@ theorem ExprCnstr.eq_true_of_isValid (ctx : Context) (c : ExprCnstr) (h : c.toNo
simp [-eq_iff_iff] at this
assumption
theorem Certificate.of_combineHyps (ctx : Context) (c : PolyCnstr) (cs : Certificate) (h : (combineHyps c cs).denote ctx False) : c.denote ctx cs.denote ctx := by
match cs with
| [] => simp [combineHyps, denote] at *; exact h
| (k, c')::cs =>
intro h₁ h₂
have := PolyCnstr.denote_combine (ctx := ctx) (c₂ := PolyCnstr.mul (k + 1) (ExprCnstr.toNormPoly c')) h₁
simp at this
have := this h₂
have ih := of_combineHyps ctx (PolyCnstr.combine c (PolyCnstr.mul (k + 1) (ExprCnstr.toNormPoly c'))) cs
exact ih h this
theorem Certificate.of_combine (ctx : Context) (cs : Certificate) (h : cs.combine.denote ctx False) : cs.denote ctx := by
match cs with
| [] => simp [combine, PolyCnstr.denote, Poly.denote_eq] at h
| (k, c)::cs =>
simp [denote, combine] at *
intro h'
apply of_combineHyps (h := h)
simp [h']
theorem Certificate.of_combine_isUnsat (ctx : Context) (cs : Certificate) (h : cs.combine.isUnsat) : cs.denote ctx :=
have h := PolyCnstr.eq_false_of_isUnsat ctx h
of_combine ctx cs (fun h' => Eq.mp h h')
theorem denote_monomialToExpr (ctx : Context) (k : Nat) (v : Var) : (monomialToExpr k v).denote ctx = k * v.denote ctx := by
simp [monomialToExpr]
by_cases h : v == fixedVar <;> simp [h, Expr.denote]
· simp [eq_of_beq h, Var.denote]
· by_cases h : k == 1 <;> simp [h, Expr.denote]; simp [eq_of_beq h]
attribute [local simp] denote_monomialToExpr
theorem Poly.denote_toExpr_go (ctx : Context) (e : Expr) (p : Poly) : (toExpr.go e p).denote ctx = e.denote ctx + p.denote ctx := by
induction p generalizing e with
| nil => simp [toExpr.go, Poly.denote]
| cons kv p ih => cases kv; simp [toExpr.go, ih, Expr.denote, Poly.denote]
attribute [local simp] Poly.denote_toExpr_go
theorem Poly.denote_toExpr (ctx : Context) (p : Poly) : p.toExpr.denote ctx = p.denote ctx := by
match p with
| [] => simp [toExpr, Expr.denote, Poly.denote]
| (k, v) :: p => simp [toExpr, Expr.denote, Poly.denote]
theorem ExprCnstr.eq_of_toNormPoly_eq (ctx : Context) (c d : ExprCnstr) (h : c.toNormPoly == d.toPoly) : c.denote ctx = d.denote ctx := by
have h := congrArg (PolyCnstr.denote ctx) (eq_of_beq h)
simp [-eq_iff_iff] at h

View File

@@ -9,7 +9,7 @@ import Init.Data.Nat.Linear
namespace Nat
theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n) : n - power * 2 < n - power := by
have : power * 2 = power + power := by simp_arith
have : power * 2 = power + power := by simp +arith
rw [this, Nat.sub_add_eq]
exact Nat.sub_lt (Nat.zero_lt_sub_of_lt h₂) h₁

View File

@@ -134,16 +134,29 @@ theorem attachWith_map {o : Option α} (f : α → β) {P : β → Prop} {H :
fun x, h => f x, h := by
cases o <;> simp
theorem map_attach {o : Option α} (f : { x // x o } β) :
theorem map_attach_eq_pmap {o : Option α} (f : { x // x o } β) :
o.attach.map f = o.pmap (fun a (h : a o) => f a, h) (fun _ h => h) := by
cases o <;> simp
theorem map_attachWith {o : Option α} {P : α Prop} {H : (a : α), a o P a}
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
abbrev map_attach := @map_attach_eq_pmap
@[simp] theorem map_attachWith {l : Option α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f = l.attach.map fun x, h => f x, H _ h := by
cases l <;> simp_all
theorem map_attachWith_eq_pmap {o : Option α} {P : α Prop} {H : (a : α), a o P a}
(f : { x // P x } β) :
(o.attachWith P H).map f =
o.pmap (fun a (h : a o P a) => f a, h.2) (fun a h => h, H a h) := by
cases o <;> simp
@[simp]
theorem map_attach_eq_attachWith {o : Option α} {p : α Prop} (f : a, a o p a) :
o.attach.map (fun x => x.1, f x.1 x.2) = o.attachWith p f := by
cases o <;> simp_all [Function.comp_def]
theorem attach_bind {o : Option α} {f : α Option β} :
(o.bind f).attach =
o.attach.bind fun x, h => (f x).attach.map fun y, h' => y, mem_bind_iff.mpr x, h, h' := by

View File

@@ -375,6 +375,9 @@ end choice
@[simp] theorem some_or : (some a).or o = some a := rfl
@[simp] theorem none_or : none.or o = o := rfl
theorem or_eq_right_of_none {o o' : Option α} (h : o = none) : o.or o' = o' := by
cases h; simp
@[deprecated some_or (since := "2024-11-03")] theorem or_some : (some a).or o = some a := rfl
/-- This will be renamed to `or_some` once the existing deprecated lemma is removed. -/
@@ -403,6 +406,10 @@ instance : Std.Associative (or (α := α)) := ⟨@or_assoc _⟩
@[simp]
theorem or_none : or o none = o := by
cases o <;> rfl
theorem or_eq_left_of_none {o o' : Option α} (h : o' = none) : o.or o' = o := by
cases h; simp
instance : Std.LawfulIdentity (or (α := α)) none where
left_id := @none_or _
right_id := @or_none _

View File

@@ -59,4 +59,12 @@ namespace Option
o.toList.foldr f a = o.elim a (fun b => f b a) := by
cases o <;> simp
@[simp]
theorem pairwise_toList {P : α α Prop} {o : Option α} : o.toList.Pairwise P := by
cases o <;> simp
@[simp]
theorem head?_toList {o : Option α} : o.toList.head? = o := by
cases o <;> simp
end Option

View File

@@ -163,7 +163,7 @@ private def reprArray : Array String := Id.run do
private def reprFast (n : Nat) : String :=
if h : n < 128 then Nat.reprArray.get n h else
if h : n < USize.size then (USize.ofNatCore n h).repr
if h : n < USize.size then (USize.ofNatLT n h).repr
else (toDigits 10 n).asString
@[implemented_by reprFast]

View File

@@ -17,6 +17,7 @@ The type of signed 8-bit integers. This type has special support in the
compiler to make it actually 8 bits rather than wrapping a `Nat`.
-/
structure Int8 where
private ofUInt8 ::
/--
Obtain the `UInt8` that is 2's complement equivalent to the `Int8`.
-/
@@ -27,6 +28,7 @@ The type of signed 16-bit integers. This type has special support in the
compiler to make it actually 16 bits rather than wrapping a `Nat`.
-/
structure Int16 where
private ofUInt16 ::
/--
Obtain the `UInt16` that is 2's complement equivalent to the `Int16`.
-/
@@ -37,6 +39,7 @@ The type of signed 32-bit integers. This type has special support in the
compiler to make it actually 32 bits rather than wrapping a `Nat`.
-/
structure Int32 where
private ofUInt32 ::
/--
Obtain the `UInt32` that is 2's complement equivalent to the `Int32`.
-/
@@ -47,6 +50,7 @@ The type of signed 64-bit integers. This type has special support in the
compiler to make it actually 64 bits rather than wrapping a `Nat`.
-/
structure Int64 where
private ofUInt64 ::
/--
Obtain the `UInt64` that is 2's complement equivalent to the `Int64`.
-/
@@ -59,6 +63,7 @@ For example, if running on a 32-bit machine, ISize is equivalent to `Int32`.
Or on a 64-bit machine, `Int64`.
-/
structure ISize where
private ofUSize ::
/--
Obtain the `USize` that is 2's complement equivalent to the `ISize`.
-/
@@ -72,6 +77,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int8
-/
@[inline] def Int8.toBitVec (x : Int8) : BitVec 8 := x.toUInt8.toBitVec
/-- Obtains the `Int8` that is 2's complement equivalent to the `UInt8`. -/
@[inline] def UInt8.toInt8 (i : UInt8) : Int8 := Int8.ofUInt8 i
@[inline, deprecated UInt8.toInt8 (since := "2025-02-13"), inherit_doc UInt8.toInt8]
def Int8.mk (i : UInt8) : Int8 := UInt8.toInt8 i
@[extern "lean_int8_of_int"]
def Int8.ofInt (i : @& Int) : Int8 := BitVec.ofInt 8 i
@[extern "lean_int8_of_nat"]
@@ -84,7 +93,11 @@ def Int8.toInt (i : Int8) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int8.toNat (i : Int8) : Nat := i.toInt.toNat
@[inline] def Int8.toNatClampNeg (i : Int8) : Nat := i.toInt.toNat
@[inline, deprecated Int8.toNatClampNeg (since := "2025-02-13"), inherit_doc Int8.toNatClampNeg]
def Int8.toNat (i : Int8) : Nat := i.toInt.toNat
/-- Obtains the `Int8` whose 2's complement representation is the given `BitVec 8`. -/
@[inline] def Int8.ofBitVec (b : BitVec 8) : Int8 := b
@[extern "lean_int8_neg"]
def Int8.neg (i : Int8) : Int8 := -i.toBitVec
@@ -95,6 +108,24 @@ instance : OfNat Int8 n := ⟨Int8.ofNat n⟩
instance : Neg Int8 where
neg := Int8.neg
/-- The maximum value an `Int8` may attain, that is, `2^7 - 1 = 127`. -/
abbrev Int8.maxValue : Int8 := 127
/-- The minimum value an `Int8` may attain, that is, `-2^7 = -128`. -/
abbrev Int8.minValue : Int8 := -128
/-- Constructs an `Int8` from an `Int` which is known to be in bounds. -/
@[inline]
def Int8.ofIntLE (i : Int) (_hl : Int8.minValue.toInt i) (_hr : i Int8.maxValue.toInt) : Int8 :=
Int8.ofInt i
/-- Constructs an `Int8` from an `Int`, clamping if the value is too small or too large. -/
def Int8.ofIntTruncate (i : Int) : Int8 :=
if hl : Int8.minValue.toInt i then
if hr : i Int8.maxValue.toInt then
Int8.ofIntLE i hl hr
else
Int8.minValue
else
Int8.minValue
@[extern "lean_int8_add"]
def Int8.add (a b : Int8) : Int8 := a.toBitVec + b.toBitVec
@[extern "lean_int8_sub"]
@@ -172,6 +203,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int1
-/
@[inline] def Int16.toBitVec (x : Int16) : BitVec 16 := x.toUInt16.toBitVec
/-- Obtains the `Int16` that is 2's complement equivalent to the `UInt16`. -/
@[inline] def UInt16.toInt16 (i : UInt16) : Int16 := Int16.ofUInt16 i
@[inline, deprecated UInt16.toInt16 (since := "2025-02-13"), inherit_doc UInt16.toInt16]
def Int16.mk (i : UInt16) : Int16 := UInt16.toInt16 i
@[extern "lean_int16_of_int"]
def Int16.ofInt (i : @& Int) : Int16 := BitVec.ofInt 16 i
@[extern "lean_int16_of_nat"]
@@ -184,7 +219,11 @@ def Int16.toInt (i : Int16) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int16.toNat (i : Int16) : Nat := i.toInt.toNat
@[inline] def Int16.toNatClampNeg (i : Int16) : Nat := i.toInt.toNat
@[inline, deprecated Int16.toNatClampNeg (since := "2025-02-13"), inherit_doc Int16.toNatClampNeg]
def Int16.toNat (i : Int16) : Nat := i.toInt.toNat
/-- Obtains the `Int16` whose 2's complement representation is the given `BitVec 16`. -/
@[inline] def Int16.ofBitVec (b : BitVec 16) : Int16 := b
@[extern "lean_int16_to_int8"]
def Int16.toInt8 (a : Int16) : Int8 := a.toBitVec.signExtend 8
@[extern "lean_int8_to_int16"]
@@ -199,6 +238,24 @@ instance : OfNat Int16 n := ⟨Int16.ofNat n⟩
instance : Neg Int16 where
neg := Int16.neg
/-- The maximum value an `Int16` may attain, that is, `2^15 - 1 = 32767`. -/
abbrev Int16.maxValue : Int16 := 32767
/-- The minimum value an `Int16` may attain, that is, `-2^15 = -32768`. -/
abbrev Int16.minValue : Int16 := -32768
/-- Constructs an `Int16` from an `Int` which is known to be in bounds. -/
@[inline]
def Int16.ofIntLE (i : Int) (_hl : Int16.minValue.toInt i) (_hr : i Int16.maxValue.toInt) : Int16 :=
Int16.ofInt i
/-- Constructs an `Int16` from an `Int`, clamping if the value is too small or too large. -/
def Int16.ofIntTruncate (i : Int) : Int16 :=
if hl : Int16.minValue.toInt i then
if hr : i Int16.maxValue.toInt then
Int16.ofIntLE i hl hr
else
Int16.minValue
else
Int16.minValue
@[extern "lean_int16_add"]
def Int16.add (a b : Int16) : Int16 := a.toBitVec + b.toBitVec
@[extern "lean_int16_sub"]
@@ -276,6 +333,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int3
-/
@[inline] def Int32.toBitVec (x : Int32) : BitVec 32 := x.toUInt32.toBitVec
/-- Obtains the `Int32` that is 2's complement equivalent to the `UInt32`. -/
@[inline] def UInt32.toInt32 (i : UInt32) : Int32 := Int32.ofUInt32 i
@[inline, deprecated UInt32.toInt32 (since := "2025-02-13"), inherit_doc UInt32.toInt32]
def Int32.mk (i : UInt32) : Int32 := UInt32.toInt32 i
@[extern "lean_int32_of_int"]
def Int32.ofInt (i : @& Int) : Int32 := BitVec.ofInt 32 i
@[extern "lean_int32_of_nat"]
@@ -288,7 +349,11 @@ def Int32.toInt (i : Int32) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int32.toNat (i : Int32) : Nat := i.toInt.toNat
@[inline] def Int32.toNatClampNeg (i : Int32) : Nat := i.toInt.toNat
@[inline, deprecated Int32.toNatClampNeg (since := "2025-02-13"), inherit_doc Int32.toNatClampNeg]
def Int32.toNat (i : Int32) : Nat := i.toInt.toNat
/-- Obtains the `Int32` whose 2's complement representation is the given `BitVec 32`. -/
@[inline] def Int32.ofBitVec (b : BitVec 32) : Int32 := b
@[extern "lean_int32_to_int8"]
def Int32.toInt8 (a : Int32) : Int8 := a.toBitVec.signExtend 8
@[extern "lean_int32_to_int16"]
@@ -307,6 +372,24 @@ instance : OfNat Int32 n := ⟨Int32.ofNat n⟩
instance : Neg Int32 where
neg := Int32.neg
/-- The maximum value an `Int32` may attain, that is, `2^31 - 1 = 2147483647`. -/
abbrev Int32.maxValue : Int32 := 2147483647
/-- The minimum value an `Int32` may attain, that is, `-2^31 = -2147483648`. -/
abbrev Int32.minValue : Int32 := -2147483648
/-- Constructs an `Int32` from an `Int` which is known to be in bounds. -/
@[inline]
def Int32.ofIntLE (i : Int) (_hl : Int32.minValue.toInt i) (_hr : i Int32.maxValue.toInt) : Int32 :=
Int32.ofInt i
/-- Constructs an `Int32` from an `Int`, clamping if the value is too small or too large. -/
def Int32.ofIntTruncate (i : Int) : Int32 :=
if hl : Int32.minValue.toInt i then
if hr : i Int32.maxValue.toInt then
Int32.ofIntLE i hl hr
else
Int32.minValue
else
Int32.minValue
@[extern "lean_int32_add"]
def Int32.add (a b : Int32) : Int32 := a.toBitVec + b.toBitVec
@[extern "lean_int32_sub"]
@@ -384,6 +467,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int6
-/
@[inline] def Int64.toBitVec (x : Int64) : BitVec 64 := x.toUInt64.toBitVec
/-- Obtains the `Int64` that is 2's complement equivalent to the `UInt64`. -/
@[inline] def UInt64.toInt64 (i : UInt64) : Int64 := Int64.ofUInt64 i
@[inline, deprecated UInt64.toInt64 (since := "2025-02-13"), inherit_doc UInt64.toInt64]
def Int64.mk (i : UInt64) : Int64 := UInt64.toInt64 i
@[extern "lean_int64_of_int"]
def Int64.ofInt (i : @& Int) : Int64 := BitVec.ofInt 64 i
@[extern "lean_int64_of_nat"]
@@ -396,7 +483,11 @@ def Int64.toInt (i : Int64) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def Int64.toNat (i : Int64) : Nat := i.toInt.toNat
@[inline] def Int64.toNatClampNeg (i : Int64) : Nat := i.toInt.toNat
@[inline, deprecated Int64.toNatClampNeg (since := "2025-02-13"), inherit_doc Int64.toNatClampNeg]
def Int64.toNat (i : Int64) : Nat := i.toInt.toNat
/-- Obtains the `Int64` whose 2's complement representation is the given `BitVec 64`. -/
@[inline] def Int64.ofBitVec (b : BitVec 64) : Int64 := b
@[extern "lean_int64_to_int8"]
def Int64.toInt8 (a : Int64) : Int8 := a.toBitVec.signExtend 8
@[extern "lean_int64_to_int16"]
@@ -419,6 +510,24 @@ instance : OfNat Int64 n := ⟨Int64.ofNat n⟩
instance : Neg Int64 where
neg := Int64.neg
/-- The maximum value an `Int64` may attain, that is, `2^63 - 1 = 9223372036854775807`. -/
abbrev Int64.maxValue : Int64 := 9223372036854775807
/-- The minimum value an `Int64` may attain, that is, `-2^63 = -9223372036854775808`. -/
abbrev Int64.minValue : Int64 := -9223372036854775808
/-- Constructs an `Int64` from an `Int` which is known to be in bounds. -/
@[inline]
def Int64.ofIntLE (i : Int) (_hl : Int64.minValue.toInt i) (_hr : i Int64.maxValue.toInt) : Int64 :=
Int64.ofInt i
/-- Constructs an `Int64` from an `Int`, clamping if the value is too small or too large. -/
def Int64.ofIntTruncate (i : Int) : Int64 :=
if hl : Int64.minValue.toInt i then
if hr : i Int64.maxValue.toInt then
Int64.ofIntLE i hl hr
else
Int64.minValue
else
Int64.minValue
@[extern "lean_int64_add"]
def Int64.add (a b : Int64) : Int64 := a.toBitVec + b.toBitVec
@[extern "lean_int64_sub"]
@@ -496,6 +605,10 @@ Obtain the `BitVec` that contains the 2's complement representation of the `ISiz
-/
@[inline] def ISize.toBitVec (x : ISize) : BitVec System.Platform.numBits := x.toUSize.toBitVec
/-- Obtains the `ISize` that is 2's complement equivalent to the `USize`. -/
@[inline] def USize.toISize (i : USize) : ISize := ISize.ofUSize i
@[inline, deprecated USize.toISize (since := "2025-02-13"), inherit_doc USize.toISize]
def ISize.mk (i : USize) : ISize := USize.toISize i
@[extern "lean_isize_of_int"]
def ISize.ofInt (i : @& Int) : ISize := BitVec.ofInt System.Platform.numBits i
@[extern "lean_isize_of_nat"]
@@ -508,16 +621,28 @@ def ISize.toInt (i : ISize) : Int := i.toBitVec.toInt
This function has the same behavior as `Int.toNat` for negative numbers.
If you want to obtain the 2's complement representation use `toBitVec`.
-/
@[inline] def ISize.toNat (i : ISize) : Nat := i.toInt.toNat
@[inline] def ISize.toNatClampNeg (i : ISize) : Nat := i.toInt.toNat
@[inline, deprecated ISize.toNatClampNeg (since := "2025-02-13"), inherit_doc ISize.toNatClampNeg]
def ISize.toNat (i : ISize) : Nat := i.toInt.toNat
/-- Obtains the `ISize` whose 2's complement representation is the given `BitVec`. -/
@[inline] def ISize.ofBitVec (b : BitVec System.Platform.numBits) : ISize := b
@[extern "lean_isize_to_int8"]
def ISize.toInt8 (a : ISize) : Int8 := a.toBitVec.signExtend 8
@[extern "lean_isize_to_int16"]
def ISize.toInt16 (a : ISize) : Int16 := a.toBitVec.signExtend 16
@[extern "lean_isize_to_int32"]
def ISize.toInt32 (a : ISize) : Int32 := a.toBitVec.signExtend 32
/--
Upcast `ISize` to `Int64`. This function is losless as `ISize` is either `Int32` or `Int64`.
Upcasts `ISize` to `Int64`. This function is lossless as `ISize` is either `Int32` or `Int64`.
-/
@[extern "lean_isize_to_int64"]
def ISize.toInt64 (a : ISize) : Int64 := a.toBitVec.signExtend 64
@[extern "lean_int8_to_isize"]
def Int8.toISize (a : Int8) : ISize := a.toBitVec.signExtend System.Platform.numBits
@[extern "lean_int16_to_isize"]
def Int16.toISize (a : Int16) : ISize := a.toBitVec.signExtend System.Platform.numBits
/--
Upcast `Int32` to `ISize`. This function is losless as `ISize` is either `Int32` or `Int64`.
Upcasts `Int32` to `ISize`. This function is lossless as `ISize` is either `Int32` or `Int64`.
-/
@[extern "lean_int32_to_isize"]
def Int32.toISize (a : Int32) : ISize := a.toBitVec.signExtend System.Platform.numBits
@@ -533,6 +658,26 @@ instance : OfNat ISize n := ⟨ISize.ofNat n⟩
instance : Neg ISize where
neg := ISize.neg
/-- The maximum value an `ISize` may attain, that is, `2^(System.Platform.numBits - 1) - 1`. -/
abbrev ISize.maxValue : ISize := .ofInt (2 ^ (System.Platform.numBits - 1) - 1)
-- 9223372036854775807
/-- The minimum value an `ISize` may attain, that is, `-2^(System.Platform.numBits - 1)`. -/
abbrev ISize.minValue : ISize := .ofInt (2 ^ (System.Platform.numBits - 1))
/-- Constructs an `ISize` from an `Int` which is known to be in bounds. -/
@[inline]
def ISize.ofIntLE (i : Int) (_hl : ISize.minValue.toInt i) (_hr : i ISize.maxValue.toInt) : ISize :=
ISize.ofInt i
/-- Constructs an `ISize` from an `Int`, clamping if the value is too small or too large. -/
def ISize.ofIntTruncate (i : Int) : ISize :=
if hl : ISize.minValue.toInt i then
if hr : i ISize.maxValue.toInt then
ISize.ofIntLE i hl hr
else
ISize.minValue
else
ISize.minValue
@[extern "lean_isize_add"]
def ISize.add (a b : ISize) : ISize := a.toBitVec + b.toBitVec
@[extern "lean_isize_sub"]

View File

@@ -9,6 +9,15 @@ import Init.Data.BitVec.Basic
open Nat
/-- Converts a `Fin UInt8.size` into the corresponding `UInt8`. -/
@[inline] def UInt8.ofFin (a : Fin UInt8.size) : UInt8 := a
@[deprecated UInt8.ofBitVec (since := "2025-02-12"), inherit_doc UInt8.ofBitVec]
def UInt8.mk (bitVec : BitVec 8) : UInt8 :=
UInt8.ofBitVec bitVec
@[inline, deprecated UInt8.ofNatLT (since := "2025-02-13"), inherit_doc UInt8.ofNatLT]
def UInt8.ofNatCore (n : Nat) (h : n < UInt8.size) : UInt8 :=
UInt8.ofNatLT n h
@[extern "lean_uint8_add"]
def UInt8.add (a b : UInt8) : UInt8 := a.toBitVec + b.toBitVec
@[extern "lean_uint8_sub"]
@@ -20,7 +29,7 @@ def UInt8.div (a b : UInt8) : UInt8 := ⟨BitVec.udiv a.toBitVec b.toBitVec⟩
@[extern "lean_uint8_mod"]
def UInt8.mod (a b : UInt8) : UInt8 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt8.mod (since := "2024-09-23")]
def UInt8.modn (a : UInt8) (n : Nat) : UInt8 := Fin.modn a.val n
def UInt8.modn (a : UInt8) (n : Nat) : UInt8 := Fin.modn a.toFin n
@[extern "lean_uint8_land"]
def UInt8.land (a b : UInt8) : UInt8 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint8_lor"]
@@ -72,6 +81,15 @@ instance (a b : UInt8) : Decidable (a ≤ b) := UInt8.decLe a b
instance : Max UInt8 := maxOfLe
instance : Min UInt8 := minOfLe
/-- Converts a `Fin UInt16.size` into the corresponding `UInt16`. -/
@[inline] def UInt16.ofFin (a : Fin UInt16.size) : UInt16 := a
@[deprecated UInt16.ofBitVec (since := "2025-02-12"), inherit_doc UInt16.ofBitVec]
def UInt16.mk (bitVec : BitVec 16) : UInt16 :=
UInt16.ofBitVec bitVec
@[inline, deprecated UInt16.ofNatLT (since := "2025-02-13"), inherit_doc UInt16.ofNatLT]
def UInt16.ofNatCore (n : Nat) (h : n < UInt16.size) : UInt16 :=
UInt16.ofNatLT n h
@[extern "lean_uint16_add"]
def UInt16.add (a b : UInt16) : UInt16 := a.toBitVec + b.toBitVec
@[extern "lean_uint16_sub"]
@@ -83,7 +101,7 @@ def UInt16.div (a b : UInt16) : UInt16 := ⟨BitVec.udiv a.toBitVec b.toBitVec
@[extern "lean_uint16_mod"]
def UInt16.mod (a b : UInt16) : UInt16 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt16.mod (since := "2024-09-23")]
def UInt16.modn (a : UInt16) (n : Nat) : UInt16 := Fin.modn a.val n
def UInt16.modn (a : UInt16) (n : Nat) : UInt16 := Fin.modn a.toFin n
@[extern "lean_uint16_land"]
def UInt16.land (a b : UInt16) : UInt16 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint16_lor"]
@@ -137,6 +155,15 @@ instance (a b : UInt16) : Decidable (a ≤ b) := UInt16.decLe a b
instance : Max UInt16 := maxOfLe
instance : Min UInt16 := minOfLe
/-- Converts a `Fin UInt32.size` into the corresponding `UInt32`. -/
@[inline] def UInt32.ofFin (a : Fin UInt32.size) : UInt32 := a
@[deprecated UInt32.ofBitVec (since := "2025-02-12"), inherit_doc UInt32.ofBitVec]
def UInt32.mk (bitVec : BitVec 32) : UInt32 :=
UInt32.ofBitVec bitVec
@[inline, deprecated UInt32.ofNatLT (since := "2025-02-13"), inherit_doc UInt32.ofNatLT]
def UInt32.ofNatCore (n : Nat) (h : n < UInt32.size) : UInt32 :=
UInt32.ofNatLT n h
@[extern "lean_uint32_add"]
def UInt32.add (a b : UInt32) : UInt32 := a.toBitVec + b.toBitVec
@[extern "lean_uint32_sub"]
@@ -148,7 +175,7 @@ def UInt32.div (a b : UInt32) : UInt32 := ⟨BitVec.udiv a.toBitVec b.toBitVec
@[extern "lean_uint32_mod"]
def UInt32.mod (a b : UInt32) : UInt32 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt32.mod (since := "2024-09-23")]
def UInt32.modn (a : UInt32) (n : Nat) : UInt32 := Fin.modn a.val n
def UInt32.modn (a : UInt32) (n : Nat) : UInt32 := Fin.modn a.toFin n
@[extern "lean_uint32_land"]
def UInt32.land (a b : UInt32) : UInt32 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint32_lor"]
@@ -187,6 +214,15 @@ instance : ShiftRight UInt32 := ⟨UInt32.shiftRight⟩
@[extern "lean_bool_to_uint32"]
def Bool.toUInt32 (b : Bool) : UInt32 := if b then 1 else 0
/-- Converts a `Fin UInt64.size` into the corresponding `UInt64`. -/
@[inline] def UInt64.ofFin (a : Fin UInt64.size) : UInt64 := a
@[deprecated UInt64.ofBitVec (since := "2025-02-12"), inherit_doc UInt64.ofBitVec]
def UInt64.mk (bitVec : BitVec 64) : UInt64 :=
UInt64.ofBitVec bitVec
@[inline, deprecated UInt64.ofNatLT (since := "2025-02-13"), inherit_doc UInt64.ofNatLT]
def UInt64.ofNatCore (n : Nat) (h : n < UInt64.size) : UInt64 :=
UInt64.ofNatLT n h
@[extern "lean_uint64_add"]
def UInt64.add (a b : UInt64) : UInt64 := a.toBitVec + b.toBitVec
@[extern "lean_uint64_sub"]
@@ -198,7 +234,7 @@ def UInt64.div (a b : UInt64) : UInt64 := ⟨BitVec.udiv a.toBitVec b.toBitVec
@[extern "lean_uint64_mod"]
def UInt64.mod (a b : UInt64) : UInt64 := BitVec.umod a.toBitVec b.toBitVec
@[deprecated UInt64.mod (since := "2024-09-23")]
def UInt64.modn (a : UInt64) (n : Nat) : UInt64 := Fin.modn a.val n
def UInt64.modn (a : UInt64) (n : Nat) : UInt64 := Fin.modn a.toFin n
@[extern "lean_uint64_land"]
def UInt64.land (a b : UInt64) : UInt64 := a.toBitVec &&& b.toBitVec
@[extern "lean_uint64_lor"]
@@ -250,6 +286,15 @@ instance (a b : UInt64) : Decidable (a ≤ b) := UInt64.decLe a b
instance : Max UInt64 := maxOfLe
instance : Min UInt64 := minOfLe
/-- Converts a `Fin USize.size` into the corresponding `USize`. -/
@[inline] def USize.ofFin (a : Fin USize.size) : USize := a
@[deprecated USize.ofBitVec (since := "2025-02-12"), inherit_doc USize.ofBitVec]
def USize.mk (bitVec : BitVec System.Platform.numBits) : USize :=
USize.ofBitVec bitVec
@[inline, deprecated USize.ofNatLT (since := "2025-02-13"), inherit_doc USize.ofNatLT]
def USize.ofNatCore (n : Nat) (h : n < USize.size) : USize :=
USize.ofNatLT n h
theorem usize_size_le : USize.size 18446744073709551616 := by
cases usize_size_eq <;> next h => rw [h]; decide
@@ -263,7 +308,7 @@ def USize.div (a b : USize) : USize := ⟨a.toBitVec / b.toBitVec⟩
@[extern "lean_usize_mod"]
def USize.mod (a b : USize) : USize := a.toBitVec % b.toBitVec
@[deprecated USize.mod (since := "2024-09-23")]
def USize.modn (a : USize) (n : Nat) : USize := Fin.modn a.val n
def USize.modn (a : USize) (n : Nat) : USize := Fin.modn a.toFin n
@[extern "lean_usize_land"]
def USize.land (a b : USize) : USize := a.toBitVec &&& b.toBitVec
@[extern "lean_usize_lor"]
@@ -281,7 +326,7 @@ This function is overridden with a native implementation.
-/
@[extern "lean_usize_of_nat"]
def USize.ofNat32 (n : @& Nat) (h : n < 4294967296) : USize :=
USize.ofNatCore n (Nat.lt_of_lt_of_le h le_usize_size)
USize.ofNatLT n (Nat.lt_of_lt_of_le h le_usize_size)
@[extern "lean_uint8_to_usize"]
def UInt8.toUSize (a : UInt8) : USize :=
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
@@ -306,7 +351,7 @@ This function is overridden with a native implementation.
-/
@[extern "lean_usize_to_uint64"]
def USize.toUInt64 (a : USize) : UInt64 :=
UInt64.ofNatCore a.toBitVec.toNat (Nat.lt_of_lt_of_le a.toBitVec.isLt usize_size_le)
UInt64.ofNatLT a.toBitVec.toNat (Nat.lt_of_lt_of_le a.toBitVec.isLt usize_size_le)
instance : Mul USize := USize.mul
instance : Mod USize := USize.mod

View File

@@ -16,18 +16,40 @@ This file thus breaks the import cycle that would be created by this dependency.
open Nat
def UInt8.val (x : UInt8) : Fin UInt8.size := x.toBitVec.toFin
/-- Converts a `UInt8` into the corresponding `Fin UInt8.size`. -/
def UInt8.toFin (x : UInt8) : Fin UInt8.size := x.toBitVec.toFin
@[deprecated UInt8.toFin (since := "2025-02-12"), inherit_doc UInt8.toFin]
def UInt8.val (x : UInt8) : Fin UInt8.size := x.toFin
@[extern "lean_uint8_of_nat"]
def UInt8.ofNat (n : @& Nat) : UInt8 := BitVec.ofNat 8 n
/--
Converts the given natural number to `UInt8`, but returns `2^8 - 1` for natural numbers `>= 2^8`.
-/
def UInt8.ofNatTruncate (n : Nat) : UInt8 :=
if h : n < UInt8.size then
UInt8.ofNatLT n h
else
UInt8.ofNatLT (UInt8.size - 1) (by decide)
abbrev Nat.toUInt8 := UInt8.ofNat
@[extern "lean_uint8_to_nat"]
def UInt8.toNat (n : UInt8) : Nat := n.toBitVec.toNat
instance UInt8.instOfNat : OfNat UInt8 n := UInt8.ofNat n
def UInt16.val (x : UInt16) : Fin UInt16.size := x.toBitVec.toFin
/-- Converts a `UInt16` into the corresponding `Fin UInt16.size`. -/
def UInt16.toFin (x : UInt16) : Fin UInt16.size := x.toBitVec.toFin
@[deprecated UInt16.toFin (since := "2025-02-12"), inherit_doc UInt16.toFin]
def UInt16.val (x : UInt16) : Fin UInt16.size := x.toFin
@[extern "lean_uint16_of_nat"]
def UInt16.ofNat (n : @& Nat) : UInt16 := BitVec.ofNat 16 n
/--
Converts the given natural number to `UInt16`, but returns `2^16 - 1` for natural numbers `>= 2^16`.
-/
def UInt16.ofNatTruncate (n : Nat) : UInt16 :=
if h : n < UInt16.size then
UInt16.ofNatLT n h
else
UInt16.ofNatLT (UInt16.size - 1) (by decide)
abbrev Nat.toUInt16 := UInt16.ofNat
@[extern "lean_uint16_to_nat"]
def UInt16.toNat (n : UInt16) : Nat := n.toBitVec.toNat
@@ -38,19 +60,22 @@ def UInt8.toUInt16 (a : UInt8) : UInt16 := ⟨⟨a.toNat, Nat.lt_trans a.toBitVe
instance UInt16.instOfNat : OfNat UInt16 n := UInt16.ofNat n
def UInt32.val (x : UInt32) : Fin UInt32.size := x.toBitVec.toFin
/-- Converts a `UInt32` into the corresponding `Fin UInt32.size`. -/
def UInt32.toFin (x : UInt32) : Fin UInt32.size := x.toBitVec.toFin
@[deprecated UInt32.toFin (since := "2025-02-12"), inherit_doc UInt32.toFin]
def UInt32.val (x : UInt32) : Fin UInt32.size := x.toFin
@[extern "lean_uint32_of_nat"]
def UInt32.ofNat (n : @& Nat) : UInt32 := BitVec.ofNat 32 n
@[extern "lean_uint32_of_nat"]
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := BitVec.ofNatLt n h
@[inline, deprecated UInt32.ofNatLT (since := "2025-02-13"), inherit_doc UInt32.ofNatLT]
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := UInt32.ofNatLT n h
/--
Converts the given natural number to `UInt32`, but returns `2^32 - 1` for natural numbers `>= 2^32`.
-/
def UInt32.ofNatTruncate (n : Nat) : UInt32 :=
if h : n < UInt32.size then
UInt32.ofNat' n h
UInt32.ofNatLT n h
else
UInt32.ofNat' (UInt32.size - 1) (by decide)
UInt32.ofNatLT (UInt32.size - 1) (by decide)
abbrev Nat.toUInt32 := UInt32.ofNat
@[extern "lean_uint32_to_uint8"]
def UInt32.toUInt8 (a : UInt32) : UInt8 := a.toNat.toUInt8
@@ -63,19 +88,38 @@ def UInt16.toUInt32 (a : UInt16) : UInt32 := ⟨⟨a.toNat, Nat.lt_trans a.toBit
instance UInt32.instOfNat : OfNat UInt32 n := UInt32.ofNat n
theorem UInt32.ofNatLT_lt_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
n < m UInt32.ofNatLT n h1 < UInt32.ofNat m := by
simp only [(· < ·), BitVec.toNat, ofNatLT, BitVec.ofNatLT, ofNat, BitVec.ofNat, Fin.ofNat',
Nat.mod_eq_of_lt h2, imp_self]
@[deprecated UInt32.ofNatLT_lt_of_lt (since := "2025-02-13")]
theorem UInt32.ofNat'_lt_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
n < m UInt32.ofNat' n h1 < UInt32.ofNat m := by
simp only [(· < ·), BitVec.toNat, ofNat', BitVec.ofNatLt, ofNat, BitVec.ofNat, Fin.ofNat',
n < m UInt32.ofNatLT n h1 < UInt32.ofNat m := UInt32.ofNatLT_lt_of_lt h1 h2
theorem UInt32.lt_ofNatLT_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
m < n UInt32.ofNat m < UInt32.ofNatLT n h1 := by
simp only [(· < ·), BitVec.toNat, ofNatLT, BitVec.ofNatLT, ofNat, BitVec.ofNat, Fin.ofNat',
Nat.mod_eq_of_lt h2, imp_self]
@[deprecated UInt32.lt_ofNatLT_of_lt (since := "2025-02-13")]
theorem UInt32.lt_ofNat'_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt32.size) :
m < n UInt32.ofNat m < UInt32.ofNat' n h1 := by
simp only [(· < ·), BitVec.toNat, ofNat', BitVec.ofNatLt, ofNat, BitVec.ofNat, Fin.ofNat',
Nat.mod_eq_of_lt h2, imp_self]
m < n UInt32.ofNat m < UInt32.ofNatLT n h1 := UInt32.lt_ofNatLT_of_lt h1 h2
def UInt64.val (x : UInt64) : Fin UInt64.size := x.toBitVec.toFin
/-- Converts a `UInt64` into the corresponding `Fin UInt64.size`. -/
def UInt64.toFin (x : UInt64) : Fin UInt64.size := x.toBitVec.toFin
@[deprecated UInt64.toFin (since := "2025-02-12"), inherit_doc UInt64.toFin]
def UInt64.val (x : UInt64) : Fin UInt64.size := x.toFin
@[extern "lean_uint64_of_nat"]
def UInt64.ofNat (n : @& Nat) : UInt64 := BitVec.ofNat 64 n
/--
Converts the given natural number to `UInt64`, but returns `2^64 - 1` for natural numbers `>= 2^64`.
-/
def UInt64.ofNatTruncate (n : Nat) : UInt64 :=
if h : n < UInt64.size then
UInt64.ofNatLT n h
else
UInt64.ofNatLT (UInt64.size - 1) (by decide)
abbrev Nat.toUInt64 := UInt64.ofNat
@[extern "lean_uint64_to_nat"]
def UInt64.toNat (n : UInt64) : Nat := n.toBitVec.toNat
@@ -97,9 +141,21 @@ instance UInt64.instOfNat : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
@[deprecated usize_size_pos (since := "2024-11-24")] theorem usize_size_gt_zero : USize.size > 0 :=
usize_size_pos
def USize.val (x : USize) : Fin USize.size := x.toBitVec.toFin
/-- Converts a `USize` into the corresponding `Fin USize.size`. -/
def USize.toFin (x : USize) : Fin USize.size := x.toBitVec.toFin
@[deprecated USize.toFin (since := "2025-02-12"), inherit_doc USize.toFin]
def USize.val (x : USize) : Fin USize.size := x.toFin
@[extern "lean_usize_of_nat"]
def USize.ofNat (n : @& Nat) : USize := BitVec.ofNat _ n
/--
Converts the given natural number to `USize`, but returns `USize.size - 1` (i.e., `2^64 - 1` or
`2^32 - 1` depending on the platform) for natural numbers `>= USize.size`.
-/
def USize.ofNatTruncate (n : Nat) : USize :=
if h : n < USize.size then
USize.ofNatLT n h
else
USize.ofNatLT (USize.size - 1) (Nat.pred_lt (Nat.ne_zero_of_lt usize_size_pos))
abbrev Nat.toUSize := USize.ofNat
@[extern "lean_usize_to_nat"]
def USize.toNat (n : USize) : Nat := n.toBitVec.toNat

View File

@@ -22,17 +22,29 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
theorem mod_def (a b : $typeName) : a % b = a.toBitVec % b.toBitVec := rfl
theorem add_def (a b : $typeName) : a + b = a.toBitVec + b.toBitVec := rfl
@[simp] theorem toNat_mk : (mk a).toNat = a.toNat := rfl
@[simp] theorem toNat_ofBitVec : (ofBitVec a).toNat = a.toNat := rfl
@[deprecated toNat_ofBitVec (since := "2025-02-12")]
theorem toNat_mk : (ofBitVec a).toNat = a.toNat := rfl
@[simp] theorem toNat_ofNat {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
@[simp] theorem toNat_ofNatCore {n : Nat} {h : n < size} : (ofNatCore n h).toNat = n := BitVec.toNat_ofNatLt ..
@[simp] theorem toNat_ofNatLT {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
@[simp] theorem val_val_eq_toNat (x : $typeName) : x.val.val = x.toNat := rfl
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
theorem toNat_ofNatCore {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
@[simp] theorem toFin_val_eq_toNat (x : $typeName) : x.toFin.val = x.toNat := rfl
@[deprecated toFin_val_eq_toNat (since := "2025-02-12")]
theorem val_val_eq_toNat (x : $typeName) : x.toFin.val = x.toNat := rfl
theorem toNat_toBitVec_eq_toNat (x : $typeName) : x.toBitVec.toNat = x.toNat := rfl
@[simp] theorem mk_toBitVec_eq : (a : $typeName), mk a.toBitVec = a
@[simp] theorem ofBitVec_toBitVec_eq : (a : $typeName), ofBitVec a.toBitVec = a
| _, _ => rfl
@[deprecated ofBitVec_toBitVec_eq (since := "2025-02-12")]
theorem mk_toBitVec_eq : (a : $typeName), ofBitVec a.toBitVec = a
| _, _ => rfl
theorem toBitVec_eq_of_lt {a : Nat} : a < size (ofNat a).toBitVec.toNat = a :=
@@ -79,13 +91,21 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
protected theorem eq_iff_toBitVec_eq {a b : $typeName} : a = b a.toBitVec = b.toBitVec :=
Iff.intro toBitVec_eq_of_eq eq_of_toBitVec_eq
open $typeName (eq_of_toBitVec_eq) in
protected theorem eq_of_val_eq {a b : $typeName} (h : a.val = b.val) : a = b := by
rcases a with _; rcases b with _; simp_all [val]
open $typeName (eq_of_toBitVec_eq toFin) in
protected theorem eq_of_toFin_eq {a b : $typeName} (h : a.toFin = b.toFin) : a = b := by
rcases a with _; rcases b with _; simp_all [toFin]
open $typeName (eq_of_toFin_eq) in
@[deprecated eq_of_toFin_eq (since := "2025-02-12")]
protected theorem eq_of_val_eq {a b : $typeName} (h : a.toFin = b.toFin) : a = b :=
eq_of_toFin_eq h
open $typeName (eq_of_val_eq) in
protected theorem val_inj {a b : $typeName} : a.val = b.val a = b :=
Iff.intro eq_of_val_eq (congrArg val)
open $typeName (eq_of_toFin_eq) in
protected theorem toFin_inj {a b : $typeName} : a.toFin = b.toFin a = b :=
Iff.intro eq_of_toFin_eq (congrArg toFin)
open $typeName (toFin_inj) in
@[deprecated toFin_inj (since := "2025-02-12")]
protected theorem val_inj {a b : $typeName} : a.toFin = b.toFin a = b :=
toFin_inj
open $typeName (eq_of_toBitVec_eq) in
protected theorem toBitVec_ne_of_ne {a b : $typeName} (h : a b) : a.toBitVec b.toBitVec :=
@@ -171,13 +191,18 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
simp [Nat.mod_eq_of_lt x.toNat_lt_size]
@[simp]
theorem val_ofNat (n : Nat) : val (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
theorem toFin_ofNat (n : Nat) : toFin (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[deprecated toFin_ofNat (since := "2025-02-12")]
theorem val_ofNat (n : Nat) : toFin (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
@[simp, int_toBitVec]
theorem toBitVec_ofNat (n : Nat) : toBitVec (no_index (OfNat.ofNat n)) = BitVec.ofNat _ n := rfl
@[simp]
theorem mk_ofNat (n : Nat) : mk (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
theorem ofBitVec_ofNat (n : Nat) : ofBitVec (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
@[deprecated ofBitVec_ofNat (since := "2025-02-12")]
theorem mk_ofNat (n : Nat) : ofBitVec (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
)
if let some nbits := bits.raw.isNatLit? then

View File

@@ -7,16 +7,16 @@ prelude
import Init.Data.Fin.Log2
@[extern "lean_uint8_log2"]
def UInt8.log2 (a : UInt8) : UInt8 := Fin.log2 a.val
def UInt8.log2 (a : UInt8) : UInt8 := Fin.log2 a.toFin
@[extern "lean_uint16_log2"]
def UInt16.log2 (a : UInt16) : UInt16 := Fin.log2 a.val
def UInt16.log2 (a : UInt16) : UInt16 := Fin.log2 a.toFin
@[extern "lean_uint32_log2"]
def UInt32.log2 (a : UInt32) : UInt32 := Fin.log2 a.val
def UInt32.log2 (a : UInt32) : UInt32 := Fin.log2 a.toFin
@[extern "lean_uint64_log2"]
def UInt64.log2 (a : UInt64) : UInt64 := Fin.log2 a.val
def UInt64.log2 (a : UInt64) : UInt64 := Fin.log2 a.toFin
@[extern "lean_usize_log2"]
def USize.log2 (a : USize) : USize := Fin.log2 a.val
def USize.log2 (a : USize) : USize := Fin.log2 a.toFin

View File

@@ -16,3 +16,4 @@ import Init.Data.Vector.Range
import Init.Data.Vector.Erase
import Init.Data.Vector.Monadic
import Init.Data.Vector.InsertIdx
import Init.Data.Vector.Extract

View File

@@ -81,7 +81,7 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
funext α β n p f L h'
rcases L with L, rfl
simp only [pmap, pmapImpl, attachWith_mk, map_mk, Array.map_attachWith, eq_mk]
simp only [pmap, pmapImpl, attachWith_mk, map_mk, Array.map_attachWith_eq_pmap, eq_mk]
apply Array.pmap_congr_left
intro a m h₁ h₂
congr
@@ -133,7 +133,7 @@ theorem attachWith_congr {l₁ l₂ : Vector α n} (w : l₁ = l₂) {P : α
(l.push a).attach =
(l.attach.map (fun x, h => x, mem_push_of_mem a h)).push a, by simp := by
rcases l with l, rfl
simp [Array.map_attachWith]
simp [Array.map_attach_eq_pmap]
@[simp] theorem attachWith_push {a : α} {l : Vector α n} {P : α Prop} {H : x l.push a, P x} :
(l.push a).attachWith P H =
@@ -145,7 +145,7 @@ theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l : Vector
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
rcases l with l, rfl
simp only [pmap_mk, Array.pmap_eq_map_attach, attach_mk, map_mk, eq_mk]
rw [Array.map_attach, Array.map_attachWith]
rw [Array.map_attach_eq_pmap, Array.map_attachWith]
ext i hi₁ hi₂ <;> simp
@[simp]
@@ -299,7 +299,13 @@ theorem attachWith_map {l : Vector α n} (f : α → β) {P : β → Prop} {H :
rcases l with l, rfl
simp [Array.attachWith_map]
theorem map_attachWith {l : Vector α n} {P : α Prop} {H : (a : α), a l P a}
@[simp] theorem map_attachWith {l : Vector α n} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f = l.attach.map fun x, h => f x, H _ h := by
rcases l with l, rfl
simp [Array.map_attachWith]
theorem map_attachWith_eq_pmap {l : Vector α n} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f =
l.pmap (fun a (h : a l P a) => f a, H _ h.1) (fun a h => h, H a h) := by
@@ -307,11 +313,14 @@ theorem map_attachWith {l : Vector α n} {P : α → Prop} {H : ∀ (a : α), a
ext <;> simp
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
theorem map_attach {l : Vector α n} (f : { x // x l } β) :
theorem map_attach_eq_pmap {l : Vector α n} (f : { x // x l } β) :
l.attach.map f = l.pmap (fun a h => f a, h) (fun _ => id) := by
cases l
ext <;> simp
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
abbrev map_attach := @map_attach_eq_pmap
theorem pmap_pmap {p : α Prop} {q : β Prop} (g : a, p a β) (f : b, q b γ) (l : Vector α n) (H₁ H₂) :
pmap f (pmap g l H₁) H₂ =
pmap (α := { x // x l }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) l.attach
@@ -339,7 +348,7 @@ theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ : Vect
ys.attach.map (fun y, h => (y, mem_append_right xs h : { y // y xs ++ ys })) := by
rcases xs with xs, rfl
rcases ys with ys, rfl
simp [Array.map_attachWith]
simp [Array.map_attach_eq_pmap]
@[simp] theorem attachWith_append {P : α Prop} {xs : Vector α n} {ys : Vector α m}
{H : (a : α), a xs ++ ys P a} :
@@ -379,7 +388,7 @@ theorem reverse_attachWith {P : α → Prop} {xs : Vector α n}
theorem reverse_attach (xs : Vector α n) :
xs.attach.reverse = xs.reverse.attach.map fun x, h => x, by simpa using h := by
cases xs
simp [Array.map_attachWith]
simp [Array.map_attach_eq_pmap]
@[simp] theorem back?_pmap {P : α Prop} (f : (a : α) P a β) (xs : Vector α n)
(H : (a : α), a xs P a) :

View File

@@ -31,7 +31,7 @@ abbrev Array.toVector (xs : Array α) : Vector α xs.size := .mk xs rfl
namespace Vector
/-- Syntax for `Vector α n` -/
syntax "#v[" withoutPosition(sepBy(term, ", ")) "]" : term
syntax (name := «term#v[_,]») "#v[" withoutPosition(term,*,?) "]" : term
open Lean in
macro_rules

View File

@@ -0,0 +1,166 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Vector.Lemmas
import Init.Data.Array.Extract
/-!
# Lemmas about `Vector.extract`
-/
open Nat
namespace Vector
/-! ### extract -/
@[simp] theorem extract_of_size_lt {as : Vector α n} {i j : Nat} (h : n < j) :
as.extract i j = (as.extract i n).cast (by omega) := by
rcases as with as, rfl
simp [h]
@[simp]
theorem extract_push {as : Vector α n} {b : α} {start stop : Nat} (h : stop n) :
(as.push b).extract start stop = (as.extract start stop).cast (by omega) := by
rcases as with as, rfl
simp [h]
@[simp]
theorem extract_eq_pop {as : Vector α n} {stop : Nat} (h : stop = n - 1) :
as.extract 0 stop = as.pop.cast (by omega) := by
rcases as with as, rfl
simp [h]
@[simp]
theorem extract_append_extract {as : Vector α n} {i j k : Nat} :
as.extract i j ++ as.extract j k =
(as.extract (min i j) (max j k)).cast (by omega) := by
rcases as with as, rfl
simp
@[simp]
theorem push_extract_getElem {as : Vector α n} {i j : Nat} (h : j < n) :
(as.extract i j).push as[j] = (as.extract (min i j) (j + 1)).cast (by omega) := by
rcases as with as, rfl
simp [h]
theorem extract_succ_right {as : Vector α n} {i j : Nat} (w : i < j + 1) (h : j < n) :
as.extract i (j + 1) = ((as.extract i j).push as[j]).cast (by omega) := by
rcases as with as, rfl
simp [Array.extract_succ_right, w, h]
theorem extract_sub_one {as : Vector α n} {i j : Nat} (h : j < n) :
as.extract i (j - 1) = (as.extract i j).pop.cast (by omega) := by
rcases as with as, rfl
simp [Array.extract_sub_one, h]
@[simp]
theorem getElem?_extract_of_lt {as : Vector α n} {i j k : Nat} (h : k < min j n - i) :
(as.extract i j)[k]? = some (as[i + k]'(by omega)) := by
simp [getElem?_extract, h]
theorem getElem?_extract_of_succ {as : Vector α n} {j : Nat} :
(as.extract 0 (j + 1))[j]? = as[j]? := by
simp only [Nat.sub_zero]
erw [getElem?_extract] -- Why does this not fire by `simp` or `rw`?
by_cases h : j < n
· rw [if_pos (by omega)]
simp
· rw [if_neg (by omega)]
simp_all
@[simp] theorem extract_extract {as : Vector α n} {i j k l : Nat} :
(as.extract i j).extract k l = (as.extract (i + k) (min (i + l) j)).cast (by omega) := by
rcases as with as, rfl
simp
theorem extract_set {as : Vector α n} {i j k : Nat} (h : k < n) {a : α} :
(as.set k a).extract i j =
if _ : k < i then
as.extract i j
else if _ : k < min j as.size then
(as.extract i j).set (k - i) a (by omega)
else as.extract i j := by
rcases as with as, rfl
simp only [set_mk, extract_mk, Array.extract_set]
split
· simp
· split <;> simp
theorem set_extract {as : Vector α n} {i j k : Nat} (h : k < min j n - i) {a : α} :
(as.extract i j).set k a = (as.set (i + k) a).extract i j := by
rcases as with as, rfl
simp [Array.set_extract]
@[simp]
theorem extract_append {as : Vector α n} {bs : Vector α m} {i j : Nat} :
(as ++ bs).extract i j =
(as.extract i j ++ bs.extract (i - n) (j - n)).cast (by omega) := by
rcases as with as, rfl
rcases bs with bs, rfl
simp
theorem extract_append_left {as : Vector α n} {bs : Vector α m} :
(as ++ bs).extract 0 n = (as.extract 0 n).cast (by omega) := by
ext i h
simp only [Nat.sub_zero, extract_append, extract_size, getElem_cast, getElem_append, Nat.min_self,
getElem_extract, Nat.zero_sub, Nat.zero_add, cast_cast]
split
· rfl
· omega
@[simp] theorem extract_append_right {as : Vector α n} {bs : Vector α m} :
(as ++ bs).extract n (n + i) = (bs.extract 0 i).cast (by omega) := by
rcases as with as, rfl
rcases bs with bs, rfl
simp only [mk_append_mk, extract_mk, Array.extract_append, Array.extract_size_left, Nat.sub_self,
Array.empty_append, Nat.sub_zero, cast_mk, eq_mk]
congr 1
omega
@[simp] theorem map_extract {as : Vector α n} {i j : Nat} :
(as.extract i j).map f = (as.map f).extract i j := by
ext k h
simp
@[simp] theorem extract_mkVector {a : α} {n i j : Nat} :
(mkVector n a).extract i j = mkVector (min j n - i) a := by
ext i h
simp
theorem extract_add_left {as : Vector α n} {i j k : Nat} :
as.extract (i + j) k = ((as.extract i k).extract j (k - i)).cast (by omega) := by
rcases as with as, rfl
simp only [extract_mk, Array.extract_extract, cast_mk, eq_mk]
rw [Array.extract_add_left]
simp
theorem mem_extract_iff_getElem {as : Vector α n} {a : α} {i j : Nat} :
a as.extract i j (k : Nat) (hm : k < min j n - i), as[i + k] = a := by
rcases as with as
simp [Array.mem_extract_iff_getElem]
constructor <;>
· rintro k, h, rfl
exact k, by omega, rfl
theorem set_eq_push_extract_append_extract {as : Vector α n} {i : Nat} (h : i < n) {a : α} :
as.set i a = ((as.extract 0 i).push a ++ (as.extract (i + 1) n)).cast (by omega) := by
rcases as with as, rfl
simp [Array.set_eq_push_extract_append_extract, h]
theorem extract_reverse {as : Vector α n} {i j : Nat} :
as.reverse.extract i j = (as.extract (n - j) (n - i)).reverse.cast (by omega) := by
ext i h
simp only [getElem_extract, getElem_reverse, getElem_cast]
congr 1
omega
theorem reverse_extract {as : Vector α n} {i j : Nat} :
(as.extract i j).reverse = (as.reverse.extract (n - j) (n - i)).cast (by omega) := by
rcases as with as, rfl
simp [Array.reverse_extract]
end Vector

View File

@@ -10,7 +10,9 @@ import Init.Data.Vector.Range
import Init.Data.Array.Find
/-!
# Lemmas about `Vector.findSome?`, `Vector.find?, `Vector.findIdx?`, `Vector.idxOf?`.
# Lemmas about `Vector.findSome?`, `Vector.find?`, `Vector.findFinIdx?`.
We are still missing results about `idxOf?`, `findIdx`, and `findIdx?`.
-/
namespace Vector

View File

@@ -70,8 +70,8 @@ theorem toArray_mk (a : Array α) (h : a.size = n) : (Vector.mk a h).toArray = a
(Vector.mk a h).back? = a.back? := rfl
@[simp] theorem back_mk [NeZero n] (a : Array α) (h : a.size = n) :
(Vector.mk a h).back =
a[n - 1]'(Nat.lt_of_lt_of_eq (Nat.sub_one_lt (NeZero.ne n)) h.symm) := rfl
(Vector.mk a h).back = a.back (by have : 0 n := NeZero.ne' n; omega) := by
simp [back, Array.back, h]
@[simp] theorem foldlM_mk [Monad m] (f : β α m β) (b : β) (a : Array α) (h : a.size = n) :
(Vector.mk a h).foldlM f b = a.foldlM f b := rfl
@@ -730,6 +730,17 @@ theorem singleton_inj : #v[a] = #v[b] ↔ a = b := by
rcases l with l, rfl
simp
/-- In an equality between two casts, push the casts to the right hand side. -/
@[simp] theorem cast_eq_cast {as : Vector α n} {bs : Vector α m} {wa : n = k} {wb : m = k} :
as.cast wa = bs.cast wb as = bs.cast (by omega) := by
constructor
· intro w
ext i h
replace w := congrArg (fun v => v[i]) w
simpa using w
· rintro rfl
simp
/-! ### mkVector -/
@[simp] theorem mkVector_zero : mkVector 0 a = #v[] := rfl
@@ -1471,7 +1482,7 @@ theorem vector₂_induction (P : Vector (Vector α n) m → Prop)
P (mk (xss.attach.map (fun xs, m => mk xs (h₂ xs m))) (by simpa using h₁)))
(ass : Vector (Vector α n) m) : P ass := by
specialize of (ass.map toArray).toArray (by simp) (by simp)
simpa [Array.map_attach, Array.pmap_map] using of
simpa [Array.map_attach_eq_pmap, Array.pmap_map] using of
/--
Use this as `induction ass using vector₃_induction` on a hypothesis of the form `ass : Vector (Vector (Vector α n) m) k`.
@@ -1489,7 +1500,7 @@ theorem vector₃_induction (P : Vector (Vector (Vector α n) m) k → Prop)
mk x (h₃ xs m x m'))) (by simpa using h₂ xs m))) (by simpa using h₁)))
(ass : Vector (Vector (Vector α n) m) k) : P ass := by
specialize of (ass.map (fun as => (as.map toArray).toArray)).toArray (by simp) (by simp) (by simp)
simpa [Array.map_attach, Array.pmap_map] using of
simpa [Array.map_attach_eq_pmap, Array.pmap_map] using of
/-! ### singleton -/
@@ -1800,7 +1811,7 @@ theorem flatten_flatten {L : Vector (Vector (Vector α n) m) k} :
induction L using vector₃_induction with
| of xss h₁ h₂ h₃ =>
-- simp [Array.flatten_flatten] -- FIXME: `simp` produces a bad proof here!
simp [Array.map_attach, Array.flatten_flatten, Array.map_pmap]
simp [Array.map_attach_eq_pmap, Array.flatten_flatten, Array.map_pmap]
/-- Two vectors of constant length vectors are equal iff their flattens coincide. -/
theorem eq_iff_flatten_eq {L L' : Vector (Vector α n) m} :
@@ -2017,6 +2028,10 @@ theorem flatMap_mkArray {β} (f : α → Vector β m) : (mkVector n a).flatMap f
cases as
simp
@[simp] theorem isEmpty_reverse {xs : Vector α n} : xs.reverse.isEmpty = xs.isEmpty := by
rcases xs with xs, rfl
simp
@[simp] theorem getElem_reverse (a : Vector α n) (i : Nat) (hi : i < n) :
(a.reverse)[i] = a[n - 1 - i] := by
rcases a with a, rfl
@@ -2101,7 +2116,7 @@ theorem flatMap_reverse {β} (l : Vector α n) (f : α → Vector β m) :
simp
theorem getElem?_extract {as : Vector α n} {start stop : Nat} :
(as.extract start stop)[i]? = if i < min stop as.size - start then as[start + i]? else none := by
(as.extract start stop)[i]? = if i < min stop n - start then as[start + i]? else none := by
rcases as with as, rfl
simp [Array.getElem?_extract]

View File

@@ -363,4 +363,25 @@ theorem mapIdx_eq_mkVector_iff {l : Vector α n} {f : Nat → α → β} {b : β
rcases l with l, rfl
simp [Array.mapIdx_reverse]
theorem toArray_mapFinIdxM [Monad m] [LawfulMonad m]
(a : Vector α n) (f : (i : Nat) α (h : i < n) m β) :
toArray <$> a.mapFinIdxM f = a.toArray.mapFinIdxM
(fun i x h => f i x (size_toArray a h)) := by
let rec go (i j : Nat) (inv : i + j = n) (bs : Vector β (n - i)) :
toArray <$> mapFinIdxM.map a f i j inv bs
= Array.mapFinIdxM.map a.toArray (fun i x h => f i x (size_toArray a h))
i j (size_toArray _ inv) bs.toArray := by
match i with
| 0 => simp only [mapFinIdxM.map, map_pure, Array.mapFinIdxM.map, Nat.sub_zero]
| k + 1 =>
simp only [mapFinIdxM.map, map_bind, Array.mapFinIdxM.map, getElem_toArray]
conv => lhs; arg 2; intro; rw [go]
rfl
simp only [mapFinIdxM, Array.mapFinIdxM, size_toArray]
exact go _ _ _ _
theorem toArray_mapIdxM [Monad m] [LawfulMonad m] (a : Vector α n) (f : Nat α m β) :
toArray <$> a.mapIdxM f = a.toArray.mapIdxM f := by
exact toArray_mapFinIdxM _ _
end Vector

View File

@@ -19,11 +19,10 @@ open Nat
/-! ## Monadic operations -/
theorem map_toArray_inj [Monad m] [LawfulMonad m] [Nonempty α]
{v₁ : m (Vector α n)} {v₂ : m (Vector α n)} (w : toArray <$> v₁ = toArray <$> v₂) :
v₁ = v₂ := by
apply map_inj_of_inj ?_ w
simp
@[simp] theorem map_toArray_inj [Monad m] [LawfulMonad m]
{v₁ : m (Vector α n)} {v₂ : m (Vector α n)} :
toArray <$> v₁ = toArray <$> v₂ v₁ = v₂ :=
_root_.map_inj_right (by simp)
/-! ### mapM -/
@@ -39,11 +38,10 @@ theorem map_toArray_inj [Monad m] [LawfulMonad m] [Nonempty α]
unfold mapM.go
simp
-- The `[Nonempty β]` hypothesis should be avoidable by unfolding `mapM` directly.
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] [Nonempty β]
@[simp] theorem mapM_append [Monad m] [LawfulMonad m]
(f : α m β) {l₁ : Vector α n} {l₂ : Vector α n'} :
(l₁ ++ l₂).mapM f = (return ( l₁.mapM f) ++ ( l₂.mapM f)) := by
apply map_toArray_inj
apply map_toArray_inj.mp
suffices toArray <$> (l₁ ++ l₂).mapM f = (return ( toArray <$> l₁.mapM f) ++ ( toArray <$> l₂.mapM f)) by
rw [this]
simp only [bind_pure_comp, Functor.map_map, bind_map_left, map_bind, toArray_append]

View File

@@ -251,6 +251,20 @@ namespace Array
instance : GetElem (Array α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i h
-- We provide a `GetElem?` instance, rather than using the low priority instance,
-- so that we use the `@[extern]` definition of `get!`.
instance : GetElem? (Array α) Nat α fun xs i => i < xs.size where
getElem? xs i := decidableGetElem? xs i
getElem! xs i := xs.get! i
instance : LawfulGetElem (Array α) Nat α fun xs i => i < xs.size where
getElem?_def xs i h := by
simp only [getElem?, decidableGetElem?]
split <;> rfl
getElem!_def xs i := by
simp only [getElem!, getElem?, decidableGetElem?, get!, getD, getElem]
split <;> rfl
@[simp] theorem get_eq_getElem (a : Array α) (i : Nat) (h) : a.get i h = a[i] := rfl
@[simp] theorem get!_eq_getElem! [Inhabited α] (a : Array α) (i : Nat) : a.get! i = a[i]! := by

View File

@@ -8,6 +8,7 @@ import Init.SimpLemmas
import Init.PropLemmas
import Init.Classical
import Init.ByCases
import Init.Data.Int.Linear
namespace Lean.Grind
/-!
@@ -72,6 +73,8 @@ theorem bne_eq_decide_not_eq {_ : BEq α} [LawfulBEq α] [DecidableEq α] (a b :
init_grind_norm
/- Pre theorems -/
not_and not_or not_ite not_forall not_exists
/- Nat relational ops neg -/
Nat.not_ge_eq Nat.not_le_eq
|
/- Post theorems -/
Classical.not_not
@@ -116,9 +119,16 @@ init_grind_norm
Nat.lt_eq
-- Nat.succ
Nat.succ_eq_add_one
-- Nat op folding
Nat.add_eq Nat.sub_eq Nat.mul_eq Nat.zero_eq Nat.le_eq
-- Int
Int.lt_eq
-- GT GE
ge_eq gt_eq
-- Int op folding
Int.add_def Int.mul_def
Int.Linear.sub_fold Int.Linear.neg_fold
-- Int divides
Int.one_dvd Int.zero_dvd
end Lean.Grind

View File

@@ -14,6 +14,12 @@ def nestedProof (p : Prop) {h : p} : p := h
/--
Gadget for marking `match`-expressions that should not be reduced by the `grind` simplifier, but the discriminants should be normalized.
We use it when adding instances of `match`-equations to prevent them from being simplified to true.
Remark: it must not be marked as `[reducible]`. Otherwise, `simp` will reduce
```
simpMatchDiscrsOnly (match 0 with | 0 => true | _ => false) = true
```
using `eq_self`.
-/
def simpMatchDiscrsOnly {α : Sort u} (a : α) : α := a
@@ -28,7 +34,7 @@ Gadget for annotating the equalities in `match`-equations conclusions.
`_origin` is the term used to instantiate the `match`-equation using E-matching.
When `EqMatch a b origin` is `True`, we mark `origin` as a resolved case-split.
-/
def EqMatch (a b : α) {_origin : α} : Prop := a = b
abbrev EqMatch (a b : α) {_origin : α} : Prop := a = b
/--
Gadget for annotating conditions of `match` equational lemmas.
@@ -36,7 +42,13 @@ We use this annotation for two different reasons:
- We don't want to normalize them.
- We have a propagator for them.
-/
def MatchCond (p : Prop) : Prop := p
abbrev MatchCond (p : Prop) : Prop := p
/--
Similar to `MatchCond`, but not reducible. We use it to ensure `simp`
will not eliminate it. After we apply `simp`, we replace it with `MatchCond`.
-/
def PreMatchCond (p : Prop) : Prop := p
theorem nestedProof_congr (p q : Prop) (h : p = q) (hp : p) (hq : q) : HEq (@nestedProof p hp) (@nestedProof q hq) := by
subst h; apply HEq.refl

View File

@@ -735,8 +735,8 @@ def decodeNatLitVal? (s : String) : Option Nat :=
def isLit? (litKind : SyntaxNodeKind) (stx : Syntax) : Option String :=
match stx with
| Syntax.node _ k args =>
if k == litKind && args.size == 1 then
match args.get! 0 with
if h : k == litKind args.size = 1 then
match args[0]'(Nat.lt_of_sub_eq_succ h.2) with
| (Syntax.atom _ val) => some val
| _ => none
else
@@ -1509,25 +1509,35 @@ This will rewrite with all equation lemmas, which can be used to
partially evaluate many definitions. -/
declare_simp_like_tactic simpAutoUnfold "simp! " (autoUnfold := true)
/-- `simp_arith` is shorthand for `simp` with `arith := true` and `decide := true`.
This enables the use of normalization by linear arithmetic. -/
declare_simp_like_tactic simpArith "simp_arith " (arith := true) (decide := true)
/--
`simp_arith` has been deprecated. It was a shorthand for `simp +arith +decide`.
Note that `+decide` is not needed for reducing arithmetic terms since simprocs have been added to Lean.
-/
syntax (name := simpArith) "simp_arith " optConfig (discharger)? (&" only")? (" [" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
/-- `simp_arith!` is shorthand for `simp_arith` with `autoUnfold := true`.
This will rewrite with all equation lemmas, which can be used to
partially evaluate many definitions. -/
declare_simp_like_tactic simpArithAutoUnfold "simp_arith! " (arith := true) (autoUnfold := true) (decide := true)
/--
`simp_arith!` has been deprecated. It was a shorthand for `simp! +arith +decide`.
Note that `+decide` is not needed for reducing arithmetic terms since simprocs have been added to Lean.
-/
syntax (name := simpArithBang) "simp_arith! " optConfig (discharger)? (&" only")? (" [" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
/-- `simp_all!` is shorthand for `simp_all` with `autoUnfold := true`.
This will rewrite with all equation lemmas, which can be used to
partially evaluate many definitions. -/
declare_simp_like_tactic (all := true) simpAllAutoUnfold "simp_all! " (autoUnfold := true)
/-- `simp_all_arith` combines the effects of `simp_all` and `simp_arith`. -/
declare_simp_like_tactic (all := true) simpAllArith "simp_all_arith " (arith := true) (decide := true)
/--
`simp_all_arith` has been deprecated. It was a shorthand for `simp_all +arith +decide`.
Note that `+decide` is not needed for reducing arithmetic terms since simprocs have been added to Lean.
-/
syntax (name := simpAllArith) "simp_all_arith" optConfig (discharger)? (&" only")? (" [" (simpErase <|> simpLemma),* "]")? : tactic
/--
`simp_all_arith!` has been deprecated. It was a shorthand for `simp_all! +arith +decide`.
Note that `+decide` is not needed for reducing arithmetic terms since simprocs have been added to Lean.
-/
syntax (name := simpAllArithBang) "simp_all_arith!" optConfig (discharger)? (&" only")? (" [" (simpErase <|> simpLemma),* "]")? : tactic
/-- `simp_all_arith!` combines the effects of `simp_all`, `simp_arith` and `simp!`. -/
declare_simp_like_tactic (all := true) simpAllArithAutoUnfold "simp_all_arith! " (arith := true) (autoUnfold := true) (decide := true)
/-- `dsimp!` is shorthand for `dsimp` with `autoUnfold := true`.
This will rewrite with all equation lemmas, which can be used to

View File

@@ -749,6 +749,21 @@ The top-level command elaborator only runs the linters if `#guard_msgs` is not p
syntax (name := guardMsgsCmd)
(docComment)? "#guard_msgs" (ppSpace guardMsgsSpec)? " in" ppLine command : command
/--
Format and print the info trees for a given command.
This is mostly useful for debugging info trees.
-/
syntax (name := infoTreesCmd)
"#info_trees" " in" ppLine command : command
/--
Specify a premise selection engine.
Note that Lean does not ship a default premise selection engine,
so this is only useful in conjunction with a downstream package which provides one.
-/
syntax (name := setPremiseSelectorCmd)
"set_premise_selector" term : command
namespace Parser
/--

View File

@@ -21,18 +21,18 @@ abbrev IntList := List Int
namespace IntList
/-- Get the `i`-th element (interpreted as `0` if the list is not long enough). -/
def get (xs : IntList) (i : Nat) : Int := (xs.get? i).getD 0
def get (xs : IntList) (i : Nat) : Int := xs[i]?.getD 0
@[simp] theorem get_nil : get ([] : IntList) i = 0 := rfl
@[simp] theorem get_cons_zero : get (x :: xs) 0 = x := rfl
@[simp] theorem get_cons_succ : get (x :: xs) (i+1) = get xs i := rfl
@[simp] theorem get_cons_zero : get (x :: xs) 0 = x := by simp [get]
@[simp] theorem get_cons_succ : get (x :: xs) (i+1) = get xs i := by simp [get]
theorem get_map {xs : IntList} (h : f 0 = 0) : get (xs.map f) i = f (xs.get i) := by
simp only [get, List.get?_eq_getElem?, List.getElem?_map]
simp only [get, List.getElem?_map]
cases xs[i]? <;> simp_all
theorem get_of_length_le {xs : IntList} (h : xs.length i) : xs.get i = 0 := by
rw [get, List.get?_eq_none_iff.mpr h]
rw [get, List.getElem?_eq_none_iff.mpr h]
rfl
/-- Like `List.set`, but right-pad with zeroes as necessary first. -/
@@ -62,7 +62,7 @@ theorem add_def (xs ys : IntList) :
rfl
@[simp] theorem add_get (xs ys : IntList) (i : Nat) : (xs + ys).get i = xs.get i + ys.get i := by
simp only [get, add_def, List.get?_eq_getElem?, List.getElem?_zipWithAll]
simp only [get, add_def, List.getElem?_zipWithAll]
cases xs[i]? <;> cases ys[i]? <;> simp
@[simp] theorem add_nil (xs : IntList) : xs + [] = xs := by simp [add_def]
@@ -79,7 +79,7 @@ theorem mul_def (xs ys : IntList) : xs * ys = List.zipWith (· * ·) xs ys :=
rfl
@[simp] theorem mul_get (xs ys : IntList) (i : Nat) : (xs * ys).get i = xs.get i * ys.get i := by
simp only [get, mul_def, List.get?_eq_getElem?, List.getElem?_zipWith]
simp only [get, mul_def, List.getElem?_zipWith]
cases xs[i]? <;> cases ys[i]? <;> simp
@[simp] theorem mul_nil_left : ([] : IntList) * ys = [] := rfl
@@ -94,7 +94,7 @@ instance : Neg IntList := ⟨neg⟩
theorem neg_def (xs : IntList) : - xs = xs.map fun x => -x := rfl
@[simp] theorem neg_get (xs : IntList) (i : Nat) : (- xs).get i = - xs.get i := by
simp only [get, neg_def, List.get?_eq_getElem?, List.getElem?_map]
simp only [get, neg_def, List.getElem?_map]
cases xs[i]? <;> simp
@[simp] theorem neg_nil : (- ([] : IntList)) = [] := rfl
@@ -120,7 +120,7 @@ instance : HMul Int IntList IntList where
theorem smul_def (xs : IntList) (i : Int) : i * xs = xs.map fun x => i * x := rfl
@[simp] theorem smul_get (xs : IntList) (a : Int) (i : Nat) : (a * xs).get i = a * xs.get i := by
simp only [get, smul_def, List.get?_eq_getElem?, List.getElem?_map]
simp only [get, smul_def, List.getElem?_map]
cases xs[i]? <;> simp
@[simp] theorem smul_nil {i : Int} : i * ([] : IntList) = [] := rfl
@@ -303,7 +303,7 @@ theorem dvd_gcd (xs : IntList) (c : Nat) (w : ∀ {a : Int}, a ∈ xs → (c : I
c xs.gcd := by
simp only [Int.ofNat_dvd_left] at w
induction xs with
| nil => have := Nat.dvd_zero c; simp at this; exact this
| nil => have := Nat.dvd_zero c; simp
| cons x xs ih =>
simp
apply Nat.dvd_gcd

View File

@@ -1904,7 +1904,7 @@ instance : DecidableEq (BitVec n) := BitVec.decEq
/-- The `BitVec` with value `i`, given a proof that `i < 2^n`. -/
@[match_pattern]
protected def BitVec.ofNatLt {n : Nat} (i : Nat) (p : LT.lt i (hPow 2 n)) : BitVec n where
protected def BitVec.ofNatLT {n : Nat} (i : Nat) (p : LT.lt i (hPow 2 n)) : BitVec n where
toFin := i, p
/-- Given a bitvector `x`, return the underlying `Nat`. This is O(1) because `BitVec` is a
@@ -1927,11 +1927,16 @@ The type of unsigned 8-bit integers. This type has special support in the
compiler to make it actually 8 bits rather than wrapping a `Nat`.
-/
structure UInt8 where
/-- Unpack a `UInt8` as a `BitVec 8`.
This function is overridden with a native implementation. -/
/--
Create a `UInt8` from a `BitVec 8`. This function is overridden with a native implementation.
-/
ofBitVec ::
/--
Unpack a `UInt8` as a `BitVec 8`. This function is overridden with a native implementation.
-/
toBitVec : BitVec 8
attribute [extern "lean_uint8_of_nat_mk"] UInt8.mk
attribute [extern "lean_uint8_of_nat_mk"] UInt8.ofBitVec
attribute [extern "lean_uint8_to_nat"] UInt8.toBitVec
/--
@@ -1939,8 +1944,8 @@ Pack a `Nat` less than `2^8` into a `UInt8`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint8_of_nat"]
def UInt8.ofNatCore (n : @& Nat) (h : LT.lt n UInt8.size) : UInt8 where
toBitVec := BitVec.ofNatLt n h
def UInt8.ofNatLT (n : @& Nat) (h : LT.lt n UInt8.size) : UInt8 where
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -1958,7 +1963,7 @@ def UInt8.decEq (a b : UInt8) : Decidable (Eq a b) :=
instance : DecidableEq UInt8 := UInt8.decEq
instance : Inhabited UInt8 where
default := UInt8.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt8.ofNatLT 0 (of_decide_eq_true rfl)
/-- The size of type `UInt16`, that is, `2^16 = 65536`. -/
abbrev UInt16.size : Nat := 65536
@@ -1968,11 +1973,16 @@ The type of unsigned 16-bit integers. This type has special support in the
compiler to make it actually 16 bits rather than wrapping a `Nat`.
-/
structure UInt16 where
/-- Unpack a `UInt16` as a `BitVec 16`.
This function is overridden with a native implementation. -/
/--
Create a `UInt16` from a `BitVec 16`. This function is overridden with a native implementation.
-/
ofBitVec ::
/--
Unpack a `UInt16` as a `BitVec 16`. This function is overridden with a native implementation.
-/
toBitVec : BitVec 16
attribute [extern "lean_uint16_of_nat_mk"] UInt16.mk
attribute [extern "lean_uint16_of_nat_mk"] UInt16.ofBitVec
attribute [extern "lean_uint16_to_nat"] UInt16.toBitVec
/--
@@ -1980,8 +1990,8 @@ Pack a `Nat` less than `2^16` into a `UInt16`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint16_of_nat"]
def UInt16.ofNatCore (n : @& Nat) (h : LT.lt n UInt16.size) : UInt16 where
toBitVec := BitVec.ofNatLt n h
def UInt16.ofNatLT (n : @& Nat) (h : LT.lt n UInt16.size) : UInt16 where
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -1999,7 +2009,7 @@ def UInt16.decEq (a b : UInt16) : Decidable (Eq a b) :=
instance : DecidableEq UInt16 := UInt16.decEq
instance : Inhabited UInt16 where
default := UInt16.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt16.ofNatLT 0 (of_decide_eq_true rfl)
/-- The size of type `UInt32`, that is, `2^32 = 4294967296`. -/
abbrev UInt32.size : Nat := 4294967296
@@ -2009,11 +2019,16 @@ The type of unsigned 32-bit integers. This type has special support in the
compiler to make it actually 32 bits rather than wrapping a `Nat`.
-/
structure UInt32 where
/-- Unpack a `UInt32` as a `BitVec 32.
This function is overridden with a native implementation. -/
/--
Create a `UInt32` from a `BitVec 32`. This function is overridden with a native implementation.
-/
ofBitVec ::
/--
Unpack a `UInt32` as a `BitVec 32`. This function is overridden with a native implementation.
-/
toBitVec : BitVec 32
attribute [extern "lean_uint32_of_nat_mk"] UInt32.mk
attribute [extern "lean_uint32_of_nat_mk"] UInt32.ofBitVec
attribute [extern "lean_uint32_to_nat"] UInt32.toBitVec
/--
@@ -2021,8 +2036,8 @@ Pack a `Nat` less than `2^32` into a `UInt32`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint32_of_nat"]
def UInt32.ofNatCore (n : @& Nat) (h : LT.lt n UInt32.size) : UInt32 where
toBitVec := BitVec.ofNatLt n h
def UInt32.ofNatLT (n : @& Nat) (h : LT.lt n UInt32.size) : UInt32 where
toBitVec := BitVec.ofNatLT n h
/--
Unpack a `UInt32` as a `Nat`.
@@ -2045,7 +2060,7 @@ def UInt32.decEq (a b : UInt32) : Decidable (Eq a b) :=
instance : DecidableEq UInt32 := UInt32.decEq
instance : Inhabited UInt32 where
default := UInt32.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt32.ofNatLT 0 (of_decide_eq_true rfl)
instance : LT UInt32 where
lt a b := LT.lt a.toBitVec b.toBitVec
@@ -2081,11 +2096,16 @@ The type of unsigned 64-bit integers. This type has special support in the
compiler to make it actually 64 bits rather than wrapping a `Nat`.
-/
structure UInt64 where
/-- Unpack a `UInt64` as a `BitVec 64`.
This function is overridden with a native implementation. -/
/--
Create a `UInt64` from a `BitVec 64`. This function is overridden with a native implementation.
-/
ofBitVec ::
/--
Unpack a `UInt64` as a `BitVec 64`. This function is overridden with a native implementation.
-/
toBitVec: BitVec 64
attribute [extern "lean_uint64_of_nat_mk"] UInt64.mk
attribute [extern "lean_uint64_of_nat_mk"] UInt64.ofBitVec
attribute [extern "lean_uint64_to_nat"] UInt64.toBitVec
/--
@@ -2093,8 +2113,8 @@ Pack a `Nat` less than `2^64` into a `UInt64`.
This function is overridden with a native implementation.
-/
@[extern "lean_uint64_of_nat"]
def UInt64.ofNatCore (n : @& Nat) (h : LT.lt n UInt64.size) : UInt64 where
toBitVec := BitVec.ofNatLt n h
def UInt64.ofNatLT (n : @& Nat) (h : LT.lt n UInt64.size) : UInt64 where
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -2112,7 +2132,7 @@ def UInt64.decEq (a b : UInt64) : Decidable (Eq a b) :=
instance : DecidableEq UInt64 := UInt64.decEq
instance : Inhabited UInt64 where
default := UInt64.ofNatCore 0 (of_decide_eq_true rfl)
default := UInt64.ofNatLT 0 (of_decide_eq_true rfl)
/-- The size of type `USize`, that is, `2^System.Platform.numBits`. -/
abbrev USize.size : Nat := (hPow 2 System.Platform.numBits)
@@ -2136,11 +2156,18 @@ For example, if running on a 32-bit machine, USize is equivalent to UInt32.
Or on a 64-bit machine, UInt64.
-/
structure USize where
/-- Unpack a `USize` as a `BitVec System.Platform.numBits`.
This function is overridden with a native implementation. -/
/--
Create a `USize` from a `BitVec System.Platform.numBits`. This function is overridden with a
native implementation.
-/
ofBitVec ::
/--
Unpack a `USize` as a `BitVec System.Platform.numBits`. This function is overridden with a native
implementation.
-/
toBitVec : BitVec System.Platform.numBits
attribute [extern "lean_usize_of_nat_mk"] USize.mk
attribute [extern "lean_usize_of_nat_mk"] USize.ofBitVec
attribute [extern "lean_usize_to_nat"] USize.toBitVec
/--
@@ -2148,8 +2175,8 @@ Pack a `Nat` less than `USize.size` into a `USize`.
This function is overridden with a native implementation.
-/
@[extern "lean_usize_of_nat"]
def USize.ofNatCore (n : @& Nat) (h : LT.lt n USize.size) : USize where
toBitVec := BitVec.ofNatLt n h
def USize.ofNatLT (n : @& Nat) (h : LT.lt n USize.size) : USize where
toBitVec := BitVec.ofNatLT n h
set_option bootstrap.genMatcherCode false in
/--
@@ -2167,7 +2194,8 @@ def USize.decEq (a b : USize) : Decidable (Eq a b) :=
instance : DecidableEq USize := USize.decEq
instance : Inhabited USize where
default := USize.ofNatCore 0 usize_size_pos
default := USize.ofNatLT 0 usize_size_pos
/--
A `Nat` denotes a valid unicode codepoint if it is less than `0x110000`, and
it is also not a "surrogate" character (the range `0xd800` to `0xdfff` inclusive).
@@ -2201,7 +2229,7 @@ This function is overridden with a native implementation.
-/
@[extern "lean_uint32_of_nat"]
def Char.ofNatAux (n : @& Nat) (h : n.isValidChar) : Char :=
{ val := BitVec.ofNatLt n (isValidChar_UInt32 h), valid := h }
{ val := BitVec.ofNatLT n (isValidChar_UInt32 h), valid := h }
/--
Convert a `Nat` into a `Char`. If the `Nat` does not encode a valid unicode scalar value,
@@ -2211,7 +2239,7 @@ Convert a `Nat` into a `Char`. If the `Nat` does not encode a valid unicode scal
def Char.ofNat (n : Nat) : Char :=
dite (n.isValidChar)
(fun h => Char.ofNatAux n h)
(fun _ => { val := BitVec.ofNatLt 0 (of_decide_eq_true rfl), valid := Or.inl (of_decide_eq_true rfl) })
(fun _ => { val := BitVec.ofNatLT 0 (of_decide_eq_true rfl), valid := Or.inl (of_decide_eq_true rfl) })
theorem Char.eq_of_val_eq : {c d : Char}, Eq c.val d.val Eq c d
| _, _, _, _, rfl => rfl
@@ -2234,9 +2262,9 @@ instance : DecidableEq Char :=
/-- Returns the number of bytes required to encode this `Char` in UTF-8. -/
def Char.utf8Size (c : Char) : Nat :=
let v := c.val
ite (LE.le v (UInt32.ofNatCore 0x7F (of_decide_eq_true rfl))) 1
(ite (LE.le v (UInt32.ofNatCore 0x7FF (of_decide_eq_true rfl))) 2
(ite (LE.le v (UInt32.ofNatCore 0xFFFF (of_decide_eq_true rfl))) 3 4))
ite (LE.le v (UInt32.ofNatLT 0x7F (of_decide_eq_true rfl))) 1
(ite (LE.le v (UInt32.ofNatLT 0x7FF (of_decide_eq_true rfl))) 2
(ite (LE.le v (UInt32.ofNatLT 0xFFFF (of_decide_eq_true rfl))) 3 4))
/--
`Option α` is the type of values which are either `some a` for some `a : α`,
@@ -2612,7 +2640,13 @@ structure Array (α : Type u) where
attribute [extern "lean_array_to_list"] Array.toList
attribute [extern "lean_array_mk"] Array.mk
@[inherit_doc Array.mk, match_pattern]
/--
Converts a `List α` into an `Array α`. (This is preferred over the synonym `Array.mk`.)
At runtime, this constructor is implemented by `List.toArrayImpl` and is O(n) in the length of the
list.
-/
@[match_pattern]
abbrev List.toArray (xs : List α) : Array α := .mk xs
/-- Construct a new empty array with initial capacity `c`. -/
@@ -2629,12 +2663,14 @@ def Array.size {α : Type u} (a : @& Array α) : Nat :=
a.toList.length
/--
Use the indexing notation `a[i]` instead.
Access an element from an array without needing a runtime bounds checks,
using a `Nat` index and a proof that it is in bounds.
This function does not use `get_elem_tactic` to automatically find the proof that
the index is in bounds. This is because the tactic itself needs to look up values in
arrays. Use the indexing notation `a[i]` instead.
arrays.
-/
@[extern "lean_array_fget"]
def Array.get {α : Type u} (a : @& Array α) (i : @& Nat) (h : LT.lt i a.size) : α :=
@@ -2644,7 +2680,11 @@ def Array.get {α : Type u} (a : @& Array α) (i : @& Nat) (h : LT.lt i a.size)
@[inline] abbrev Array.getD (a : Array α) (i : Nat) (v₀ : α) : α :=
dite (LT.lt i a.size) (fun h => a.get i h) (fun _ => v₀)
/-- Access an element from an array, or panic if the index is out of bounds. -/
/--
Use the indexing notation `a[i]!` instead.
Access an element from an array, or panic if the index is out of bounds.
-/
@[extern "lean_array_get"]
def Array.get! {α : Type u} [Inhabited α] (a : @& Array α) (i : @& Nat) : α :=
Array.getD a i default
@@ -3495,9 +3535,9 @@ with
/-- A hash function for names, which is stored inside the name itself as a
computed field. -/
@[computed_field] hash : Name UInt64
| .anonymous => .ofNatCore 1723 (of_decide_eq_true rfl)
| .anonymous => .ofNatLT 1723 (of_decide_eq_true rfl)
| .str p s => mixHash p.hash s.hash
| .num p v => mixHash p.hash (dite (LT.lt v UInt64.size) (fun h => UInt64.ofNatCore v h) (fun _ => UInt64.ofNatCore 17 (of_decide_eq_true rfl)))
| .num p v => mixHash p.hash (dite (LT.lt v UInt64.size) (fun h => UInt64.ofNatLT v h) (fun _ => UInt64.ofNatLT 17 (of_decide_eq_true rfl)))
instance : Inhabited Name where
default := Name.anonymous

View File

@@ -450,9 +450,9 @@ if h : p then
decidable_of_decidable_of_iff fun h2 _ => h2, fun al => al h
else isTrue fun h2 => absurd h2 h
theorem decide_eq_true_iff {p : Prop} [Decidable p] : (decide p = true) p := by simp
@[bool_to_prop] theorem decide_eq_true_iff {p : Prop} [Decidable p] : (decide p = true) p := by simp
@[simp, boolToPropSimps] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
@[simp, bool_to_prop] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
decide p = decide q (p q) :=
fun h => by rw [ decide_eq_true_iff (p := p), h, decide_eq_true_iff], fun h => by simp [h]

View File

@@ -9,28 +9,28 @@ import Init.SizeOf
import Init.Data.Nat.Linear
@[simp] protected theorem Fin.sizeOf (a : Fin n) : sizeOf a = a.val + 1 := by
cases a; simp_arith
cases a; simp +arith
@[simp] protected theorem BitVec.sizeOf (a : BitVec w) : sizeOf a = sizeOf a.toFin + 1 := by
cases a; simp_arith
cases a; simp +arith
@[simp] protected theorem UInt8.sizeOf (a : UInt8) : sizeOf a = a.toNat + 3 := by
cases a; simp_arith [UInt8.toNat, BitVec.toNat]
cases a; simp +arith [UInt8.toNat, BitVec.toNat]
@[simp] protected theorem UInt16.sizeOf (a : UInt16) : sizeOf a = a.toNat + 3 := by
cases a; simp_arith [UInt16.toNat, BitVec.toNat]
cases a; simp +arith [UInt16.toNat, BitVec.toNat]
@[simp] protected theorem UInt32.sizeOf (a : UInt32) : sizeOf a = a.toNat + 3 := by
cases a; simp_arith [UInt32.toNat, BitVec.toNat]
cases a; simp +arith [UInt32.toNat, BitVec.toNat]
@[simp] protected theorem UInt64.sizeOf (a : UInt64) : sizeOf a = a.toNat + 3 := by
cases a; simp_arith [UInt64.toNat, BitVec.toNat]
cases a; simp +arith [UInt64.toNat, BitVec.toNat]
@[simp] protected theorem USize.sizeOf (a : USize) : sizeOf a = a.toNat + 3 := by
cases a; simp_arith [USize.toNat, BitVec.toNat]
cases a; simp +arith [USize.toNat, BitVec.toNat]
@[simp] protected theorem Char.sizeOf (a : Char) : sizeOf a = a.toNat + 4 := by
cases a; simp_arith [Char.toNat]
cases a; simp +arith [Char.toNat]
@[simp] protected theorem Subtype.sizeOf {α : Sort u_1} {p : α Prop} (s : Subtype p) : sizeOf s = sizeOf s.val + 1 := by
cases s; simp

View File

@@ -238,7 +238,12 @@ protected def TaskState.toString : TaskState → String
instance : ToString TaskState := TaskState.toString
/-- Returns current state of the `Task` in the Lean runtime's task manager. -/
/--
Returns current state of the `Task` in the Lean runtime's task manager.
Note that for tasks derived from `Promise`s, `waiting` and `running` should be considered
equivalent.
-/
@[extern "lean_io_get_task_state"] opaque getTaskState : @& Task α BaseIO TaskState
/-- Check if the task has finished execution, at which point calling `Task.get` will return immediately. -/

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