Compare commits

..

117 Commits

Author SHA1 Message Date
Kim Morrison
66b47a843b feat: deprecate Array.mkArray in favour of Array.replicate 2025-03-24 10:30:32 +11:00
Kim Morrison
cff19d7edf chore: update stage0 2025-03-24 10:25:35 +11:00
Kim Morrison
600b5c5f9c feat: add Array.replicate 2025-03-24 10:21:22 +11:00
Kyle Miller
414ba28cef fix: make pretty printed structure instances hoverable (#7648)
This PR fixes a bug introduced in #7589, causing pretty printed
structure instances to not be hoverable in the Infoview.

This was caused by a choice node being introduced, since `{ $fields,* }`
is ambiguous syntax.
2025-03-23 19:36:13 +00:00
Henrik Böving
d24dfa1031 perf: add a cache to bv_decide's reflection procedure (#7644)
This PR adds a cache to the reflection procedure of bv_decide.

This was motivated by the following profile on QF_BV SMTLIB problem
`sage/app12/bench_3564.smt2`: https://share.firefox.dev/4iTG8KX. After
this change we roughly get a 10x speedup and `simp` is the bottleneck
again: https://share.firefox.dev/4iuezYT
2025-03-23 13:56:00 +00:00
Henrik Böving
f241cc832b perf: bv_decide don't drop the expression level cache (#7636)
This PR makes sure that the expression level cache in bv_decide is
maintained across the entire bitblaster instead of just locally per
BitVec expression.

The PR was split off from the first one (#7606) as this mostly entails
pulling the invariant through and is thus much more mechanical.
2025-03-23 13:05:01 +00:00
Kyle Miller
e663eb1b7a feat: structure autoParam inheritance (#7640)
This PR implements the main logic for inheriting and overriding
autoParam fields in the `structure`/`class` commands, pending being
enabled in the structure instance notation elaborator. Adds term info to
overridden fields, so they now can be hovered over, and "go to
definition" goes to the structure the field is originally defined in.

Implementation notes:
- The inherited autoParams are all recorded in the flat constructor.
Defined/overridden autoParam auxiliary tactic declarations now have
names of the form `StructName.fieldName._autoParam`
- The field `StructureFieldInfo.autoParam?` is soon to be deprecated.
The elaborator is still setting it for now, since the structure instance
notation elaborator is still using it.
2025-03-23 06:04:00 +00:00
Leonardo de Moura
06d6dbff5d feat: model-based theory combination in grind (#7641)
This PR implements basic model-based theory combination in `grind`.
`grind` can now solve examples such as
```lean
example (f : Int → Int) (x : Int)
    : 0 ≤ x → x ≠ 0 → x ≤ 1 → f x = 2 → f 1 = 2 := by
  grind
```
2025-03-23 04:06:09 +00:00
Mac Malone
66e0a5440b refactor: lake: unified configuration (#7504)
This PR augments the Lake configuration data structures declarations
(e.g., `PackageConfig`, `LeanLibConfig`) to produce additional metadata
which is used to automatically generate the Lean & TOML encoders and
decoders via metaprograms.

**Warning:** This refactor should not produce any significant
user-facing breaking changes. However, configurations have been tweaked,
so there is a chance something may have slipped through.

Lake TOML decoding and Lean syntax manipulation utilities have also
undergone significant rework to facilitate this PR. Such utilities are
considered internal and thus little has been done to mitigate possible
downstream breakages.
2025-03-23 02:49:57 +00:00
Lean stage0 autoupdater
7f362c8e8a chore: update stage0 2025-03-23 00:37:25 +00:00
Kyle Miller
cde237daea feat: change structure command to elaborate fields as if structures are flat (#7302)
This PR changes how fields are elaborated in the `structure`/`class`
commands and also makes default values respect the structure resolution
order when there is diamond inheritance. Before, the details of
subobjects were exposed during elaboration, and in the local context any
fields that came from a subobject were defined to be projections of the
subobject field. Now, every field is represented as a local variable.
All parents (not just subobject parents) are now represented in the
local context, and they are now local variables defined to be parent
constructors applied to field variables (inverting the previous
relationship). Other notes:
- The entire collection of parents is processed, and all parent
projection names are checked for consistency. Every parent appears in
the local context now.
- For classes, every parent now contributes an instance, not just the
parents represented as subobjects.
- Default values are now processed according to the parent resolution
order. Default value definition/override auxiliary definitions are
stored at `StructName.fieldName._default`, and inherited values are
stored at `StructName.fieldName._inherited_default`. Metaprograms no
longer need to look at parents when doing calculations on default
values.
- Default value omission for structure instance notation pretty printing
has been updated in consideration of this.
- Now the elaborator generates a `_flat_ctor` constructor that will be
used for structure instance elaboration. All types in this constructor
are put in "field normal form" (projections of parent constructors are
reduced, and parent constructors are eta reduced), and all fields with
autoParams are annotated as such. This is not meant for users, but it
may be useful for metaprogramming.
- While elaborating fields, any metavariables whose type is one of the
parents is assigned to that parent. The hypothesis is that, for the
purpose of elaborating structure fields, parents are fixed: there is
only *one* instance of any given parent under consideration. See the
`Magma` test for an example of this being necessary. The hypothesis may
not be true when there are recursive structures, since different values
of the structure might not agree on parent fields.

Other notes:
- The elaborator has been refactored, and it now uses a monad to keep
track of the elaboration state.
- This PR was motivation for #7100, since we need to be able to make all
parents have consistent projection names when there is diamond
inheritance.

Still to do:
- Handle autoParams like we do default values. Inheritance for these is
not correct when there is diamond inheritance.
- Avoid splitting apart parents if the overlap is only on proof fields.
- Non-subobject parent projections do not have parameter binder kinds
that are consistent with other projections (i.e., all implicit by
default, no inst implicits). This needs to wait on adjustments to the
synthOrder algorithm.
- We could elide parents with no fields, letting their projections be
constant functions. This causes some trouble for defeq checking however
(maybe #2258 would address this).
2025-03-22 22:33:10 +00:00
Henrik Böving
b97a7ef4cb perf: bv_decide introduce an expression level bitblasting cache (#7606)
This PR introduces an expression level bitblasting cache to bv_decide.
2025-03-22 13:25:52 +00:00
Leonardo de Moura
eb0c015e7c perf: quadratic behavior in whnfCore (#7630)
This PR fixes a performance issue in the `whnfCore` procedure.
2025-03-21 22:29:21 +00:00
David Thrane Christiansen
b768e44ba7 doc: further missing docstrings (#7613)
This PR adds a variety of docstrings for names that appear in the
manual.
2025-03-21 22:20:07 +00:00
Lean stage0 autoupdater
385c6db4ce chore: update stage0 2025-03-21 21:12:34 +00:00
David Thrane Christiansen
aef6c6d518 doc: review docstrings for fixed-width integer types (#7602)
This PR adds missing docstrings for fixed-width integer operations and
makes their style consistent.
2025-03-21 20:16:28 +00:00
Sebastian Ullrich
d57cbdfb95 chore: CI: bring back coredump tracing (#7625) 2025-03-21 15:25:45 +00:00
Sebastian Ullrich
7240d910d3 chore: more core proof benchmarks 2025-03-21 15:59:14 +01:00
Joachim Breitner
6931e91bf0 fix: mark Nat.div and Nat.modCore irreducible (#7614)
This PR marks `Nat.div` and `Nat.modCore` as `irreducible`, to recover 
the behavior from from before #7558.

Fixes #7612. H't to @tobiasgrosser for the good bug report.
2025-03-21 14:23:03 +00:00
Sebastian Ullrich
501bd64a89 chore: CI: avoid empty matrix error (#7620) 2025-03-21 13:30:58 +00:00
Marc Huisinga
2b11c8d9a4 chore: bump server version to 0.3.0 (#7624)
This PR bumps the server version so that clients like NeoVim can detect
whether the server supports our recent language server extensions
(modulo the time that has passed since these extension PRs).

I'd like to have server capabilities for this at some point, but this
will have to do for now.
2025-03-21 12:56:59 +00:00
Joachim Breitner
770af38c14 fix: fun_induction: correctly identify params and targets (#7622)
This PR fixes `fun_induction` when used on structurally recursive
functions where there are targets occurring before fixed parameters.

Fixes #7550
2025-03-21 12:12:15 +00:00
Sebastian Ullrich
7b787c81f3 perf: avoid contended access to IO.Ref in isTracingEnabledFor (#7601) 2025-03-21 12:07:25 +00:00
Joachim Breitner
bd01461b5f chore: run awaiting-mathlib.yml on more events (#7621)
so that we can make it a required check
2025-03-21 11:37:35 +00:00
Henrik Böving
1afd678100 perf: handle more symmetries in bv_decide bitblasting (#7617)
This PR adds the known bits optimization from the multiplication circuit
to the add one, allowing us to discover potentially even more symmetries
before going to the SAT solver.
2025-03-21 10:45:06 +00:00
Henrik Böving
677d26a581 refactor: apply fording to BVExpr to enable deriving DecidableEq (#7619)
This PR applies fording to bv_decide's BVExpr type to enable deriving
DecidableEq.
2025-03-21 10:29:04 +00:00
Henrik Böving
f673facdbe feat: add BV_EXTRACT_ADD to bv_decide (#7615)
This PR adds the ADD part of bitwuzlas BV_EXTRACT_ADD_MUL rule to
bv_decide's preprocessor.
2025-03-21 09:31:12 +00:00
Siddharth
9fc991da33 feat: add BV De Morgan's (extended) theorems from Hacker's Delight, 2.1 (#7604)
This PR adds bitvector theorems that to push negation into other
operations, following Hacker's Delight: Ch2.1.
2025-03-21 08:58:18 +00:00
Sebastian Ullrich
3d0f41e323 chore: fix interpreter lean_assert 2025-03-21 09:38:50 +01:00
David Thrane Christiansen
7e1ee70b7c doc: add docstrings for String.drop and String.dropRight (#7607)
This PR adds docstrings for `String.drop` and `String.dropRight`.
2025-03-21 05:38:07 +00:00
Mac Malone
131b458236 chore: lake: revert use of Lake plugin (#7608)
This PR removes the use of the Lake plugin in the Lake build and in
configuration files.

With #7399, the plugin is no longer necessary and may be the source of
some persistent intermittent Lake test failures.
2025-03-21 00:59:43 +00:00
Kim Morrison
74ffa1e413 chore: remove the old Lean.Data.HashMap implementation (#7519)
This PR removes `Lean.Data.HashMap` and `HashSet`. These have been
deprecated for 6 months, replaced by `Std.Data.HashMap` and `HashSet`.
2025-03-20 23:49:55 +00:00
Siddharth
42bbc4b6e2 feat: BitVec.extractLsb'_add_eq (#7595)
This PR implements the addition rewrite from the Bitwuzla rewrite
[BV_EXTRACT_ADD_MUL](e09c50818b/src/rewrite/rewrites_bv.cpp (L1495-L1510)),
which witness that the high bits at `i >= len` do not affect the bits of
the sum upto `len`:

```lean
theorem extractLsb'_add {w len} {x y : BitVec w} (hlen : len ≤ w) : 
    (x + y).extractLsb' 0 len = x.extractLsb' 0 len + y.extractLsb' 0 len
```

---------

Co-authored-by: Luisa Cicolini <48860705+luisacicolini@users.noreply.github.com>
2025-03-20 22:51:21 +00:00
Tobias Grosser
7c62881a95 feat: bv_decide short-circuit a * x = b * x (#6496)
This PR adds short-circuit support to bv_decide to accelerate
multiplications with shared coefficients. In particular, `a * x = b * x`
can be extended to `a = b v (a * x = b * x)`. The latter is faster if `a
= b` is true, as `a = b` may be evaluated without considering the
multiplication circuit. On the other hand, we require the multiplication
circuit, as `a * x = b * x -> a = b` is not always true due to two's
complement wrapping.

We support multiplications through acNF, which takes into account shared
terms across equality canonicalizing `a * (b * c1) = a * (b * c2)` to
`(a * b) * c1 = (a * b) * c2`. As a result, the non-shared terms are
lifted to the top such that canonical rewrites for binary multiplication
with shared terms on the left/right are sufficient.

We add an option `bv_decide +shortCircuit` which controls this feature
(currently disabled by default).

---------

Co-authored-by: Siddharth Bhat <siddu.druid@gmail.com>
Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-03-20 19:51:53 +00:00
Henrik Böving
c66cb00c0f refactor: turn the AIG framework's RefVec from Array to Vector (#7603)
This PR uses the new `Vector` API inside of the AIG framework's `RefVec`
datatype.
2025-03-20 16:57:04 +00:00
Kyle Miller
c066b5cf1c feat: pretty printing structures, omit default values (#7589)
This PR changes the structure instance notation pretty printer so that
fields are omitted if their value is definitionally equal to the default
value for the field (up to reducible transparancy). Setting
`pp.structureInstances.defaults` to true forces such fields to be pretty
printed anyway.

Closes #1100
2025-03-20 15:32:13 +00:00
Henrik Böving
3221ca1704 fix: interaction of enums and fixedInt in bv_decide (#7596)
This PR fixes an interaction between the enums and fixedInt pass in
bv_decide.

Marked as no changelog as this feature isn't released yet.
2025-03-20 15:12:52 +00:00
David Thrane Christiansen
c279c088c8 doc: review Int docstrings (#7568)
This PR adds missing `Int` docstrings and makes the style of all of them
consistent.
2025-03-20 14:04:56 +00:00
Sebastian Ullrich
086d45f27c perf: interpreter: use global native symbol cache (#7575)
With parallelism, a thread-local cache is not sufficient anymore.
2025-03-20 12:51:27 +00:00
Luisa Cicolini
637d8b2a2d feat: add BitVec.(negOverflow, negOverflow_eq) (#7554)
This PR adds SMT-LIB operators to detect overflow `BitVec.negOverflow`,
according to the [SMTLIB
standard](https://github.com/SMT-LIB/SMT-LIB-2/blob/2.7/Theories/FixedSizeBitVectors.smt2),
and the theorem proving equivalence of such definition with the `BitVec`
library functions (`negOverflow_eq`).

Co-authored by @bollu and @alexkeizer

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-03-20 12:43:43 +00:00
David Thrane Christiansen
d8cbf1cefc doc: docstring review for monads and transformers (#7548)
This PR adds missing monad transformer docstrings and makes their style
consistent.

---------

Co-authored-by: Bhavik Mehta <bm489@cam.ac.uk>
2025-03-20 12:18:46 +00:00
Sebastian Ullrich
edbb84d23b chore: CI: USE_LAKE secondary build job (#7505)
As preparation for the module system, and in hopes it will be faster
than and replace the Nix CI. Secondary build jobs do not block merging.

Also makes macOS aarch64 a secondary build job on the PR level, where it
is the current bottleneck.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2025-03-20 12:16:53 +00:00
Sebastian Ullrich
756fd66745 chore: CI: relax check-stage0 check 2025-03-20 13:16:43 +01:00
David Thrane Christiansen
99f296a2e7 doc: review docstrings for universe lifting operators (#7564)
This PR updates the docstrings for `ULift` and `PLift`, making their
style consistent with the others.
2025-03-20 10:52:48 +00:00
Paul Reichert
d2c35fd39d feat: more tree map lemmas for minKey? (#7556)
This PR provides lemmas about the tree map function `minKey?` and its
interaction with other functions for which lemmas already exist.

---------

Co-authored-by: Paul Reichert <datokrat@users.noreply.github.com>
2025-03-20 10:40:30 +00:00
David Thrane Christiansen
cbfb9e482f doc: review of Nat docstrings (#7552)
This PR adds missing `Nat` docstrings and makes their style consistent.

---------

Co-authored-by: Bhavik Mehta <bm489@cam.ac.uk>
2025-03-20 09:13:36 +00:00
Sebastian Ullrich
1fb4a32c8d fix: avoid follow-up kernel errors (#7570)
Asynchronous elaboration means that constants can exist in the elab
environment while failing to be added to the kernel environment, avoid
the latter by falling back to axioms there
2025-03-20 09:11:25 +00:00
Sebastian Ullrich
f42a28f718 chore: revert "perf: avoid taking mutex on task deactivation" (#7590)
Likely introduced segfaults.

Reverts leanprover/lean4#7572
2025-03-20 07:04:50 +00:00
Mac Malone
160ca476a1 chore: USE_LAKE touchups (#7581)
This PR adds some documentation to the Lean's `lakefile.toml` and makes
a few tweaks required to get `USE_LAKE` working properly on Windows. It
also adds a `stage1-configure` step target so the Lake configuration
files can be generated without performing a build of stage 1. This
enables one to build stage 0 and configure Lake via CMake and then use
Lake instead of CMake to build stage 1.

Partly adapted from #7505.
2025-03-20 06:27:22 +00:00
Lean stage0 autoupdater
17f67df257 chore: update stage0 2025-03-20 05:52:03 +00:00
Mac Malone
10f0adc9f9 feat: lake: thin libraries for static.export (#7586)
This PR changes the `static.export` facet for Lean libraries to produce
thin static libraries.

Static libraries with explicitly exported symbols are only necessary on
Windows (where symbol counts are a concern) and are usually used as part
of local build process and not distributed (as they are in Lean's
build). Thus, it seems reasonable to make them unilaterally thin. They
also need to be thin for the Lean build with Lake.
2025-03-20 04:53:35 +00:00
Mac Malone
a67de7ebda fix: lake: use response files on Windows to avoid CLI length limits (#7576)
This PR changes Lake to produce and use response files on Windows when
building executables and libraries (static and shared). This is done to
avoid potentially exceeding Windows command line length limits.

Closes #4159.
2025-03-20 02:58:10 +00:00
Leonardo de Moura
08af091a1c chore: missing normalization rules for cutsat (#7583) 2025-03-20 01:39:16 +00:00
Leonardo de Moura
22b327f077 test: cutsat (#7582)
Additional tests for cutsat
2025-03-20 00:46:07 +00:00
Leonardo de Moura
497ac70c38 feat: improve cutsat counterexamples (#7579)
This PR improves the counterexamples produced by the cutsat procedure,
and adds proper support for `Nat`. Before this PR, the assignment for an
natural variable `x` would be represented as `NatCast.natCast x`.
2025-03-19 19:27:40 +00:00
Sebastian Ullrich
a5348f4bdc perf: avoid taking mutex on task deactivation (#7572) 2025-03-19 15:59:45 +00:00
Sofia Rodrigues
d7d1754e69 feat: socket support using LibUV (#6683)
This PR introduces TCP socket support using the LibUV library, enabling
asynchronous I/O operations with it.

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-03-19 13:54:51 +00:00
Kim Morrison
720f6fca94 chore: fix name of Nat.mul_add_lt_is_or (#7563) 2025-03-19 11:23:03 +00:00
Paul Reichert
a634b96f6d feat: tree map lemmas for minKey? (#7437)
This PR provides (some but not all) lemmas about the tree map function
`minKey?`.
2025-03-19 09:54:33 +00:00
Lean stage0 autoupdater
9821bd9707 chore: update stage0 2025-03-19 10:13:04 +00:00
Kim Morrison
0f781136e7 chore: remove @[simp] from Int.neg_mul and Int.mul_neg (#7559)
This PR removes `@[simp]` from `Int.neg_mul` and `Int.mul_neg`. These
simp lemmas were interfering with normalization of numerals in `simp
+arith`.
2025-03-19 09:21:18 +00:00
Joachim Breitner
41a2e9af19 feat: well-founded recursion: opaque well-foundedness proofs (#5182)
This PR makes functions defined by well-founded recursion use an
`opaque` well-founded proof by default. This reliably prevents kernel
reduction of such definitions and proofs, which tends to be
prohibitively slow (fixes #2171), and which regularly causes
hard-to-debug kernel type-checking failures. This changes renders
`unseal` ineffective for such definitions. To avoid the opaque proof,
annotate the function definition with `@[semireducible]`.
2025-03-19 09:21:04 +00:00
Paul Reichert
bf241f9e86 feat: List.min? lemmas and Option.bind_congr (#7529)
This PR upstreams `bind_congr` from Mathlib and proves that the minimum
of a sorted list is its head and weakens the antisymmetry condition of
`min?_eq_some_iff`. Instead of requiring an `Std.Antisymm` instance,
`min?_eq_some_iff` now only expects a proof that the relation is
antisymmetric *on the elements of the list*. If the new premise is left
out, an autoparam will try to derive it from `Std.Antisymm`, so existing
usages of the theorem will most likely continue to work.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-19 07:33:49 +00:00
David Thrane Christiansen
a97813e11f doc: review docstrings for syntax-related operators in manual (#7534)
This PR adds missing `Syntax`-related docstrings and makes the existing
ones consistent in style with the others.
2025-03-19 05:15:05 +00:00
David Thrane Christiansen
1b0168d7b3 doc: docstring review for System and System.FilePath (#7523)
This PR adds missing docstrings and makes docstring style consistent for
`System` and `System.FilePath`.
2025-03-19 05:14:35 +00:00
David Thrane Christiansen
dc57365e95 doc: review docstrings for Thunk (#7528)
This PR makes the docstrings for `Thunk` consistent with the style of
the others.
2025-03-19 05:14:20 +00:00
Leonardo de Moura
174145929f test: omega test suite with grind (#7562)
We have removed tests about `Fin` and `BitVec` since these features are
not supported in `grind` yet.
2025-03-19 02:04:03 +00:00
Mac Malone
75300d30d3 refactor: lake: unified target config declarations (#7543)
This PR unifies the configuration declarations of dynamic targets,
external libraries, Lean libraries, and Lean executables into a single
data type stored in a unified map within a package.

As a side-effect of these changes, auto-completion now also works on an
empty configuration (after the `where`).

**Breaking change:** Users can no longer define multiple targets with
the same name but different kinds (e.g., a Lean executable and a Lean
library both named `foo`). This should not effect most users as the Lake
DSL already discouraged this.
2025-03-19 01:14:02 +00:00
Leonardo de Moura
2946ba04d5 fix: assert that nonlinear Nat terms are nonneg in cutsat (#7561)
This PR fixes the support for nonlinear `Nat` terms in cutsat. For
example, cutsat was failing in the following example
```lean
example (i j k l : Nat) : i / j + k + l - k = i / j + l := by grind
```
because we were not adding the fact that `i / j` is non negative when we
inject the `Nat` expression into `Int`.
2025-03-19 00:52:04 +00:00
Joachim Breitner
3857603dbb feat: Nat.{div,mod} to use fuel, not fix (#7558)
This PR changes the definition of `Nat.div` and `Nat.mod` to use a
structurally recursive, fuel-based implementation rather than
well-founded recursion. This leads to more predicable reduction behavior
in the kernel.

`Nat.div` and `Nat.mod` are somewhat special because the kernel has
native reduction for them when applied to literals. But sometimes this
does not kick in, and the kernel has to unfold `Nat.div`/`Nat.mod` (e.g.
in `lazy_delta_reduction` when there are open terms around). In these
cases we want a well-behaved definition.

We really do not want to reduce proofs in the kernel, which we want to
prevent anyways well-founded recursion (to be prevented by #5182).

Hence we avoid well-founded recursion here, and use a (somewhat
standard) translation to a fuel-based definition.

(If this idiom is needed more often we could even support it in Lean
with `termination_by +fuel <measure>` rather easily.)
2025-03-18 23:08:42 +00:00
Leonardo de Moura
389537cf0e fix: consistent term order in linear integer normalization (#7560)
This PR ensures that we use the same ordering to normalize linear `Int`
terms and relations. This change affects `simp +arith` and `grind`
normalizer.

This consistency is important in the cutsat procedure. We want to avoid
a situation where the cutsat state contains both "atoms":
- `「(NatCast.natCast x + NatCast.natCast y) % 8」`
- `「(NatCast.natCast y + NatCast.natCast x) % 8」`

This was happening because we were using different orderings for
(nested) terms and relations (`=`, `<=`).
2025-03-18 23:04:06 +00:00
Wojciech Rozowski
134d11f1a3 fix: ignore optParams in isNatCmp (#7551)
This PR changes `isNatCmp` to ignore optional arguments annotations,
when checking for `<`-like comparison between elements of `Nat`. That
previously caused `guessLex` to fail when checking termination of a
function, whose signature involved an optional argument of the type
`Nat`.

Closes https://github.com/leanprover/lean4/issues/7458
2025-03-18 21:21:43 +00:00
David Thrane Christiansen
404a931219 doc: review funext docstring (#7535)
This PR revises the docstring for `funext`, making it more concise and
adding a reference to the manual for more details.

This revised docstring is less technical, while still capturing the most
important points of the prior one.
2025-03-18 20:26:36 +00:00
Leonardo de Moura
e288e9266b fix: bad normalization rule in grind, and missing dsimproc (#7553)
This PR removes a bad normalization rule in `grind`, and adds a missing
dsimproc.
2025-03-18 18:32:25 +00:00
Sebastian Ullrich
53fcae031e perf: async optimizations for Init.Data.BitVec.Lemmas (#7546) 2025-03-18 12:56:16 +00:00
Markus Himmel
d66abc0fc0 feat: lemmas about operations on finite unsigned integers (#7484)
This PR adds some lemmas about operations defined on `UIntX`
2025-03-18 10:52:54 +00:00
Markus Himmel
6a202f5acb feat: Nat, Fin and BitVec theorems required for unsigned integers (#7522)
This PR splits off the required theory about `Nat`, `Fin` and `BitVec`
from #7484.
2025-03-18 08:35:02 +00:00
Siddharth
4e83f23955 feat: bv_normalize pass: AC normalization of multiplication (#7461)
This PR introduces a bitvector associativity/commutativity normalization
on bitvector terms of the form `(a * b) = (c * d)` for `a, b, c, d`
bitvectors. This mirrors Bitwuzla's `PassNormalize::process`'s
`PassNormalize::normalize_eq_add_mul`.

For example, `x₁ * (y₁ * z) = x₂ * (y₂ * z)` is normalized to `z * (x₁ *
y₁) = z * (x₂ * y₂)`,
pulling the shared variable `z` to the front on both sides. The PR also
replaces the use of `ac_nf` in the normalization pass of `bv_decide`.

Note that this is based on Bitwuzla's normalizer, and we eventually want
to have support for bitvector addition normalization as well. However,
since we currently lack a `ring` equivalent for bitvectors, we cannot
currently justify rewrites such as `x + x + x → 3 * x`. Similarly, we
leave the implementation of `PassNormalize::normalize_comm_assoc`, which
is called when the toplevel terms are different for a subsequent patch.

For posterity, we record the precise location in Bitwuzla where the
implemented codepath occurs:
```cpp
-- d1f1bc2ad3/src/preprocess/pass/normalize.cpp (L1550-L1554)
        Kind k = cur.kind();
        if (k == Kind::EQUAL && children[0].kind() == children[1].kind()
            && (children[0].kind() == Kind::BV_ADD
                || children[0].kind() == Kind::BV_MUL))
        {
          auto [res, norm] = normalize_eq_add_mul(children[0], children[1]);
          ...
```

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
Co-authored-by: Tobias Grosser <github@grosser.es>
2025-03-18 08:28:35 +00:00
David Thrane Christiansen
5d91ed01b7 doc: review String docstrings (#7506)
This PR adds missing `String` docstrings and makes the existing ones
consistent in style.
2025-03-18 04:36:49 +00:00
Kim Morrison
ce138e1cec fix: correct names in library lemmas (#7541)
This PR corrects names of a number of lemmas, where the incorrect name
was identified automatically by a
[tool](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/automatic.20spelling.20generation.20.26.20comparison/near/505760384)
written by @Rob23oba.
2025-03-18 03:50:03 +00:00
Leonardo de Moura
0e598c96c9 feat: add [grind cases eager] Subtype (#7540)
This PR adds `[grind cases eager]` attribute to `Subtype`. See new test.
2025-03-18 01:19:22 +00:00
Leonardo de Moura
dad9b18d49 fix: missing reset at decision stack in cutsat (#7538)
This PR fixes a bug in the cutsat model construction. It was not
resetting the decision stack at the end of the search.
2025-03-18 00:21:56 +00:00
Leonardo de Moura
a638e2e207 feat: Int.toNat and Int.natAbst in cutsat (#7537)
This PR implements support for `Int.natAbs` and `Int.toNat` in the
cutsat procedure.
2025-03-17 23:29:21 +00:00
Leonardo de Moura
a0acbd77ea feat: not divides in cutsat (#7536)
This PR implements support for `¬ d ∣ p` in the cutsat procedure.
2025-03-17 22:29:42 +00:00
Joachim Breitner
a26084c433 refactor: Int.div: avoid using unseal (#7533)
In preparation for #5182 (and arguably good practice anyways).
2025-03-17 20:29:27 +00:00
Leonardo de Moura
798da80459 fix: grind push new fact (#7532)
This PR fixes the procedure for putting new facts into the `grind`
"to-do" list. It ensures the new facts are preprocessed. This PR also
removes some of the clutter in the `Nat.sub` support.
2025-03-17 19:14:08 +00:00
Lean stage0 autoupdater
5513f6a468 chore: update stage0 2025-03-17 19:01:29 +00:00
David Thrane Christiansen
70fb253739 doc: review of Array docstrings for manual (#7492)
This PR adds missing `Array` docstrings and makes their style
consistent.
2025-03-17 18:22:01 +00:00
jrr6
4b406b6d5f chore: remove comment from src/stdlib_flags.h (#7531)
This PR removes a misplaced comment from `src/stdlib_flags.h` introduced
by #7425 that was intended to (ephemerally) go in
`stage0/src/stdlib_flags.h`.
2025-03-17 18:07:58 +00:00
David Thrane Christiansen
1a3614616d doc: review docstrings for IO (#7476)
This PR adds missing docstrings for `IO` and related code and makes the
style of the existing docstrings consistent.
2025-03-17 17:59:44 +00:00
David Thrane Christiansen
c53b0c99de fix: broken docstring examples (#7526)
This PR fixes docstring breakage from #7516.
2025-03-17 17:59:03 +00:00
Henrik Böving
5a5e83c26c refactor: the AIG framework to track negations in a more efficient way (#7381)
This PR refactors the AIG datastructures that underly bv_decide in order
to allow a better tracking of negations in the circuit. This refactor
has two effects, for one adding full constant folding to the AIG
framework and secondly enabling us to add further simplifications from
the Brummayer Biere paper in the future which was previously
architecturally impossible.
2025-03-17 17:33:49 +00:00
Henrik Böving
5e0648fe98 feat: bv_decide rewrites around concat, extract and multplication (#7527)
This PR adds the BV_EXTRACT_CONCAT_LHS_RHS, NORM_BV_ADD_MUL and
NORM_BV_SHL_NEG rewrite from Bitwuzla as well as a reduction from
getLsbD to extractLsb' to bv_decide.
2025-03-17 16:01:15 +00:00
Henrik Böving
49819dad16 perf: Add RefVec.emptyWithCapacity to the AIG framework (#7521)
This PR adds the equivalent of `Array.emptyWithCapacity` to the AIG
framework and applies it to `bv_decide`. This is particularly useful as
we are only working with capacities that are always known at run time so
we should never have to reallocate a `RefVec`.
2025-03-17 13:02:51 +00:00
Luisa Cicolini
594587541c feat: add Bitvec.[(toInt, toFin)_twoPow, toNat_twoPow_of_le, toNat_twoPow_of_lt, toNat_twoPow_eq_ite] (#7225)
This PR contains `BitVec.(toInt, toFin)_twoPow` theorems, completing the
API for `BitVec.*_twoPow`. It also expands the `toNat_twoPow` API with
`toNat_twoPow_of_le`, `toNat_twoPow_of_lt`, as well as
`toNat_twoPow_eq_if` and moves `msb_twoPow` up, as it is used in the
`toInt_msb` proof.

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-03-17 12:51:58 +00:00
Siddharth
6df6011641 feat: BitVec.shiftLeft_neg_eq_neg_shiftLeft (#7508)
This PR shows that negation commutes with left shift, which is the
Bitwuzla rewrite
[NORM_BV_SHL_NEG](e09c50818b/src/rewrite/rewrites_bv_norm.cpp (L142-L148)).

```lean
theorem shiftLeft_neg_eq_neg_shiftLeft {x : BitVec w} {y : Nat} :
    (-x) <<< y = - (x <<< y)
```

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2025-03-17 11:54:43 +00:00
Rob23oba
e77b528ef5 perf: reduce elaboration time and proof size of tree map internals (#7459)
There are several things done here:
1. Use the modified `simp_to_model` which already exists in hash maps.
This version of `simp_to_model` allows specifying the query operations
to use in addition to the modifying operations. This is mostly to
improve elaboration time and actually increases olean size.
2. Instead of proving `toListModel_balance` directly, we write
`toListModel_balanceₘ` and use that instead (this saves ~3 MB).
3. Use `fun_cases` and `dsimp` instead of `rw [x.eq_def]` more
frequently in `Balancing.olean` (this saves a bit over 2 MB).
4. Mark `updateCell` and other functions dependent on it as
`noncomputable`. The main problem with `updateCell` is how other
functions, in particular `glue`, get recursively inlined, which blows
the size of the IR (this saves ~1 MB).
5. Instead of using `simp_to_model` to prove results on `insert!`,
`erase!`, etc., `simpa`s are used now, e.g. `simpa only
[insert_eq_insert!] using isEmpty_insert h`. This mainly improves
elaboration time although the olean size also goes down by ~0.3 MB.
2025-03-17 10:05:49 +00:00
Markus Himmel
6153474c00 feat: Neg instance for unsigned integers (#7487)
This PR adds the instance `Neg UInt8`.

This useful if you want to think about finite unsigned integers as a
commutative ring.
2025-03-17 09:06:14 +00:00
Siddharth
654c3781c4 feat: BitVec.neg_mul_not_eq_add_mul (#7493)
This PR implements the Bitwuzla rewrite rule
[NORM_BV_ADD_MUL](e09c50818b/src/rewrite/rewrites_bv_norm.cpp (L19-L23)),
and the associated lemmas to allow for expedient rewriting:

```lean
theorem neg_add_mul_eq_mul_not {x y : BitVec w} : - (x + x * y) = x * ~~~ y
```

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-03-17 08:54:56 +00:00
Kim Morrison
d32a7b250a chore: remove >6 month old deprecations (#7518) 2025-03-17 04:42:05 +00:00
Kim Morrison
53abb99a81 fix: make List/Array modify argument order consistent (#7516)
This PR changes the order of arguments for `List.modify` and
`List.insertIdx`, making them consistent with `Array`.
2025-03-17 04:36:05 +00:00
Leonardo de Moura
e7cde1180b fix: simp +arith (#7515)
This PR fixes another bug in `simp +arith`. This bug was affecting
`grind`. See new test for an example.
2025-03-17 03:11:48 +00:00
Leonardo de Moura
318c782ea7 feat: missing normalization rules for div and mod in grind (#7514)
This PR adds more missing normalization rules for `div` and `mod` to
`grind`.
2025-03-16 23:00:12 +00:00
Leonardo de Moura
0da54f517a fix: missing Nat div and mod norm rules in grind (#7512)
This PR adds missing normalization rules for `Nat` div and mod to the
`grind` tactic.
2025-03-16 21:23:49 +00:00
Leonardo de Moura
1284d43ad7 fix: simp +arith (#7511)
This PR fixes two bugs in `simp +arith` that were preventing specific
subterms from being normalized.
2025-03-16 20:24:51 +00:00
Leonardo de Moura
71b2b67a12 feat: exfalso in grind (#7510)
This PR ensures that `grind` can be used as a more powerful
`contradiction` tactic, sparing the user from having to type `exfalso;
grind` or `intros; exfalso; grind`.
2025-03-16 17:25:19 +00:00
Henrik Böving
84a4e37f1b perf: disable implicitDefEqProofs in bv_decide (#7509)
This PR disables the `implicitDefEqProofs` simp option in the
preprocessor of `bv_decide` in order to account for regressions caused
by #7387.

These regressions were noticed by @abdoo8080 while benchmarking on
SMTLIB:
- 07/03/2025: 30,661 with kernel, 35,153 without kernel
- 14/03/2025: 26,405 with kernel, 35,797 without kernel

I performed testing on a bunch of randomly failing problems from the
regressed set and all of them seem to pass again.

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-03-16 14:45:28 +00:00
Henrik Böving
6f16a535f8 perf: speedup bv_decide's LRAT checker by improving input validaton (#7491)
This PR achieves a speed up in bv_decide's LRAT checker by improving its
input validation.

When the LRAT checker works on a clause it needs to know that the clause
has no duplicate literals and is not tautological (i.e. doesn't contain
the same variable in different polarities). Previously this was done
using a naive quadratic algorithm, now we check the property using a
HashMap in linear time. Beyond this there is also a few micro
optimizations.
Together they improve the runtime on the SMTLIB problem
`non-incremental/QF_BV/20210312-Bouvier/vlsat3_a15.smt2` from `1:25.31`
to `1:01.32` minutes (where 39 seconds of this run time are the SAT
solver and thus completely unaffected by the optimization)

Co-authored-by: @JOSHCLUNE

---------

Co-authored-by: JOSHCLUNE <josh.seth.clune@gmail.com>
2025-03-16 14:29:33 +00:00
Leonardo de Moura
6cbb8876d6 feat: Nat.sub in cutsat (#7503)
This PR implements support for `Nat.sub` in cutsat
2025-03-16 03:03:36 +00:00
Leonardo de Moura
ae81567fbe feat: Nat div/mod in cutsat (#7502)
This PR implements support for `Nat` div and mod in the cutsat
procedure.
2025-03-16 00:29:43 +00:00
Leonardo de Moura
b7354aacaa feat: Nat equalities and disequalities in cutsat (#7501)
This PR implements support for `Nat` equalities and disequalities in the
cutsat procedure.
2025-03-15 21:24:04 +00:00
Sebastian Ullrich
1dc3626ff7 perf: remove most remaining async blockers in Init.Data.List.Sublist (#7500) 2025-03-15 15:26:06 +00:00
Sebastian Ullrich
a788e6aa67 perf: remove more async blockers (#7497) 2025-03-15 11:07:04 +00:00
Sebastian Ullrich
0f06393149 chore: USE_LAKE: integrate into CMake (#4466)
With `USE_LAKE=ON`, only linking is now left to the Makefile.

TODO:
* include stage 0 changes in Lake's trace. This is an issue already on
master but prevents us from using this PR to put .oleans in an Actions
cache.
2025-03-15 08:58:01 +00:00
Sebastian Ullrich
141e52685c fix: include async elaboration time in elaboration profile (#7496) 2025-03-15 07:59:03 +00:00
Lean stage0 autoupdater
10b7c4e46e chore: update stage0 2025-03-15 08:02:41 +00:00
Sebastian Ullrich
41c58002f1 feat: enable Elab.async by default (#7485)
...after successful test on Mathlib
2025-03-15 07:24:52 +00:00
926 changed files with 22364 additions and 7191 deletions

View File

@@ -3,7 +3,7 @@ name: Check awaiting-mathlib label
on:
merge_group:
pull_request:
types: [opened, labeled]
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
check-awaiting-mathlib:
@@ -17,4 +17,4 @@ jobs:
const { labels } = context.payload.pull_request;
if (labels.some(label => label.name == "awaiting-mathlib") && !labels.some(label => label.name == "builds-mathlib")) {
core.setFailed('PR is marked "awaiting-mathlib" but "builds-mathlib" label has not been applied yet by the bot');
}
}

246
.github/workflows/build-template.yml vendored Normal file
View File

@@ -0,0 +1,246 @@
name: build-template
on:
workflow_call:
inputs:
check-level:
type: string
required: true
config:
type: string
required: true
nightly:
type: string
required: true
LEAN_VERSION_MAJOR:
type: string
required: true
LEAN_VERSION_MINOR:
type: string
required: true
LEAN_VERSION_PATCH:
type: string
required: true
LEAN_SPECIAL_VERSION_DESC:
type: string
required: true
RELEASE_TAG:
type: string
required: true
jobs:
build:
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
strategy:
matrix:
include: ${{fromJson(inputs.config)}}
# complete all jobs
fail-fast: false
runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
name: ${{ matrix.name }}
env:
# must be inside workspace
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
# current cache limit
CCACHE_MAXSIZE: 200M
# squelch error message about missing nixpkgs channel
NIX_BUILD_SHELL: bash
LSAN_OPTIONS: max_leaks=10
# somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
CXX: c++
MACOSX_DEPLOYMENT_TARGET: 10.15
steps:
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
if: runner.os == 'Linux' && !matrix.cmultilib
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: clang64
# `:` means do not prefix with msystem
pacboy: "make: python: cmake clang ccache gmp libuv git: zip: unzip: diffutils: binutils: tree: zstd tar:"
if: runner.os == 'Windows'
- name: Install Brew Packages
run: |
brew install ccache tree zstd coreutils gmp libuv
if: runner.os == 'macOS'
- name: Checkout
uses: actions/checkout@v4
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Open Nix shell once
run: true
if: runner.os == 'Linux'
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
# master (as the workflow files themselves are always taken from the merge)
# (needs to be after "Install *" to use the right shell)
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.44
actions-cache-folder: emsdk
if: matrix.wasm
- name: Install 32bit c libs
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386 pkgconf:i386
if: matrix.cmultilib
- name: Cache
if: matrix.name != 'Linux Lake'
uses: actions/cache@v4
with:
path: |
.ccache
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-build-v3
save-always: true
- name: Cache
if: matrix.name == 'Linux Lake'
uses: actions/cache@v4
with:
path: |
.ccache
build/stage1/**/*.trace
build/stage1/**/*.olean
build/stage1/**/*.ilean
build/stage1/**/*.c
build/stage1/**/*.c.o*
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-build-v3
save-always: true
# open nix-shell once for initial setup
- name: Setup
run: |
ccache --zero-stats
if: runner.os == 'Linux'
- name: Set up NPROC
run: |
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
- name: Build
run: |
ulimit -c unlimited # coredumps
[ -d build ] || mkdir build
cd build
# arguments passed to `cmake`
# this also enables githash embedding into stage 1 library
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
if [[ -n '${{ matrix.cross_target }}' ]]; then
# used by `prepare-llvm`
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
OPTIONS+=(-DLEAN_PLATFORM_TARGET=${{ matrix.cross_target }})
fi
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
wget -q ${{ matrix.llvm-url }}
PREPARE="$(${{ matrix.prepare-llvm }})"
eval "OPTIONS+=($PREPARE)"
fi
if [[ -n '${{ matrix.release }}' && -n '${{ inputs.nightly }}' ]]; then
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ inputs.nightly }})
fi
if [[ -n '${{ matrix.release }}' && -n '${{ inputs.RELEASE_TAG }}' ]]; then
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ inputs.LEAN_VERSION_MAJOR }})
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ inputs.LEAN_VERSION_MINOR }})
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ inputs.LEAN_VERSION_PATCH }})
OPTIONS+=(-DLEAN_VERSION_IS_RELEASE=1)
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ inputs.LEAN_SPECIAL_VERSION_DESC }})
fi
# contortion to support empty OPTIONS with old macOS bash
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
time make -j$NPROC
- name: Install
run: |
make -C build install
- name: Check Binaries
run: ${{ matrix.binary-check }} lean-*/bin/* || true
- name: Count binary symbols
run: |
for f in lean-*/bin/*; do
echo "$f: $(nm $f | grep " T " | wc -l) exported symbols"
done
if: matrix.name == 'Windows'
- name: List Install Tree
run: |
# omit contents of Init/, ...
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
- name: Pack
run: |
dir=$(echo lean-*-*)
mkdir pack
# high-compression tar.zst + zip for release, fast tar.zst otherwise
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ inputs.nightly }}' || -n '${{ inputs.RELEASE_TAG }}' ]]; then
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -19 -o pack/$dir.tar.zst
zip -rq pack/$dir.zip $dir
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v4
if: matrix.release
with:
name: build-${{ matrix.name }}
path: pack/*
- name: Lean stats
run: |
build/stage1/bin/lean --stats src/Lean.lean
if: ${{ !matrix.cross }}
- name: Test
id: test
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && inputs.check-level >= 1
- name: Test Summary
uses: test-summary/action@v2
with:
paths: build/stage1/test-results.xml
# prefix `if` above with `always` so it's run even if tests failed
if: always() && steps.test.conclusion != 'skipped'
- name: Check Test Binary
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
if: (!matrix.cross) && steps.test.conclusion != 'skipped'
- name: Build Stage 2
run: |
make -C build -j$NPROC stage2
if: matrix.test-speedcenter
- name: Check Stage 3
run: |
make -C build -j$NPROC check-stage3
if: matrix.test-speedcenter
- name: Test Speedcenter Benchmarks
run: |
# Necessary for some timing metrics but does not work on Namespace runners
# and we just want to test that the benchmarks run at all here
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
cd tests/bench
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
if: matrix.test-speedcenter
- name: Check rebootstrap
run: |
# clean rebuild in case of Makefile changes
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
if: matrix.name == 'Linux' && inputs.check-level >= 1
- name: CCache stats
run: ccache -s
- name: Show stacktrace for coredumps
if: failure() && runner.os == 'Linux'
run: |
for c in $(find . -name core); do
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
echo bt | $GDB/bin/gdb -q $progbin $c || true
done

View File

@@ -20,9 +20,7 @@ jobs:
- name: Identify stage0 changes
run: |
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0 |
grep -v -x -F $'stage0/src/stdlib_flags.h\nstage0/src/lean.mk.in' \
> "$RUNNER_TEMP/stage0" || true
git diff "${BASE:-HEAD^}..HEAD" --name-only -- stage0/stdlib > "$RUNNER_TEMP/stage0" || true
if test -s "$RUNNER_TEMP/stage0"
then
echo "CHANGES=yes" >> "$GITHUB_ENV"

View File

@@ -36,7 +36,9 @@ jobs:
# 2: PRs with `release-ci` label, releases (incl. nightlies)
check-level: ${{ steps.set-level.outputs.check-level }}
# The build matrix, dynamically generated here
matrix: ${{ steps.set-matrix.outputs.result }}
matrix: ${{ steps.set-matrix.outputs.matrix }}
# secondary build jobs that should not block the CI success/merge queue
matrix-secondary: ${{ steps.set-matrix.outputs.matrix-secondary }}
# Should we make a nightly release? If so, this output contains the lean version string, else it is empty
nightly: ${{ steps.set-nightly.outputs.nightly }}
# Should this be the CI for a tagged release?
@@ -135,6 +137,7 @@ jobs:
console.log(`level: ${level}`);
// use large runners where available (original repo)
let large = ${{ github.repository == 'leanprover/lean4' }};
const isPr = "${{ github.event_name }}" == "pull_request";
let matrix = [
{
"name": "Linux LLVM",
@@ -163,6 +166,14 @@ jobs:
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'"
},
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
// just a secondary PR build job for now
"check-level": isPr ? 0 : 3,
"secondary": true,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON"
},
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
@@ -204,13 +215,18 @@ jobs:
"os": "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
// special cased below
// "check-level": 0,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
// Special handling for MacOS aarch64, we want:
// 1. To run it in PRs so Mac devs get PR toolchains (so secondary is sufficient)
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
// little value in the merge queue
// 3. To run it in release (obviously)
"check-level": isPr ? 0 : 2,
"secondary": isPr,
},
{
"name": "Windows",
@@ -262,208 +278,40 @@ jobs:
// }
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
const isPr = "${{ github.event_name }}" == "pull_request";
const filter = (job) => {
if (job["name"] === "macOS aarch64") {
// Special handling for MacOS aarch64, we want:
// 1. To run it in PRs so Mac devs get PR toolchains
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
// little value in the merge queue
// 3. To run it in release (obviously)
return isPr || level >= 2;
} else {
return level >= job["check-level"];
}
};
return matrix.filter(filter);
matrix = matrix.filter((job) => level >= job["check-level"]);
core.setOutput('matrix', matrix.filter((job) => !job["secondary"]));
core.setOutput('matrix-secondary', matrix.filter((job) => job["secondary"]));
build:
needs: [configure]
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
strategy:
matrix:
include: ${{fromJson(needs.configure.outputs.matrix)}}
# complete all jobs
fail-fast: false
runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
name: ${{ matrix.name }}
env:
# must be inside workspace
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
# current cache limit
CCACHE_MAXSIZE: 200M
# squelch error message about missing nixpkgs channel
NIX_BUILD_SHELL: bash
LSAN_OPTIONS: max_leaks=10
# somehow MinGW clang64 (or cmake?) defaults to `g++` even though it doesn't exist
CXX: c++
MACOSX_DEPLOYMENT_TARGET: 10.15
steps:
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
if: runner.os == 'Linux' && !matrix.cmultilib
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: clang64
# `:` means do not prefix with msystem
pacboy: "make: python: cmake clang ccache gmp libuv git: zip: unzip: diffutils: binutils: tree: zstd tar:"
if: runner.os == 'Windows'
- name: Install Brew Packages
run: |
brew install ccache tree zstd coreutils gmp libuv
if: runner.os == 'macOS'
- name: Checkout
uses: actions/checkout@v4
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
# master (as the workflow files themselves are always taken from the merge)
# (needs to be after "Install *" to use the right shell)
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.44
actions-cache-folder: emsdk
if: matrix.wasm
- name: Install 32bit c libs
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386 pkgconf:i386
if: matrix.cmultilib
- name: Cache
uses: actions/cache@v4
with:
path: .ccache
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-build-v3
save-always: true
# open nix-shell once for initial setup
- name: Setup
run: |
ccache --zero-stats
if: runner.os == 'Linux'
- name: Set up NPROC
run: |
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
- name: Build
run: |
mkdir build
cd build
# arguments passed to `cmake`
# this also enables githash embedding into stage 1 library
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
if [[ -n '${{ matrix.cross_target }}' ]]; then
# used by `prepare-llvm`
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
OPTIONS+=(-DLEAN_PLATFORM_TARGET=${{ matrix.cross_target }})
fi
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
wget -q ${{ matrix.llvm-url }}
PREPARE="$(${{ matrix.prepare-llvm }})"
eval "OPTIONS+=($PREPARE)"
fi
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.nightly }}' ]]; then
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.nightly }})
fi
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ needs.configure.outputs.LEAN_VERSION_MAJOR }})
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ needs.configure.outputs.LEAN_VERSION_MINOR }})
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ needs.configure.outputs.LEAN_VERSION_PATCH }})
OPTIONS+=(-DLEAN_VERSION_IS_RELEASE=1)
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }})
fi
# contortion to support empty OPTIONS with old macOS bash
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
time make -j$NPROC
- name: Install
run: |
make -C build install
- name: Check Binaries
run: ${{ matrix.binary-check }} lean-*/bin/* || true
- name: Count binary symbols
run: |
for f in lean-*/bin/*; do
echo "$f: $(nm $f | grep " T " | wc -l) exported symbols"
done
if: matrix.name == 'Windows'
- name: List Install Tree
run: |
# omit contents of Init/, ...
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
- name: Pack
run: |
dir=$(echo lean-*-*)
mkdir pack
# high-compression tar.zst + zip for release, fast tar.zst otherwise
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ needs.configure.outputs.nightly }}' || -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -19 -o pack/$dir.tar.zst
zip -rq pack/$dir.zip $dir
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v4
if: matrix.release
with:
name: build-${{ matrix.name }}
path: pack/*
- name: Lean stats
run: |
build/stage1/bin/lean --stats src/Lean.lean
if: ${{ !matrix.cross }}
- name: Test
id: test
run: |
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.check-level >= 1
- name: Test Summary
uses: test-summary/action@v2
with:
paths: build/stage1/test-results.xml
# prefix `if` above with `always` so it's run even if tests failed
if: always() && steps.test.conclusion != 'skipped'
- name: Check Test Binary
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
if: (!matrix.cross) && steps.test.conclusion != 'skipped'
- name: Build Stage 2
run: |
make -C build -j$NPROC stage2
if: matrix.test-speedcenter
- name: Check Stage 3
run: |
make -C build -j$NPROC check-stage3
if: matrix.test-speedcenter
- name: Test Speedcenter Benchmarks
run: |
# Necessary for some timing metrics but does not work on Namespace runners
# and we just want to test that the benchmarks run at all here
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
cd tests/bench
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
if: matrix.test-speedcenter
- name: Check rebootstrap
run: |
# clean rebuild in case of Makefile changes
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
if: matrix.name == 'Linux' && needs.configure.outputs.check-level >= 1
- name: CCache stats
run: ccache -s
needs: [configure]
uses: ./.github/workflows/build-template.yml
with:
config: ${{needs.configure.outputs.matrix}}
check-level: ${{ needs.configure.outputs.check-level }}
nightly: ${{ needs.configure.outputs.nightly }}
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }}
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }}
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }}
secrets: inherit
# build jobs that should not be considered by `all-done` below
build-secondary:
needs: [configure]
if: needs.configure.outputs.matrix-secondary != '[]'
uses: ./.github/workflows/build-template.yml
with:
config: ${{needs.configure.outputs.matrix-secondary}}
check-level: ${{ needs.configure.outputs.check-level }}
nightly: ${{ needs.configure.outputs.nightly }}
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
LEAN_VERSION_PATCH: ${{ needs.configure.outputs.LEAN_VERSION_PATCH }}
LEAN_SPECIAL_VERSION_DESC: ${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }}
RELEASE_TAG: ${{ needs.configure.outputs.RELEASE_TAG }}
secrets: inherit
# This job collects results from all the matrix jobs
# This can be made the "required" job, instead of listing each

View File

@@ -15,10 +15,7 @@ foreach(var ${vars})
# must forward options that generate incompatible .olean format
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
if("${var}" MATCHES "LLVM*")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
if("${var}" MATCHES "PKG_CONFIG*")
if("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE")
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
@@ -68,8 +65,8 @@ ExternalProject_add(stage0
SOURCE_SUBDIR src
BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
# (however, `CHECK_OLEAN_VERSION=ON` in CI will override this as we need to
# embed the githash into the stage 1 library built by stage 0)
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install
@@ -83,6 +80,7 @@ ExternalProject_add(stage1
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage0
STEP_TARGETS configure
)
ExternalProject_add(stage2
SOURCE_DIR "${LEAN_SOURCE_DIR}"

View File

@@ -63,6 +63,7 @@
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
# for CI coredumps
GDB = pkgsDist.gdb;
});
in {

View File

@@ -455,20 +455,20 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -Wl,--no-whole-archive")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=$ORIGIN/../lib:$ORIGIN/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -Wl,--no-whole-archive")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -515,7 +515,16 @@ if(USE_GITHASH)
message(STATUS "git commit sha1: ${GIT_SHA1}")
endif()
else()
set(GIT_SHA1 "")
if(USE_LAKE AND ${STAGE} EQUAL 0)
# we need to embed *some* hash for Lake to invalidate stage 1 on stage 0 changes
execute_process(
COMMAND git ls-tree HEAD "${CMAKE_CURRENT_SOURCE_DIR}/../../stage0" --object-only
OUTPUT_VARIABLE GIT_SHA1
OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "stage0 sha1: ${GIT_SHA1}")
else()
set(GIT_SHA1 "")
endif()
endif()
configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h")
@@ -542,6 +551,9 @@ include_directories(${LEAN_SOURCE_DIR})
include_directories(${CMAKE_BINARY_DIR}) # version.h etc., "private" headers
include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" headers
# Lean code only needs this one include
string(APPEND LEANC_OPTS " -I${CMAKE_BINARY_DIR}/include")
# Use CMake profile C++ flags for building Lean libraries, but do not embed in `leanc`
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
@@ -754,7 +766,12 @@ add_custom_target(clean-olean
DEPENDS clean-stdlib)
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
PATTERN temp EXCLUDE)
PATTERN temp
PATTERN "*.export"
PATTERN "*.hash"
PATTERN "*.trace"
PATTERN "*.rsp"
EXCLUDE)
# symlink source into expected installation location for go-to-definition, if file system allows it
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
@@ -785,10 +802,32 @@ if(LEAN_INSTALL_PREFIX)
endif()
# Escape for `make`. Yes, twice.
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
string(REPLACE "$" "\\\$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
# hacky
function(toml_escape IN OUTVAR)
if(IN)
string(STRIP "${IN}" OUT)
string(REPLACE " " "\", \"" OUT "${OUT}")
set(${OUTVAR} "\"${OUT}\"" PARENT_SCOPE)
endif()
endfunction()
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_CC "${LEANC_CC}")
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_FLAGS "${LEANC_INTERNAL_FLAGS}")
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_LINKER_FLAGS "${LEANC_INTERNAL_LINKER_FLAGS}")
set(LEANC_OPTS_TOML "${LEANC_OPTS} ${LEANC_EXTRA_CC_FLAGS} ${LEANC_INTERNAL_FLAGS}")
set(LINK_OPTS_TOML "${LEANC_INTERNAL_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/lib/lean ${LEAN_EXTRA_LINKER_FLAGS}")
toml_escape("${LEAN_EXTRA_MAKE_OPTS}" LEAN_EXTRA_OPTS_TOML)
toml_escape("${LEANC_OPTS_TOML}" LEANC_OPTS_TOML)
toml_escape("${LINK_OPTS_TOML}" LINK_OPTS_TOML)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(LAKE_LIB_PREFIX "lib")
endif()
if(USE_LAKE AND STAGE EQUAL 1)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/lakefile.toml)

View File

@@ -175,7 +175,10 @@ theorem or_iff_not_imp_right : a b ↔ (¬b → a) := Decidable.or_iff_not_i
theorem not_imp_iff_and_not : ¬(a b) a ¬b := Decidable.not_imp_iff_and_not
theorem not_and_iff_or_not_not : ¬(a b) ¬a ¬b := Decidable.not_and_iff_or_not_not
theorem not_and_iff_not_or_not : ¬(a b) ¬a ¬b := Decidable.not_and_iff_not_or_not
@[deprecated not_and_iff_not_or_not (since := "2025-03-18")]
abbrev not_and_iff_or_not_not := @not_and_iff_not_or_not
theorem not_iff : ¬(a b) (¬a b) := Decidable.not_iff

View File

@@ -135,6 +135,13 @@ instance : ToBool Bool where
| true => t
| false => f
/--
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns it and ignores
`y`; otherwise, runs `y` and returns its result.
This a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
operator.
-/
@[macro_inline] def orM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
let b x
match toBool b with
@@ -145,6 +152,13 @@ infixr:30 " <||> " => orM
recommended_spelling "orM" for "<||>" in [orM, «term_<||>_»]
/--
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns `y`; otherwise,
returns the original result of `x`.
This a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
operator.
-/
@[macro_inline] def andM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
let b x
match toBool b with
@@ -155,6 +169,9 @@ infixr:35 " <&&> " => andM
recommended_spelling "andM" for "<&&>" in [andM, «term_<&&>_»]
/--
Runs a monadic action and returns the negation of its result.
-/
@[macro_inline] def notM {m : Type Type v} [Applicative m] (x : m Bool) : m Bool :=
not <$> x
@@ -270,21 +287,61 @@ Using `control` means that `runInBase` can be used multiple times.
-/
/-- MonadControl is a way of stating that the monad `m` can be 'run inside' the monad `n`.
This is the same as [`MonadBaseControl`](https://hackage.haskell.org/package/monad-control-1.0.3.1/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl) in Haskell.
To learn about `MonadControl`, see the comment above this docstring.
/--
A way to lift a computation from one monad to another while providing the lifted computation with a
means of interpreting computations from the outer monad. This provides a means of lifting
higher-order operations automatically.
Clients should typically use `control` or `controlAt`, which request an instance of `MonadControlT`:
the reflexive, transitive closure of `MonadControl`. New instances should be defined for
`MonadControl` itself.
-/
-- This is the same as
-- [`MonadBaseControl`](https://hackage.haskell.org/package/monad-control-1.0.3.1/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl)
-- in Haskell.
class MonadControl (m : semiOutParam (Type u Type v)) (n : Type u Type w) where
/--
A type that can be used to reconstruct both a returned value and any state used by the outer
monad.
-/
stM : Type u Type u
/--
Lifts an action from the inner monad `m` to the outer monad `n`. The inner monad has access to a
reverse lifting operator that can run an `n` action, returning a value and state together.
-/
liftWith : {α : Type u} (({β : Type u} n β m (stM β)) m α) n α
/--
Lifts a monadic action that returns a state and a value in the inner monad to an action in the
outer monad. The extra state information is used to restore the results of effects from the
reverse lift passed to `liftWith`'s parameter.
-/
restoreM : {α : Type u} m (stM α) n α
/-- Transitive closure of MonadControl. -/
/--
A way to lift a computation from one monad to another while providing the lifted computation with a
means of interpreting computations from the outer monad. This provides a means of lifting
higher-order operations automatically.
Clients should typically use `control` or `controlAt`, which request an instance of `MonadControlT`:
the reflexive, transitive closure of `MonadControl`. New instances should be defined for
`MonadControl` itself.
-/
class MonadControlT (m : Type u Type v) (n : Type u Type w) where
/--
A type that can be used to reconstruct both a returned value and any state used by the outer
monad.
-/
stM : Type u Type u
/--
Lifts an action from the inner monad `m` to the outer monad `n`. The inner monad has access to a
reverse lifting operator that can run an `n` action, returning a value and state together.
-/
liftWith : {α : Type u} (({β : Type u} n β m (stM β)) m α) n α
/--
Lifts a monadic action that returns a state and a value in the inner monad to an action in the
outer monad. The extra state information is used to restore the results of effects from the
reverse lift passed to `liftWith`'s parameter.
-/
restoreM {α : Type u} : stM α n α
export MonadControlT (stM liftWith restoreM)
@@ -300,11 +357,28 @@ instance (m : Type u → Type v) [Pure m] : MonadControlT m m where
liftWith f := f fun x => x
restoreM x := pure x
/--
Lifts an operation from an inner monad to an outer monad, providing it with a reverse lifting
operator that allows outer monad computations to be run in the inner monad. The lifted operation is
required to return extra information that is required in order to reconstruct the reverse lift's
effects in the outer monad; this extra information is determined by `stM`.
This function takes the inner monad as an explicit parameter. Use `control` to infer the monad.
-/
@[always_inline, inline]
def controlAt (m : Type u Type v) {n : Type u Type w} [MonadControlT m n] [Bind n] {α : Type u}
(f : ({β : Type u} n β m (stM m n β)) m (stM m n α)) : n α :=
liftWith f >>= restoreM
/--
Lifts an operation from an inner monad to an outer monad, providing it with a reverse lifting
operator that allows outer monad computations to be run in the inner monad. The lifted operation is
required to return extra information that is required in order to reconstruct the reverse lift's
effects in the outer monad; this extra information is determined by `stM`.
This function takes the inner monad as an implicit parameter. Use `controlAt` to specify it
explicitly.
-/
@[always_inline, inline]
def control {m : Type u Type v} {n : Type u Type w} [MonadControlT m n] [Bind n] {α : Type u}
(f : ({β : Type u} n β m (stM m n β)) m (stM m n α)) : n α :=

View File

@@ -265,13 +265,24 @@ instance (ε : Type u) (m : Type u → Type v) [Monad m] : MonadControl m (Excep
liftWith f := liftM <| f fun x => x.run
restoreM x := x
/--
Monads that provide the ability to ensure an action happens, regardless of exceptions or other
failures.
`MonadFinally.tryFinally'` is used to desugar `try ... finally ...` syntax.
-/
class MonadFinally (m : Type u Type v) where
/-- `tryFinally' x f` runs `x` and then the "finally" computation `f`.
When `x` succeeds with `a : α`, `f (some a)` is returned. If `x` fails
for `m`'s definition of failure, `f none` is returned. Hence `tryFinally'`
can be thought of as performing the same role as a `finally` block in
an imperative programming language. -/
tryFinally' {α β} : m α (Option α m β) m (α × β)
/--
Runs an action, ensuring that some other action always happens afterward.
More specifically, `tryFinally' x f` runs `x` and then the “finally” computation `f`. If `x`
succeeds with some value `a : α`, `f (some a)` is returned. If `x` fails for `m`'s definition of
failure, `f none` is returned.
`tryFinally'` can be thought of as performing the same role as a `finally` block in an imperative
programming language.
-/
tryFinally' {α β} : (x : m α) (f : Option α m β) m (α × β)
export MonadFinally (tryFinally')

View File

@@ -8,13 +8,22 @@ import Init.Data.Option.Basic
import Init.Control.Basic
import Init.Control.Except
set_option linter.missingDocs true
universe u v
instance : ToBool (Option α) := Option.isSome
/--
Adds the ability to fail to a monad. Unlike ordinary exceptions, there is no way to signal why a
failure occurred.
-/
def OptionT (m : Type u Type v) (α : Type u) : Type v :=
m (Option α)
/--
Executes an action that might fail in the underlying monad `m`, returning `none` in case of failure.
-/
@[always_inline, inline]
def OptionT.run {m : Type u Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
x
@@ -22,15 +31,25 @@ def OptionT.run {m : Type u → Type v} {α : Type u} (x : OptionT m α) : m (Op
namespace OptionT
variable {m : Type u Type v} [Monad m] {α β : Type u}
/--
Converts an action that returns an `Option` into one that might fail, with `none` indicating
failure.
-/
protected def mk (x : m (Option α)) : OptionT m α :=
x
/--
Sequences two potentially-failing actions. The second action is run only if the first succeeds.
-/
@[always_inline, inline]
protected def bind (x : OptionT m α) (f : α OptionT m β) : OptionT m β := OptionT.mk do
match ( x) with
| some a => f a
| none => pure none
/--
Succeeds with the provided value.
-/
@[always_inline, inline]
protected def pure (a : α) : OptionT m α := OptionT.mk do
pure (some a)
@@ -40,11 +59,17 @@ instance : Monad (OptionT m) where
pure := OptionT.pure
bind := OptionT.bind
/--
Recovers from failures. Typically used via the `<|>` operator.
-/
@[always_inline, inline] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
match ( x) with
| some a => pure (some a)
| _ => y ()
/--
A recoverable failure.
-/
@[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do
pure none
@@ -52,6 +77,12 @@ instance : Alternative (OptionT m) where
failure := OptionT.fail
orElse := OptionT.orElse
/--
Converts a computation from the underlying monad into one that could fail, even though it does not.
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
return some ( x)
@@ -59,6 +90,9 @@ instance : MonadLift m (OptionT m) := ⟨OptionT.lift⟩
instance : MonadFunctor m (OptionT m) := fun f x => f x
/--
Handles failures by treating them as exceptions of type `Unit`.
-/
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle ()
pure a

View File

@@ -10,12 +10,21 @@ import Init.Control.Basic
import Init.Control.Id
import Init.Control.Except
set_option linter.missingDocs true
namespace ReaderT
/--
Recovers from errors. The same local value is provided to both branches. Typically used via the
`<|>` operator.
-/
@[always_inline, inline]
protected def orElse [Alternative m] (x₁ : ReaderT ρ m α) (x₂ : Unit ReaderT ρ m α) : ReaderT ρ m α :=
fun s => x₁ s <|> x₂ () s
/--
Fails with a recoverable error.
-/
@[always_inline, inline]
protected def failure [Alternative m] : ReaderT ρ m α :=
fun _ => failure
@@ -35,4 +44,8 @@ instance : MonadControl m (ReaderT ρ m) where
instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)
/--
A monad with access to a read-only value of type `ρ`. The value can be locally overridden by
`withReader`, but it cannot be mutated.
-/
@[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id

View File

@@ -9,19 +9,42 @@ prelude
import Init.Control.Basic
import Init.Control.Id
import Init.Control.Except
set_option linter.missingDocs true
universe u v w
/--
Adds a mutable state of type `σ` to a monad.
Actions in the resulting monad are functions that take an initial state and return, in `m`, a tuple
of a value and a state.
-/
def StateT (σ : Type u) (m : Type u Type v) (α : Type u) : Type (max u v) :=
σ m (α × σ)
/--
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value paired with the final state.
-/
@[always_inline, inline]
def StateT.run {σ : Type u} {m : Type u Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
x s
/--
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value, discarding the final state.
-/
@[always_inline, inline]
def StateT.run' {σ : Type u} {m : Type u Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
(·.1) <$> x s
/--
A tuple-based state monad.
Actions in `StateM σ` are functions that take an initial state and return a value paired with a
final state.
-/
@[reducible]
def StateM (σ α : Type u) : Type u := StateT σ Id α
@@ -38,14 +61,23 @@ section
variable {σ : Type u} {m : Type u Type v}
variable [Monad m] {α β : Type u}
/--
Returns the given value without modifying the state. Typically used via `Pure.pure`.
-/
@[always_inline, inline]
protected def pure (a : α) : StateT σ m α :=
fun s => pure (a, s)
/--
Sequences two actions. Typically used via the `>>=` operator.
-/
@[always_inline, inline]
protected def bind (x : StateT σ m α) (f : α StateT σ m β) : StateT σ m β :=
fun s => do let (a, s) x s; f a s
/--
Modifies the value returned by a computation. Typically used via the `<$>` operator.
-/
@[always_inline, inline]
protected def map (f : α β) (x : StateT σ m α) : StateT σ m β :=
fun s => do let (a, s) x s; pure (f a, s)
@@ -56,10 +88,17 @@ instance : Monad (StateT σ m) where
bind := StateT.bind
map := StateT.map
/--
Recovers from errors. The state is rolled back on error recovery. Typically used via the `<|>`
operator.
-/
@[always_inline, inline]
protected def orElse [Alternative m] {α : Type u} (x₁ : StateT σ m α) (x₂ : Unit StateT σ m α) : StateT σ m α :=
fun s => x₁ s <|> x₂ () s
/--
Fails with a recoverable error. The state is rolled back on error recovery.
-/
@[always_inline, inline]
protected def failure [Alternative m] {α : Type u} : StateT σ m α :=
fun _ => failure
@@ -68,18 +107,40 @@ instance [Alternative m] : Alternative (StateT σ m) where
failure := StateT.failure
orElse := StateT.orElse
/--
Retrieves the current value of the monad's mutable state.
This increments the reference count of the state, which may inhibit in-place updates.
-/
@[always_inline, inline]
protected def get : StateT σ m σ :=
fun s => pure (s, s)
/--
Replaces the mutable state with a new value.
-/
@[always_inline, inline]
protected def set : σ StateT σ m PUnit :=
fun s' _ => pure (, s')
/--
Applies a function to the current state that both computes a new state and a value. The new state
replaces the current state, and the value is returned.
It is equivalent to `do let (a, s) := f (← StateT.get); StateT.set s; pure a`. However, using
`StateT.modifyGet` may lead to better performance because it doesn't add a new reference to the
state value, and additional references can inhibit in-place updates of data.
-/
@[always_inline, inline]
protected def modifyGet (f : σ α × σ) : StateT σ m α :=
fun s => pure (f s)
/--
Runs an action from the underlying monad in the monad with state. The state is not modified.
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline]
protected def lift {α : Type u} (t : m α) : StateT σ m α :=
fun s => do let a t; pure (a, s)

View File

@@ -6,24 +6,45 @@ Authors: Leonardo de Moura
prelude
import Init.Control.Lawful.Basic
set_option linter.missingDocs true
/-!
The State monad transformer using CPS style.
-/
/--
An alternative implementation of a state monad transformer that internally uses continuation passing
style instead of tuples.
-/
def StateCpsT (σ : Type u) (m : Type u Type v) (α : Type u) := (δ : Type u) σ (α σ m δ) m δ
namespace StateCpsT
variable {α σ : Type u} {m : Type u Type v}
/--
Runs a stateful computation that's represented using continuation passing style by providing it with
an initial state and a continuation.
-/
@[always_inline, inline]
def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k
/--
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value paired with the final state.
While the state is internally represented in continuation passing style, the resulting value is the
same as for a non-CPS state monad.
-/
@[always_inline, inline]
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s))
/--
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value, discarding the final state.
-/
@[always_inline, inline]
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a)
@@ -43,6 +64,12 @@ instance : MonadStateOf σ (StateCpsT σ m) where
set s := fun _ _ k => k s
modifyGet f := fun _ s k => let (a, s) := f s; k a s
/--
Runs an action from the underlying monad in the monad with state. The state is not modified.
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline]
protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
fun _ s k => x >>= (k . s)

View File

@@ -8,10 +8,23 @@ The State monad transformer using IO references.
prelude
import Init.System.ST
set_option linter.missingDocs true
/--
A state monad that uses an actual mutable reference cell (i.e. an `ST.Ref ω σ`).
The macro `StateRefT σ m α` infers `ω` from `m`. It should normally be used instead.
-/
def StateRefT' (ω : Type) (σ : Type) (m : Type Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
/-! Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/
/--
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value paired with the final state.
The monad `m` must support `ST` effects in order to create and mutate reference cells.
-/
@[always_inline, inline]
def StateRefT'.run {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m (α × σ) := do
let ref ST.mkRef s
@@ -19,6 +32,12 @@ def StateRefT'.run {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
let s ref.get
pure (a, s)
/--
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value, discarding the final state.
The monad `m` must support `ST` effects in order to create and mutate reference cells.
-/
@[always_inline, inline]
def StateRefT'.run' {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m α := do
let (a, _) x.run s
@@ -27,6 +46,12 @@ def StateRefT'.run' {ω σ : Type} {m : Type → Type} [Monad m] [MonadLiftT (ST
namespace StateRefT'
variable {ω σ : Type} {m : Type Type} {α : Type}
/--
Runs an action from the underlying monad in the monad with state. The state is not modified.
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline]
protected def lift (x : m α) : StateRefT' ω σ m α :=
fun _ => x
@@ -36,14 +61,30 @@ instance : MonadLift m (StateRefT' ω σ m) := ⟨StateRefT'.lift⟩
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
/--
Retrieves the current value of the monad's mutable state.
This increments the reference count of the state, which may inhibit in-place updates.
-/
@[inline]
protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
fun ref => ref.get
/--
Replaces the mutable state with a new value.
-/
@[inline]
protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
fun ref => ref.set s
/--
Applies a function to the current state that both computes a new state and a value. The new state
replaces the current state, and the value is returned.
It is equivalent to a `get` followed by a `set`. However, using `modifyGet` may lead to higher
performance because it doesn't add a new reference to the state value. Additional references can
inhibit in-place updates of data.
-/
@[inline]
protected def modifyGet [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
fun ref => ref.modifyGet f

View File

@@ -68,35 +68,56 @@ instance : DecidableEq Empty := fun a => a.elim
instance : DecidableEq PEmpty := fun a => a.elim
/--
Thunks are "lazy" values that are evaluated when first accessed using `Thunk.get/map/bind`.
The value is then stored and not recomputed for all further accesses. -/
-- NOTE: the runtime has special support for the `Thunk` type to implement this behavior
Delays evaluation. The delayed code is evaluated at most once.
A thunk is code that constructs a value when it is requested via `Thunk.get`, `Thunk.map`, or
`Thunk.bind`. The resulting value is cached, so the code is executed at most once. This is also
known as lazy or call-by-need evaluation.
The Lean runtime has special support for the `Thunk` type in order to implement the caching
behavior.
-/
structure Thunk (α : Type u) : Type u where
/-- Constructs a new thunk from a function `Unit → α`
that will be called when the thunk is forced. -/
/--
Constructs a new thunk from a function `Unit → α` that will be called when the thunk is first
forced.
The result is cached. It is re-used when the thunk is forced again.
-/
mk ::
/-- Extract the getter function out of a thunk. Use `Thunk.get` instead. -/
private fn : Unit α
attribute [extern "lean_mk_thunk"] Thunk.mk
/-- Store a value in a thunk. Note that the value has already been computed, so there is no laziness. -/
/--
Stores an already-computed value in a thunk.
Because the value has already been computed, there is no laziness.
-/
@[extern "lean_thunk_pure"] protected def Thunk.pure (a : α) : Thunk α :=
fun _ => a
/--
Forces a thunk to extract the value. This will cache the result,
so a second call to the same function will return the value in O(1)
instead of calling the stored getter function.
Gets the thunk's value. If the value is cached, it is returned in constant time; if not, it is
computed.
Computed values are cached, so the value is not recomputed.
-/
-- NOTE: we use `Thunk.get` instead of `Thunk.fn` as the accessor primitive as the latter has an additional `Unit` argument
@[extern "lean_thunk_get_own"] protected def Thunk.get (x : @& Thunk α) : α :=
x.fn ()
/-- Map a function over a thunk. -/
/--
Constructs a new thunk that forces `x` and then applies `x` to the result. Upon forcing, the result
of `f` is cached and the reference to the thunk `x` is dropped.
-/
@[inline] protected def Thunk.map (f : α β) (x : Thunk α) : Thunk β :=
fun _ => f x.get
/-- Constructs a thunk that applies `f` to the result of `x` when forced. -/
/--
Constructs a new thunk that applies `f` to the result of `x` when forced.
-/
@[inline] protected def Thunk.bind (x : Thunk α) (f : α Thunk β) : Thunk β :=
fun _ => (f x.get).get
@@ -575,7 +596,12 @@ attribute [extern "lean_task_pure"] Task.pure
attribute [extern "lean_task_get_own"] Task.get
namespace Task
/-- Task priority. Tasks with higher priority will always be scheduled before ones with lower priority. -/
/--
Task priority.
Tasks with higher priority will always be scheduled before tasks with lower priority. Tasks with a
priority greater than `Task.Priority.max` are scheduled on dedicated threads.
-/
abbrev Priority := Nat
/-- The default priority for spawned tasks, also the lowest priority: `0`. -/
@@ -583,16 +609,18 @@ def Priority.default : Priority := 0
/--
The highest regular priority for spawned tasks: `8`.
Spawning a task with a priority higher than `Task.Priority.max` is not an error but
will spawn a dedicated worker for the task, see `Task.Priority.dedicated`.
Regular priority tasks are placed in a thread pool and worked on according to the priority order.
Spawning a task with a priority higher than `Task.Priority.max` is not an error but will spawn a
dedicated worker for the task. This is indicated using `Task.Priority.dedicated`. Regular priority
tasks are placed in a thread pool and worked on according to their priority order.
-/
-- see `LEAN_MAX_PRIO`
def Priority.max : Priority := 8
/--
Indicates that a task should be scheduled on a dedicated thread.
Any priority higher than `Task.Priority.max` will result in the task being scheduled
immediately on a dedicated thread. This is particularly useful for long-running and/or
I/O-bound tasks since Lean will by default allocate no more non-dedicated workers
I/O-bound tasks since Lean will, by default, allocate no more non-dedicated workers
than the number of cores to reduce context switches.
-/
def Priority.dedicated : Priority := 9
@@ -837,37 +865,55 @@ noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : S
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : HEq a b) (h₂ : p a) : p b :=
eq_of_heq h₁ h₂
/-- Substitution with heterogeneous equality. -/
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
HEq.ndrecOn h₁ h₂
/-- Heterogeneous equality is symmetric. -/
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
h.rec (HEq.refl a)
/-- Propositionally equal terms are also heterogeneously equal. -/
theorem heq_of_eq (h : a = a') : HEq a a' :=
Eq.subst h (HEq.refl a)
/-- Heterogeneous equality is transitive. -/
theorem HEq.trans (h₁ : HEq a b) (h₂ : HEq b c) : HEq a c :=
HEq.subst h₂ h₁
/-- Heterogeneous equality precomposes with propositional equality. -/
theorem heq_of_heq_of_eq (h₁ : HEq a b) (h₂ : b = b') : HEq a b' :=
HEq.trans h₁ (heq_of_eq h₂)
/-- Heterogeneous equality postcomposes with propositional equality. -/
theorem heq_of_eq_of_heq (h₁ : a = a') (h₂ : HEq a' b) : HEq a b :=
HEq.trans (heq_of_eq h₁) h₂
/-- If two terms are heterogeneously equal then their types are propositionally equal. -/
theorem type_eq_of_heq (h : HEq a b) : α = β :=
h.rec (Eq.refl α)
end
/--
Rewriting inside `φ` using `Eq.recOn` yields a term that's heterogeneously equal to the original
term.
-/
theorem eqRec_heq {α : Sort u} {φ : α Sort v} {a a' : α} : (h : a = a') (p : φ a) HEq (Eq.recOn (motive := fun x _ => φ x) h p) p
| rfl, p => HEq.refl p
/--
If casting a term with `Eq.rec` to another type makes it equal to some other term, then the two
terms are heterogeneously equal.
-/
theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : Eq.rec (motive := fun α _ => α) a h₁ = b) : HEq a b := by
subst h₁
apply heq_of_eq
exact h₂
/--
The result of casting a term with `cast` is heterogeneously equal to the original term.
-/
theorem cast_heq {α β : Sort u} : (h : α = β) (a : α) HEq (cast h a) a
| rfl, a => HEq.refl a
@@ -1201,9 +1247,6 @@ inductive Relation.TransGen {α : Sort u} (r : αα → Prop) : αα
This is the inductive case of the transitive closure. -/
| tail {a b c} : TransGen r a b r b c TransGen r a c
/-- Deprecated synonym for `Relation.TransGen`. -/
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
/-- The transitive closure is transitive. -/
theorem Relation.TransGen.trans {α : Sort u} {r : α α Prop} {a b c} :
TransGen r a b TransGen r b c TransGen r a c := by
@@ -1346,10 +1389,6 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
theorem Exists.of_psigma_prop {α : Sort u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
| x, hx => x, hx
@[deprecated Exists.of_psigma_prop (since := "2024-07-27")]
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x) :=
Exists.of_psigma_prop
protected theorem PSigma.eta {α : Sort u} {β : α Sort v} {a₁ a₂ : α} {b₁ : β a₁} {b₂ : β a₂}
(h₁ : a₁ = a₂) (h₂ : Eq.ndrec b₁ h₁ = b₂) : PSigma.mk a₁ b₁ = PSigma.mk a₂ b₂ := by
subst h₁
@@ -2120,16 +2159,13 @@ instance Quotient.decidableEq {α : Sort u} {s : Setoid α} [d : ∀ (a b : α),
/-! # Function extensionality -/
/--
**Function extensionality** is the statement that if two functions take equal values
every point, then the functions themselves are equal: `(∀ x, f x = g x) → f = g`.
It is called "extensionality" because it talks about how to prove two objects are equal
based on the properties of the object (compare with set extensionality,
which is `(∀ x, x ∈ s ↔ x ∈ t) → s = t`).
**Function extensionality.** If two functions return equal results for all possible arguments, then
they are equal.
This is often an axiom in dependent type theory systems, because it cannot be proved
from the core logic alone. However in lean's type theory this follows from the existence
of quotient types (note the `Quot.sound` in the proof, as well as the `show` line
which makes use of the definitional equality `Quot.lift f h (Quot.mk x) = f x`).
It is called “extensionality” because it provides a way to prove two objects equal based on the
properties of the underlying mathematical functions, rather than based on the syntax used to denote
them. Function extensionality is a theorem that can be [proved using quotient
types](lean-manual://section/quotient-funext).
-/
theorem funext {α : Sort u} {β : α Sort v} {f g : (x : α) β x}
(h : x, f x = g x) : f = g := by
@@ -2262,6 +2298,12 @@ So, you are mainly losing the capability of type checking your development using
-/
axiom ofReduceNat (a b : Nat) (h : reduceNat a = b) : a = b
/--
The term `opaqueId x` will not be reduced by the kernel.
-/
opaque opaqueId {α : Sort u} (x : α) : α := x
end Lean
@[simp] theorem ge_iff_le [LE α] {x y : α} : x y y x := Iff.rfl

View File

@@ -15,13 +15,12 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/--
`O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
but is defined only when all members of `l` satisfy `P`, using the proof
to apply `f`.
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over
an array `xs : Array α`, given a proof that every element of `xs` in fact satisfies `P`.
We replace this at runtime with a more efficient version via the `csimp` lemma `pmap_eq_pmapImpl`.
`Array.pmap`, named for “partial map,” is the equivalent of `Array.map` for such partial functions.
-/
def pmap {P : α Prop} (f : a, P a β) (xs : Array α) (H : a xs, P a) : Array β :=
(xs.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
@@ -32,14 +31,27 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
@[inline] private unsafe def attachWithImpl
(xs : Array α) (P : α Prop) (_ : x xs, P x) : Array {x // P x} := unsafeCast xs
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
with the same elements but in the type `{x // P x}`. -/
/--
“Attaches” individual proofs to an array of values that satisfy a predicate `P`, returning an array
of elements in the corresponding subtype `{ x // P x }`.
`O(1)`.
-/
@[implemented_by attachWithImpl] def attachWith
(xs : Array α) (P : α Prop) (H : x xs, P x) : Array {x // P x} :=
xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
with the same elements but in the type `{x // x ∈ xs}`. -/
/--
“Attaches” the proof that the elements of `xs` are in fact elements of `xs`, producing a new array with
the same elements but in the subtype `{ x // x ∈ xs }`.
`O(1)`.
This function is primarily used to allow definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
`Array.map`) to prove that an value taken from a list is smaller than the list. This allows the
well-founded recursion mechanism to prove that the function terminates.
-/
@[inline] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
@[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α Prop} {H : x l.toArray, P x} :
@@ -542,15 +554,25 @@ Further, we provide simp lemmas that push `unattach` inwards.
-/
/--
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
It is introduced as in intermediate step by lemmas such as `map_subtype`,
and is ideally subsequently simplified away by `unattach_attach`.
Maps an array of terms in a subtype to the corresponding terms in the type by forgetting that they
satisfy the predicate.
If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
This is the inverse of `Array.attachWith` and a synonym for `xs.map (·.val)`.
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
This function is usually inserted automatically by Lean as an intermediate step while proving
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
the elaboration of definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
state, the right approach is usually the tactic `simp [Array.unattach, -Array.map_subtype]`.
-/
def unattach {α : Type _} {p : α Prop} (xs : Array { x // p x }) : Array α := xs.map (·.val)
@[simp] theorem unattach_nil {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
@[simp] theorem unattach_nil {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := by
simp [unattach]
@[simp] theorem unattach_push {p : α Prop} {a : { x // p x }} {xs : Array { x // p x }} :
(xs.push a).unattach = xs.unattach.push a.1 := by
simp only [unattach, Array.map_push]
@@ -721,10 +743,13 @@ and simplifies these to the function directly taking the value.
List.map_toArray, List.map_flatten, map_subtype, map_id_fun', List.unattach_toArray, mk.injEq]
simp only [List.unattach]
@[simp] theorem unattach_mkArray {p : α Prop} {n : Nat} {x : { x // p x }} :
(Array.mkArray n x).unattach = Array.mkArray n x.1 := by
@[simp] theorem unattach_replicate {p : α Prop} {n : Nat} {x : { x // p x }} :
(Array.replicate n x).unattach = Array.replicate n x.1 := by
simp [unattach]
@[deprecated unattach_replicate (since := "2025-03-18")]
abbrev unattach_mkArray := @unattach_replicate
/-! ### Well-founded recursion preprocessing setup -/
@[wf_preprocess] theorem Array.map_wfParam (xs : Array α) (f : α β) :

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,11 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by
simp
/--
Applies the monadic action `f` to every element in the array, left-to-right, and returns the array
of results. Furthermore, the resulting array's type guarantees that it contains the same number of
elements as the input array.
-/
def Array.mapM' [Monad m] (f : α m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
go 0 mkEmpty as.size, rfl (by simp)
where
@@ -66,11 +71,19 @@ where
return as
/--
Monomorphic `Array.mapM`. The internal implementation uses pointer equality, and does not allocate a new array
if the result of each `f a` is a pointer equal value `a`.
Applies a monadic function to each element of an array, returning the array of results. The function is
monomorphic: it is required to return a value of the same type. The internal implementation uses
pointer equality, and does not allocate a new array if the result of each function call is
pointer-equal to its argument.
-/
@[implemented_by mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α m α) : m (Array α) :=
as.mapM f
/--
Applies a function to each element of an array, returning the array of results. The function is
monomorphic: it is required to return a value of the same type. The internal implementation uses
pointer equality, and does not allocate a new array if the result of each function call is
pointer-equal to its argument.
-/
@[inline] def Array.mapMono (as : Array α) (f : α α) : Array α :=
Id.run <| as.mapMonoM f

View File

@@ -29,6 +29,16 @@ namespace Array
else found (some a)
termination_by lo hi => hi.1 - lo.1
/--
Binary search for an element equivalent to `k` in the sorted array `as`. Returns the element from
the array, if it is found, or `none` otherwise.
The array `as` must be sorted according to the comparison operator `lt`, which should be a total
order.
The optional parameters `lo` and `hi` determine the region of the array indices to be searched. Both
are inclusive, and default to searching the entire array.
-/
@[inline] def binSearch {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Option α :=
if h : lo < as.size then
let hi := if hi < as.size then hi else as.size - 1
@@ -39,6 +49,16 @@ termination_by lo hi => hi.1 - lo.1
else
none
/--
Binary search for an element equivalent to `k` in the sorted array `as`. Returns `true` if the
element is found, or `false` otherwise.
The array `as` must be sorted according to the comparison operator `lt`, which should be a total
order.
The optional parameters `lo` and `hi` determine the region of the array indices to be searched. Both
are inclusive, and default to searching the entire array.
-/
@[inline] def binSearchContains {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Bool :=
if h : lo < as.size then
let hi := if hi < as.size then hi else as.size - 1
@@ -68,6 +88,16 @@ termination_by lo hi => hi.1 - lo.1
as.modifyM mid <| fun v => merge v
termination_by lo hi => hi.1 - lo.1
/--
Inserts an element `k` into a sorted array `as` such that the resulting array is sorted.
The ordering predicate `lt` should be a total order on elements, and the array `as` should be sorted
with respect to `lt`.
If an element that `lt` equates to `k` is already present in `as`, then `merge` is applied to the
existing element to determine the value of that position in the resulting array. If no element equal
to `k` is present, then `add` is used to determine the value to be inserted.
-/
@[specialize] def binInsertM {α : Type u} {m : Type u Type v} [Monad m]
(lt : α α Bool)
(merge : α m α)
@@ -81,6 +111,21 @@ termination_by lo hi => hi.1 - lo.1
else if !lt k as[as.size - 1] then as.modifyM (as.size - 1) <| merge
else binInsertAux lt merge add as k 0, by omega as.size - 1, by omega (by simp) (by simpa using h')
/--
Inserts an element into a sorted array such that the resulting array is sorted. If the element is
already present in the array, it is not inserted.
The ordering predicate `lt` should be a total order on elements, and the array `as` should be sorted
with respect to `lt`.
`Array.binInsertM` is a more general operator that provides greater control over the handling of
duplicate elements in addition to running in a monad.
Examples:
* `#[0, 1, 3, 5].binInsert (· < ·) 2 = #[0, 1, 2, 3, 5]`
* `#[0, 1, 3, 5].binInsert (· < ·) 1 = #[0, 1, 3, 5]`
* `#[].binInsert (· < ·) 1 = #[1]`
-/
@[inline] def binInsert {α : Type u} (lt : α α Bool) (as : Array α) (k : α) : Array α :=
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k

View File

@@ -88,10 +88,13 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
rcases xs with xs
simp
theorem countP_mkArray (p : α Bool) (a : α) (n : Nat) :
countP p (mkArray n a) = if p a then n else 0 := by
theorem countP_replicate (p : α Bool) (a : α) (n : Nat) :
countP p (replicate n a) = if p a then n else 0 := by
simp [ List.toArray_replicate, List.countP_replicate]
@[deprecated countP_replicate (since := "2025-03-18")]
abbrev countP_mkArray := @countP_replicate
theorem boole_getElem_le_countP (p : α Bool) (xs : Array α) (i : Nat) (h : i < xs.size) :
(if p xs[i] then 1 else 0) xs.countP p := by
rcases xs with xs
@@ -241,25 +244,34 @@ theorem count_eq_size {xs : Array α} : count a xs = xs.size ↔ ∀ b ∈ xs, a
· simpa using h b hb
· rw [h b hb, beq_self_eq_true]
@[simp] theorem count_mkArray_self (a : α) (n : Nat) : count a (mkArray n a) = n := by
@[simp] theorem count_replicate_self (a : α) (n : Nat) : count a (replicate n a) = n := by
simp [ List.toArray_replicate]
theorem count_mkArray (a b : α) (n : Nat) : count a (mkArray n b) = if b == a then n else 0 := by
@[deprecated count_replicate_self (since := "2025-03-18")]
abbrev count_mkArray_self := @count_replicate_self
theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b == a then n else 0 := by
simp [ List.toArray_replicate, List.count_replicate]
theorem filter_beq (xs : Array α) (a : α) : xs.filter (· == a) = mkArray (count a xs) a := by
@[deprecated count_replicate (since := "2025-03-18")]
abbrev count_mkArray := @count_replicate
theorem filter_beq (xs : Array α) (a : α) : xs.filter (· == a) = replicate (count a xs) a := by
rcases xs with xs
simp [List.filter_beq]
theorem filter_eq {α} [DecidableEq α] (xs : Array α) (a : α) : xs.filter (· = a) = mkArray (count a xs) a :=
theorem filter_eq {α} [DecidableEq α] (xs : Array α) (a : α) : xs.filter (· = a) = replicate (count a xs) a :=
filter_beq xs a
theorem mkArray_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
mkArray (count a xs) a = xs := by
theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs.size) :
replicate (count a xs) a = xs := by
rcases xs with xs
rw [ toList_inj]
simp [List.replicate_count_eq_of_count_eq_length (by simpa using h)]
@[deprecated replicate_count_eq_of_count_eq_size (since := "2025-03-18")]
abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
@[simp] theorem count_filter {xs : Array α} (h : p a) : count a (filter p xs) = count a xs := by
rcases xs with xs
simp [List.count_filter, h]

View File

@@ -21,7 +21,7 @@ open Nat
/-! ### eraseP -/
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := rfl
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := by simp
theorem eraseP_of_forall_mem_not {xs : Array α} (h : a, a xs ¬p a) : xs.eraseP p = xs := by
rcases xs with xs
@@ -122,21 +122,30 @@ theorem eraseP_append {xs : Array α} {ys : Array α} :
simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray]
split <;> simp
theorem eraseP_mkArray (n : Nat) (a : α) (p : α Bool) :
(mkArray n a).eraseP p = if p a then mkArray (n - 1) a else mkArray n a := by
theorem eraseP_replicate (n : Nat) (a : α) (p : α Bool) :
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
simp only [ List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
split <;> simp
@[simp] theorem eraseP_mkArray_of_pos {n : Nat} {a : α} (h : p a) :
(mkArray n a).eraseP p = mkArray (n - 1) a := by
@[deprecated eraseP_replicate (since := "2025-03-18")]
abbrev eraseP_mkArray := @eraseP_replicate
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
(replicate n a).eraseP p = replicate (n - 1) a := by
simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h]
@[simp] theorem eraseP_mkArray_of_neg {n : Nat} {a : α} (h : ¬p a) :
(mkArray n a).eraseP p = mkArray n a := by
@[deprecated eraseP_replicate_of_pos (since := "2025-03-18")]
abbrev eraseP_mkArray_of_pos := @eraseP_replicate_of_pos
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
(replicate n a).eraseP p = replicate n a := by
simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h]
@[deprecated eraseP_replicate_of_neg (since := "2025-03-18")]
abbrev eraseP_mkArray_of_neg := @eraseP_replicate_of_neg
theorem eraseP_eq_iff {p} {xs : Array α} :
xs.eraseP p = ys
(( a xs, ¬ p a) xs = ys)
@@ -243,12 +252,15 @@ theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
split <;> simp
theorem erase_mkArray [LawfulBEq α] (n : Nat) (a b : α) :
(mkArray n a).erase b = if b == a then mkArray (n - 1) a else mkArray n a := by
theorem erase_replicate [LawfulBEq α] (n : Nat) (a b : α) :
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
simp only [ List.toArray_replicate, List.erase_toArray]
simp only [List.erase_replicate, beq_iff_eq, List.toArray_replicate]
split <;> simp
@[deprecated erase_replicate (since := "2025-03-18")]
abbrev erase_mkArray := @erase_replicate
theorem erase_comm [LawfulBEq α] (a b : α) (xs : Array α) :
(xs.erase a).erase b = (xs.erase b).erase a := by
rcases xs with xs
@@ -268,16 +280,22 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
· left; simp_all
· right; refine a, as, h, rfl, bs, by simp
@[simp] theorem erase_mkArray_self [LawfulBEq α] {a : α} :
(mkArray n a).erase a = mkArray (n - 1) a := by
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
(replicate n a).erase a = replicate (n - 1) a := by
simp only [ List.toArray_replicate, List.erase_toArray]
simp [List.erase_replicate]
@[simp] theorem erase_mkArray_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(mkArray n a).erase b = mkArray n a := by
@[deprecated erase_replicate_self (since := "2025-03-18")]
abbrev erase_mkArray_self := @erase_replicate_self
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(replicate n a).erase b = replicate n a := by
rw [erase_of_not_mem]
simp_all
@[deprecated erase_replicate_ne (since := "2025-03-18")]
abbrev erase_mkArray_ne := @erase_replicate_ne
end erase
/-! ### eraseIdx -/
@@ -353,12 +371,15 @@ theorem eraseIdx_append_of_length_le {xs : Array α} {k : Nat} (hk : xs.size ≤
simp at hk
simp [List.eraseIdx_append_of_length_le, *]
theorem eraseIdx_mkArray {n : Nat} {a : α} {k : Nat} {h} :
(mkArray n a).eraseIdx k = mkArray (n - 1) a := by
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by
simp at h
simp only [ List.toArray_replicate, List.eraseIdx_toArray]
simp [List.eraseIdx_replicate, h]
@[deprecated eraseIdx_replicate (since := "2025-03-18")]
abbrev eraseIdx_mkArray := @eraseIdx_replicate
theorem mem_eraseIdx_iff_getElem {x : α} {xs : Array α} {k} {h} : x xs.eraseIdx k h i w, i k xs[i]'w = x := by
rcases xs with xs
simp [List.mem_eraseIdx_iff_getElem, *]

View File

@@ -249,12 +249,15 @@ theorem extract_append_left {as bs : Array α} :
· 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
@[simp] theorem extract_replicate {a : α} {n i j : Nat} :
(replicate n a).extract i j = replicate (min j n - i) a := by
ext l h₁ h₂
· simp
· simp only [size_extract, size_mkArray] at h₁ h₂
simp only [getElem_extract, getElem_mkArray]
· simp only [size_extract, size_replicate] at h₁ h₂
simp only [getElem_extract, getElem_replicate]
@[deprecated extract_replicate (since := "2025-03-18")]
abbrev extract_mkArray := @extract_replicate
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} :
as.extract i j = as.extract i j' min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
@@ -387,24 +390,36 @@ theorem popWhile_append {xs ys : Array α} :
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] theorem takeWhile_replicate_eq_filter (p : α Bool) :
(replicate n a).takeWhile p = (replicate 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]
@[deprecated takeWhile_replicate_eq_filter (since := "2025-03-18")]
abbrev takeWhile_mkArray_eq_filter := @takeWhile_replicate_eq_filter
@[simp] theorem popWhile_mkArray_eq_filter_not (p : α Bool) :
(mkArray n a).popWhile p = (mkArray n a).filter (fun a => !p a) := by
theorem takeWhile_replicate (p : α Bool) :
(replicate n a).takeWhile p = if p a then replicate n a else #[] := by
simp [takeWhile_replicate_eq_filter, filter_replicate]
@[deprecated takeWhile_replicate (since := "2025-03-18")]
abbrev takeWhile_mkArray := @takeWhile_replicate
@[simp] theorem popWhile_replicate_eq_filter_not (p : α Bool) :
(replicate n a).popWhile p = (replicate n a).filter (fun a => !p a) := by
simp [ List.toArray_replicate, List.filter_reverse]
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,
@[deprecated popWhile_replicate_eq_filter_not (since := "2025-03-18")]
abbrev popWhile_mkArray_eq_filter_not := @popWhile_replicate_eq_filter_not
theorem popWhile_replicate (p : α Bool) :
(replicate n a).popWhile p = if p a then #[] else replicate n a := by
simp only [popWhile_replicate_eq_filter_not, size_replicate, filter_replicate, Bool.not_eq_eq_eq_not,
Bool.not_true]
split <;> simp_all
@[deprecated popWhile_replicate (since := "2025-03-18")]
abbrev popWhile_mkArray := @popWhile_replicate
theorem extract_takeWhile {as : Array α} {i : Nat} :
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
rcases as with as

View File

@@ -12,7 +12,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/-- `finRange n` is the array of all elements of `Fin n` in order. -/
/--
Returns an array of all elements of `Fin n` in order, starting at `0`.
Examples:
* `Array.finRange 0 = (#[] : Array (Fin 0))`
* `Array.finRange 2 = (#[0, 1] : Array (Fin 2))`
-/
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
@[simp] theorem size_finRange (n) : (Array.finRange n).size = n := by

View File

@@ -99,21 +99,33 @@ theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
simp [getElem?_eq_getElem, h] at t
simp [ t]
theorem findSome?_mkArray : findSome? f (mkArray n a) = if n = 0 then none else f a := by
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [ List.toArray_replicate, List.findSome?_replicate]
@[simp] theorem findSome?_mkArray_of_pos (h : 0 < n) : findSome? f (mkArray n a) = f a := by
simp [findSome?_mkArray, Nat.ne_of_gt h]
@[deprecated findSome?_replicate (since := "2025-03-18")]
abbrev findSome?_mkArray := @findSome?_replicate
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
simp [findSome?_replicate, Nat.ne_of_gt h]
@[deprecated findSome?_replicate_of_pos (since := "2025-03-18")]
abbrev findSome?_mkArray_of_pos := @findSome?_replicate_of_pos
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_mkArray_of_isSome (_ : (f a).isSome) :
findSome? f (mkArray n a) = if n = 0 then none else f a := by
simp [findSome?_mkArray]
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) :
findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate]
@[simp] theorem findSome?_mkArray_of_isNone (h : (f a).isNone) :
findSome? f (mkArray n a) = none := by
@[deprecated findSome?_replicate_of_isSome (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isSome := @findSome?_replicate_of_isSome
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) :
findSome? f (replicate n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_mkArray, h]
simp [findSome?_replicate, h]
@[deprecated findSome?_replicate_of_isNone (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@@ -254,40 +266,58 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
theorem find?_mkArray :
find? p (mkArray n a) = if n = 0 then none else if p a then some a else none := by
theorem find?_replicate :
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
simp [ List.toArray_replicate, List.find?_replicate]
@[simp] theorem find?_mkArray_of_length_pos (h : 0 < n) :
find? p (mkArray n a) = if p a then some a else none := by
simp [find?_mkArray, Nat.ne_of_gt h]
@[deprecated find?_replicate (since := "2025-03-18")]
abbrev find?_mkArray := @find?_replicate
@[simp] theorem find?_mkArray_of_pos (h : p a) :
find? p (mkArray n a) = if n = 0 then none else some a := by
simp [find?_mkArray, h]
@[simp] theorem find?_replicate_of_size_pos (h : 0 < n) :
find? p (replicate n a) = if p a then some a else none := by
simp [find?_replicate, Nat.ne_of_gt h]
@[simp] theorem find?_mkArray_of_neg (h : ¬ p a) : find? p (mkArray n a) = none := by
simp [find?_mkArray, h]
@[deprecated find?_replicate_of_size_pos (since := "2025-03-18")]
abbrev find?_mkArray_of_length_pos := @find?_replicate_of_size_pos
@[simp] theorem find?_replicate_of_pos (h : p a) :
find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[deprecated find?_replicate_of_pos (since := "2025-03-18")]
abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
@[deprecated find?_replicate_of_neg (since := "2025-03-18")]
abbrev find?_mkArray_of_neg := @find?_replicate_of_neg
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
theorem find?_mkArray_eq_none_iff {n : Nat} {a : α} {p : α Bool} :
(mkArray n a).find? p = none n = 0 !p a := by
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α Bool} :
(replicate n a).find? p = none n = 0 !p a := by
simp [ List.toArray_replicate, List.find?_replicate_eq_none_iff, Classical.or_iff_not_imp_left]
@[deprecated find?_mkArray_eq_none_iff (since := "2025-02-03")]
abbrev find?_mkArray_eq_none := @find?_mkArray_eq_none_iff
@[deprecated find?_replicate_eq_none_iff (since := "2025-03-18")]
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
@[simp] theorem find?_mkArray_eq_some_iff {n : Nat} {a b : α} {p : α Bool} :
(mkArray n a).find? p = some b n 0 p a a = b := by
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α Bool} :
(replicate n a).find? p = some b n 0 p a a = b := by
simp [ List.toArray_replicate]
@[deprecated find?_mkArray_eq_some_iff (since := "2025-02-03")]
abbrev find?_mkArray_eq_some := @find?_mkArray_eq_some_iff
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")]
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff
@[simp] theorem get_find?_mkArray (n : Nat) (a : α) (p : α Bool) (h) :
((mkArray n a).find? p).get h = a := by
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
@[simp] theorem get_find?_replicate (n : Nat) (a : α) (p : α Bool) (h) :
((replicate n a).find? p).get h = a := by
simp [ List.toArray_replicate]
@[deprecated get_find?_replicate (since := "2025-03-18")]
abbrev get_find?_mkArray := @get_find?_replicate
theorem find?_pmap {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs P a) (p : β Bool) :
(xs.pmap f H).find? p = (xs.attach.find? (fun a, m => p (f a (H a m)))).map fun a, m => f a (H a m) := by
@@ -408,7 +438,7 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := rfl
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
@[simp]
theorem findIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
@@ -481,12 +511,15 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
cases xss using array₂_induction
simp [List.findIdx?_flatten, Function.comp_def]
@[simp] theorem findIdx?_mkArray :
(mkArray n a).findIdx? p = if 0 < n p a then some 0 else none := by
@[simp] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by
rw [ List.toArray_replicate]
simp only [List.findIdx?_toArray]
simp
@[deprecated findIdx?_replicate (since := "2025-03-18")]
abbrev findIdx?_mkArray := @findIdx?_replicate
theorem findIdx?_eq_findSome?_zipIdx {xs : Array α} {p : α Bool} :
xs.findIdx? p = xs.zipIdx.findSome? fun a, i => if p a then some i else none := by
rcases xs with xs
@@ -526,7 +559,7 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := rfl
@[simp] theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
theorem findFinIdx?_congr {p : α Bool} {xs ys : Array α} (w : xs = ys) :
@@ -595,7 +628,7 @@ The verification API for `idxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
-/
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := rfl
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf? a = none a xs := by
@@ -612,7 +645,7 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := rfl
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.finIdxOf? a = none a xs := by

View File

@@ -9,6 +9,12 @@ import Init.Data.Array.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.
/--
Sorts an array using insertion sort.
The optional parameter `lt` specifies an ordering predicate. It defaults to `LT.lt`, which must be
decidable to be used for sorting.
-/
@[inline] def Array.insertionSort (xs : Array α) (lt : α α Bool := by exact (· < ·)) : Array α :=
traverse xs 0 xs.size
where

View File

@@ -321,27 +321,45 @@ theorem eq_push_of_size_ne_zero {xs : Array α} (h : xs.size ≠ 0) :
theorem singleton_inj : #[a] = #[b] a = b := by
simp
/-! ### mkArray -/
/-! ### replicate -/
@[simp] theorem size_mkArray (n : Nat) (v : α) : (mkArray n v).size = n :=
@[simp] theorem size_replicate (n : Nat) (v : α) : (replicate n v).size = n :=
List.length_replicate ..
@[simp] theorem toList_mkArray : (mkArray n a).toList = List.replicate n a := by
simp only [mkArray]
@[deprecated size_replicate (since := "2025-03-18")]
abbrev size_mkArray := @size_replicate
@[simp] theorem mkArray_zero : mkArray 0 a = #[] := rfl
@[simp] theorem toList_replicate : (replicate n a).toList = List.replicate n a := by
simp only [replicate]
theorem mkArray_succ : mkArray (n + 1) a = (mkArray n a).push a := by
@[deprecated toList_replicate (since := "2025-03-18")]
abbrev toList_mkArray := @toList_replicate
@[simp] theorem replicate_zero : replicate 0 a = #[] := rfl
@[deprecated replicate_zero (since := "2025-03-18")]
abbrev mkArray_zero := @replicate_zero
theorem replicate_succ : replicate (n + 1) a = (replicate n a).push a := by
apply toList_inj.1
simp [List.replicate_succ']
@[simp] theorem getElem_mkArray (n : Nat) (v : α) (h : i < (mkArray n v).size) :
(mkArray n v)[i] = v := by simp [ getElem_toList]
@[deprecated replicate_succ (since := "2025-03-18")]
abbrev mkArray_succ := @replicate_succ
theorem getElem?_mkArray (n : Nat) (v : α) (i : Nat) :
(mkArray n v)[i]? = if i < n then some v else none := by
@[simp] theorem getElem_replicate (n : Nat) (v : α) (h : i < (replicate n v).size) :
(replicate n v)[i] = v := by simp [ getElem_toList]
@[deprecated getElem_replicate (since := "2025-03-18")]
abbrev getElem_mkArray := @getElem_replicate
@[simp] theorem getElem?_replicate (n : Nat) (v : α) (i : Nat) :
(replicate n v)[i]? = if i < n then some v else none := by
simp [getElem?_def]
@[deprecated getElem?_replicate (since := "2025-03-18")]
abbrev getElem?_mkArray := @getElem?_replicate
/-! ### mem -/
theorem not_mem_empty (a : α) : ¬ a #[] := by simp
@@ -554,7 +572,7 @@ theorem anyM_loop_cons [Monad m] (p : α → m Bool) (a : α) (as : List α) (st
@[simp] theorem anyM_toList [Monad m] (p : α m Bool) (as : Array α) :
as.toList.anyM p = as.anyM p :=
match as with
| [] => rfl
| [] => by simp [anyM, anyM.loop]
| a :: as => by
simp only [List.anyM, anyM, List.size_toArray, List.length_cons, Nat.le_refl, reduceDIte]
rw [anyM.loop, dif_pos (by omega)]
@@ -1037,15 +1055,18 @@ theorem size_eq_of_beq [BEq α] {xs ys : Array α} (h : xs == ys) : xs.size = ys
cases ys
simp [List.length_eq_of_beq (by simpa using h)]
@[simp] theorem mkArray_beq_mkArray [BEq α] {a b : α} {n : Nat} :
(mkArray n a == mkArray n b) = (n == 0 || a == b) := by
@[simp] theorem replicate_beq_replicate [BEq α] {a b : α} {n : Nat} :
(replicate n a == replicate n b) = (n == 0 || a == b) := by
cases n with
| zero => simp
| succ n =>
rw [mkArray_succ, mkArray_succ, push_beq_push, mkArray_beq_mkArray]
rw [replicate_succ, replicate_succ, push_beq_push, replicate_beq_replicate]
rw [Bool.eq_iff_iff]
simp +contextual
@[deprecated replicate_beq_replicate (since := "2025-03-18")]
abbrev mkArray_beq_mkArray := @replicate_beq_replicate
private theorem beq_of_beq_singleton [BEq α] {a b : α} : #[a] == #[b] a == b := by
intro h
have : isEqv #[a] #[b] BEq.beq = true := h
@@ -1178,7 +1199,7 @@ theorem map_id' (xs : Array α) : map (fun (a : α) => a) xs = xs := map_id xs
theorem map_id'' {f : α α} (h : x, f x = x) (xs : Array α) : map f xs = xs := by
simp [show f = id from funext h]
theorem map_singleton (f : α β) (a : α) : map f #[a] = #[f a] := rfl
theorem map_singleton (f : α β) (a : α) : map f #[a] = #[f a] := by simp
-- We use a lower priority here as there are more specific lemmas in downstream libraries
-- which should be able to fire first.
@@ -2306,147 +2327,234 @@ theorem flatMap_eq_foldl (f : α → Array β) (xs : Array α) :
rw [List.foldl_cons, ih]
simp [toArray_append]
/-! ### mkArray -/
/-! ### replicate -/
@[simp] theorem mkArray_one : mkArray 1 a = #[a] := rfl
@[simp] theorem replicate_one : replicate 1 a = #[a] := rfl
/-- Variant of `mkArray_succ` that prepends `a` at the beginning of the array. -/
theorem mkArray_succ' : mkArray (n + 1) a = #[a] ++ mkArray n a := by
@[deprecated replicate_one (since := "2025-03-18")]
abbrev mkArray_one := @replicate_one
/-- Variant of `replicate_succ` that prepends `a` at the beginning of the array. -/
theorem replicate_succ' : replicate (n + 1) a = #[a] ++ replicate n a := by
apply Array.ext'
simp [List.replicate_succ]
@[simp] theorem mem_mkArray {a b : α} {n} : b mkArray n a n 0 b = a := by
unfold mkArray
@[deprecated replicate_succ' (since := "2025-03-18")]
abbrev mkArray_succ' := @replicate_succ'
@[simp] theorem mem_replicate {a b : α} {n} : b replicate n a n 0 b = a := by
unfold replicate
simp only [mem_toArray, List.mem_replicate]
theorem eq_of_mem_mkArray {a b : α} {n} (h : b mkArray n a) : b = a := (mem_mkArray.1 h).2
@[deprecated mem_replicate (since := "2025-03-18")]
abbrev mem_mkArray := @mem_replicate
theorem forall_mem_mkArray {p : α Prop} {a : α} {n} :
( b, b mkArray n a p b) n = 0 p a := by
cases n <;> simp [mem_mkArray]
theorem eq_of_mem_replicate {a b : α} {n} (h : b replicate n a) : b = a := (mem_replicate.1 h).2
@[simp] theorem mkArray_succ_ne_empty (n : Nat) (a : α) : mkArray (n+1) a #[] := by
simp [mkArray_succ]
@[deprecated eq_of_mem_mkArray (since := "2025-03-18")]
abbrev eq_of_mem_mkArray := @eq_of_mem_replicate
@[simp] theorem mkArray_eq_empty_iff {n : Nat} (a : α) : mkArray n a = #[] n = 0 := by
theorem forall_mem_replicate {p : α Prop} {a : α} {n} :
( b, b replicate n a p b) n = 0 p a := by
cases n <;> simp [mem_replicate]
@[deprecated forall_mem_replicate (since := "2025-03-18")]
abbrev forall_mem_mkArray := @forall_mem_replicate
@[simp] theorem replicate_succ_ne_empty (n : Nat) (a : α) : replicate (n+1) a #[] := by
simp [replicate_succ]
@[deprecated replicate_succ_ne_empty (since := "2025-03-18")]
abbrev mkArray_succ_ne_empty := @replicate_succ_ne_empty
@[simp] theorem replicate_eq_empty_iff {n : Nat} (a : α) : replicate n a = #[] n = 0 := by
cases n <;> simp
@[simp] theorem getElem?_mkArray_of_lt {n : Nat} {i : Nat} (h : i < n) : (mkArray n a)[i]? = some a := by
simp [getElem?_mkArray, h]
@[deprecated replicate_eq_empty_iff (since := "2025-03-18")]
abbrev mkArray_eq_empty_iff := @replicate_eq_empty_iff
@[simp] theorem mkArray_inj : mkArray n a = mkArray m b n = m (n = 0 a = b) := by
@[simp] theorem replicate_inj : replicate n a = replicate m b n = m (n = 0 a = b) := by
rw [ toList_inj]
simp
theorem eq_mkArray_of_mem {a : α} {xs : Array α} (h : (b) (_ : b xs), b = a) : xs = mkArray xs.size a := by
@[deprecated replicate_inj (since := "2025-03-18")]
abbrev mkArray_inj := @replicate_inj
theorem eq_replicate_of_mem {a : α} {xs : Array α} (h : (b) (_ : b xs), b = a) : xs = replicate xs.size a := by
rw [ toList_inj]
simpa using List.eq_replicate_of_mem (by simpa using h)
theorem eq_mkArray_iff {a : α} {n} {xs : Array α} :
xs = mkArray n a xs.size = n (b) (_ : b xs), b = a := by
@[deprecated eq_replicate_of_mem (since := "2025-03-18")]
abbrev eq_mkArray_of_mem := @eq_replicate_of_mem
theorem eq_replicate_iff {a : α} {n} {xs : Array α} :
xs = replicate n a xs.size = n (b) (_ : b xs), b = a := by
rw [ toList_inj]
simpa using List.eq_replicate_iff (l := xs.toList)
theorem map_eq_mkArray_iff {xs : Array α} {f : α β} {b : β} :
xs.map f = mkArray xs.size b x xs, f x = b := by
simp [eq_mkArray_iff]
@[deprecated eq_replicate_iff (since := "2025-03-18")]
abbrev eq_mkArray_iff := @eq_replicate_iff
@[simp] theorem map_const (xs : Array α) (b : β) : map (Function.const α b) xs = mkArray xs.size b :=
map_eq_mkArray_iff.mpr fun _ _ => rfl
theorem map_eq_replicate_iff {xs : Array α} {f : α β} {b : β} :
xs.map f = replicate xs.size b x xs, f x = b := by
simp [eq_replicate_iff]
@[simp] theorem map_const_fun (x : β) : map (Function.const α x) = (mkArray ·.size x) := by
@[deprecated map_eq_replicate_iff (since := "2025-03-18")]
abbrev map_eq_mkArray_iff := @map_eq_replicate_iff
@[simp] theorem map_const (xs : Array α) (b : β) : map (Function.const α b) xs = replicate xs.size b :=
map_eq_replicate_iff.mpr fun _ _ => rfl
@[simp] theorem map_const_fun (x : β) : map (Function.const α x) = (replicate ·.size x) := by
funext xs
simp
/-- Variant of `map_const` using a lambda rather than `Function.const`. -/
-- This can not be a `@[simp]` lemma because it would fire on every `List.map`.
theorem map_const' (xs : Array α) (b : β) : map (fun _ => b) xs = mkArray xs.size b :=
theorem map_const' (xs : Array α) (b : β) : map (fun _ => b) xs = replicate xs.size b :=
map_const xs b
@[simp] theorem set_mkArray_self : (mkArray n a).set i a h = mkArray n a := by
@[simp] theorem set_replicate_self : (replicate n a).set i a h = replicate n a := by
apply Array.ext'
simp
@[simp] theorem setIfInBounds_mkArray_self : (mkArray n a).setIfInBounds i a = mkArray n a := by
@[deprecated set_replicate_self (since := "2025-03-18")]
abbrev set_mkArray_self := @set_replicate_self
@[simp] theorem setIfInBounds_replicate_self : (replicate n a).setIfInBounds i a = replicate n a := by
apply Array.ext'
simp
@[simp] theorem mkArray_append_mkArray : mkArray n a ++ mkArray m a = mkArray (n + m) a := by
@[deprecated setIfInBounds_replicate_self (since := "2025-03-18")]
abbrev setIfInBounds_mkArray_self := @setIfInBounds_replicate_self
@[simp] theorem replicate_append_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
apply Array.ext'
simp
theorem append_eq_mkArray_iff {xs ys : Array α} {a : α} :
xs ++ ys = mkArray n a
xs.size + ys.size = n xs = mkArray xs.size a ys = mkArray ys.size a := by
@[deprecated replicate_append_replicate (since := "2025-03-18")]
abbrev mkArray_append_mkArray := @replicate_append_replicate
theorem append_eq_replicate_iff {xs ys : Array α} {a : α} :
xs ++ ys = replicate n a
xs.size + ys.size = n xs = replicate xs.size a ys = replicate ys.size a := by
simp [ toList_inj, List.append_eq_replicate_iff]
theorem mkArray_eq_append_iff {xs ys : Array α} {a : α} :
mkArray n a = xs ++ ys
xs.size + ys.size = n xs = mkArray xs.size a ys = mkArray ys.size a := by
rw [eq_comm, append_eq_mkArray_iff]
@[deprecated append_eq_replicate_iff (since := "2025-03-18")]
abbrev append_eq_mkArray_iff := @append_eq_replicate_iff
@[simp] theorem map_mkArray : (mkArray n a).map f = mkArray n (f a) := by
theorem replicate_eq_append_iff {xs ys : Array α} {a : α} :
replicate n a = xs ++ ys
xs.size + ys.size = n xs = replicate xs.size a ys = replicate ys.size a := by
rw [eq_comm, append_eq_replicate_iff]
@[deprecated replicate_eq_append_iff (since := "2025-03-18")]
abbrev replicate_eq_mkArray_iff := @replicate_eq_append_iff
@[simp] theorem map_replicate : (replicate n a).map f = replicate n (f a) := by
apply Array.ext'
simp
theorem filter_mkArray (w : stop = n) :
(mkArray n a).filter p 0 stop = if p a then mkArray n a else #[] := by
@[deprecated map_replicate (since := "2025-03-18")]
abbrev map_mkArray := @map_replicate
theorem filter_replicate (w : stop = n) :
(replicate n a).filter p 0 stop = if p a then replicate n a else #[] := by
apply Array.ext'
simp only [w, toList_filter', toList_mkArray, List.filter_replicate]
simp only [w, toList_filter', toList_replicate, List.filter_replicate]
split <;> simp_all
@[simp] theorem filter_mkArray_of_pos (w : stop = n) (h : p a) :
(mkArray n a).filter p 0 stop = mkArray n a := by
simp [filter_mkArray, h, w]
@[deprecated filter_replicate (since := "2025-03-18")]
abbrev filter_mkArray := @filter_replicate
@[simp] theorem filter_mkArray_of_neg (w : stop = n) (h : ¬ p a) :
(mkArray n a).filter p 0 stop = #[] := by
simp [filter_mkArray, h, w]
@[simp] theorem filter_replicate_of_pos (w : stop = n) (h : p a) :
(replicate n a).filter p 0 stop = replicate n a := by
simp [filter_replicate, h, w]
theorem filterMap_mkArray {f : α Option β} (w : stop = n := by simp) :
(mkArray n a).filterMap f 0 stop = match f a with | none => #[] | .some b => mkArray n b := by
@[deprecated filter_replicate_of_pos (since := "2025-03-18")]
abbrev filter_mkArray_of_pos := @filter_replicate_of_pos
@[simp] theorem filter_replicate_of_neg (w : stop = n) (h : ¬ p a) :
(replicate n a).filter p 0 stop = #[] := by
simp [filter_replicate, h, w]
@[deprecated filter_replicate_of_neg (since := "2025-03-18")]
abbrev filter_mkArray_of_neg := @filter_replicate_of_neg
theorem filterMap_replicate {f : α Option β} (w : stop = n := by simp) :
(replicate n a).filterMap f 0 stop = match f a with | none => #[] | .some b => replicate n b := by
apply Array.ext'
simp only [w, size_mkArray, toList_filterMap', toList_mkArray, List.filterMap_replicate]
simp only [w, size_replicate, toList_filterMap', toList_replicate, List.filterMap_replicate]
split <;> simp_all
@[deprecated filterMap_replicate (since := "2025-03-18")]
abbrev filterMap_mkArray := @filterMap_replicate
-- This is not a useful `simp` lemma because `b` is unknown.
theorem filterMap_mkArray_of_some {f : α Option β} (h : f a = some b) :
(mkArray n a).filterMap f = mkArray n b := by
simp [filterMap_mkArray, h]
theorem filterMap_replicate_of_some {f : α Option β} (h : f a = some b) :
(replicate n a).filterMap f = replicate n b := by
simp [filterMap_replicate, h]
@[simp] theorem filterMap_mkArray_of_isSome {f : α Option β} (h : (f a).isSome) :
(mkArray n a).filterMap f = mkArray n (Option.get _ h) := by
@[deprecated filterMap_replicate_of_some (since := "2025-03-18")]
abbrev filterMap_mkArray_of_some := @filterMap_replicate_of_some
@[simp] theorem filterMap_replicate_of_isSome {f : α Option β} (h : (f a).isSome) :
(replicate n a).filterMap f = replicate n (Option.get _ h) := by
match w : f a, h with
| some b, _ => simp [filterMap_mkArray, h, w]
| some b, _ => simp [filterMap_replicate, h, w]
@[simp] theorem filterMap_mkArray_of_none {f : α Option β} (h : f a = none) :
(mkArray n a).filterMap f = #[] := by
simp [filterMap_mkArray, h]
@[deprecated filterMap_replicate_of_isSome (since := "2025-03-18")]
abbrev filterMap_mkArray_of_isSome := @filterMap_replicate_of_isSome
@[simp] theorem flatten_mkArray_empty : (mkArray n (#[] : Array α)).flatten = #[] := by
@[simp] theorem filterMap_replicate_of_none {f : α Option β} (h : f a = none) :
(replicate n a).filterMap f = #[] := by
simp [filterMap_replicate, h]
@[deprecated filterMap_replicate_of_none (since := "2025-03-18")]
abbrev filterMap_mkArray_of_none := @filterMap_replicate_of_none
@[simp] theorem flatten_replicate_empty : (replicate n (#[] : Array α)).flatten = #[] := by
rw [ toList_inj]
simp
@[simp] theorem flatten_mkArray_singleton : (mkArray n #[a]).flatten = mkArray n a := by
@[deprecated flatten_replicate_empty (since := "2025-03-18")]
abbrev flatten_mkArray_empty := @flatten_replicate_empty
@[simp] theorem flatten_replicate_singleton : (replicate n #[a]).flatten = replicate n a := by
rw [ toList_inj]
simp
@[simp] theorem flatten_mkArray_mkArray : (mkArray n (mkArray m a)).flatten = mkArray (n * m) a := by
@[deprecated flatten_replicate_singleton (since := "2025-03-18")]
abbrev flatten_mkArray_singleton := @flatten_replicate_singleton
@[simp] theorem flatten_replicate_replicate : (replicate n (replicate m a)).flatten = replicate (n * m) a := by
rw [ toList_inj]
simp
theorem flatMap_mkArray {β} (f : α Array β) : (mkArray n a).flatMap f = (mkArray n (f a)).flatten := by
@[deprecated flatten_replicate_replicate (since := "2025-03-18")]
abbrev flatten_mkArray_replicate := @flatten_replicate_replicate
theorem flatMap_replicate {β} (f : α Array β) : (replicate n a).flatMap f = (replicate n (f a)).flatten := by
rw [ toList_inj]
simp [flatMap_toList, List.flatMap_replicate]
@[simp] theorem isEmpty_mkArray : (mkArray n a).isEmpty = decide (n = 0) := by
@[deprecated flatMap_replicate (since := "2025-03-18")]
abbrev flatMap_mkArray := @flatMap_replicate
@[simp] theorem isEmpty_replicate : (replicate n a).isEmpty = decide (n = 0) := by
rw [ List.toArray_replicate, List.isEmpty_toArray]
simp
@[simp] theorem sum_mkArray_nat (n : Nat) (a : Nat) : (mkArray n a).sum = n * a := by
@[deprecated isEmpty_replicate (since := "2025-03-18")]
abbrev isEmpty_mkArray := @isEmpty_replicate
@[simp] theorem sum_replicate_nat (n : Nat) (a : Nat) : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
@[deprecated sum_replicate_nat (since := "2025-03-18")]
abbrev sum_mkArray_nat := @sum_replicate_nat
/-! ### Preliminaries about `swap` needed for `reverse`. -/
theorem getElem?_swap (xs : Array α) (i j : Nat) (hi hj) (k : Nat) : (xs.swap i j hi hj)[k]? =
@@ -2625,10 +2733,13 @@ theorem flatMap_reverse {β} (xs : Array α) (f : α → Array β) :
cases xs
simp [List.flatMap_reverse, Function.comp_def]
@[simp] theorem reverse_mkArray (n) (a : α) : reverse (mkArray n a) = mkArray n a := by
@[simp] theorem reverse_replicate (n) (a : α) : reverse (replicate n a) = replicate n a := by
rw [ toList_inj]
simp
@[deprecated reverse_replicate (since := "2025-03-18")]
abbrev reverse_mkArray := @reverse_replicate
/-! ### extract -/
theorem extract_loop_zero (xs ys : Array α) (start : Nat) : extract.loop xs 0 start ys = ys := by
@@ -3464,14 +3575,20 @@ theorem back?_flatten {xss : Array (Array α)} :
(flatten xss).back? = xss.reverse.findSome? fun xs => xs.back? := by
simp [ flatMap_id, back?_flatMap]
theorem back?_mkArray (a : α) (n : Nat) :
(mkArray n a).back? = if n = 0 then none else some a := by
rw [mkArray_eq_toArray_replicate]
theorem back?_replicate (a : α) (n : Nat) :
(replicate n a).back? = if n = 0 then none else some a := by
rw [replicate_eq_toArray_replicate]
simp only [List.back?_toArray, List.getLast?_replicate]
@[simp] theorem back_mkArray (w : 0 < n) : (mkArray n a).back (by simpa using w) = a := by
@[deprecated back?_replicate (since := "2025-03-18")]
abbrev back?_mkArray := @back?_replicate
@[simp] theorem back_replicate (w : 0 < n) : (replicate n a).back (by simpa using w) = a := by
simp [back_eq_getElem]
@[deprecated back_replicate (since := "2025-03-18")]
abbrev back_mkArray := @back_replicate
/-! ## Additional operations -/
/-! ### leftpad -/
@@ -3508,9 +3625,12 @@ theorem pop_append {xs ys : Array α} :
(xs ++ ys).pop = if ys.isEmpty then xs.pop else xs ++ ys.pop := by
split <;> simp_all
@[simp] theorem pop_mkArray (n) (a : α) : (mkArray n a).pop = mkArray (n - 1) a := by
@[simp] theorem pop_replicate (n) (a : α) : (replicate n a).pop = replicate (n - 1) a := by
ext <;> simp
@[deprecated pop_replicate (since := "2025-03-18")]
abbrev pop_mkArray := @pop_replicate
/-! ### modify -/
@[simp] theorem size_modify (xs : Array α) (i : Nat) (f : α α) : (xs.modify i f).size = xs.size := by
@@ -3675,15 +3795,21 @@ theorem replace_extract {xs : Array α} {i : Nat} :
rcases xs with xs
simp [List.replace_take]
@[simp] theorem replace_mkArray_self {a : α} (h : 0 < n) :
(mkArray n a).replace a b = #[b] ++ mkArray (n - 1) a := by
cases n <;> simp_all [mkArray_succ', replace_append]
@[simp] theorem replace_replicate_self {a : α} (h : 0 < n) :
(replicate n a).replace a b = #[b] ++ replicate (n - 1) a := by
cases n <;> simp_all [replicate_succ', replace_append]
@[simp] theorem replace_mkArray_ne {a b c : α} (h : !b == a) :
(mkArray n a).replace b c = mkArray n a := by
@[deprecated replace_replicate_self (since := "2025-03-18")]
abbrev replace_mkArray_self := @replace_replicate_self
@[simp] theorem replace_replicate_ne {a b c : α} (h : !b == a) :
(replicate n a).replace b c = replicate n a := by
rw [replace_of_not_mem]
simp_all
@[deprecated replace_replicate_ne (since := "2025-03-18")]
abbrev replace_mkArray_ne := @replace_replicate_ne
end replace
/-! ## Logic -/
@@ -3911,17 +4037,31 @@ theorem any_reverse {xs : Array α} : xs.reverse.any f 0 = xs.any f := by
theorem all_reverse {xs : Array α} : xs.reverse.all f 0 = xs.all f := by
simp
@[simp] theorem any_mkArray {n : Nat} {a : α} :
(mkArray n a).any f = if n = 0 then false else f a := by
induction n <;> simp_all [mkArray_succ']
@[simp] theorem any_replicate {n : Nat} {a : α} :
(replicate n a).any f = if n = 0 then false else f a := by
induction n <;> simp_all [replicate_succ']
@[simp] theorem all_mkArray {n : Nat} {a : α} :
(mkArray n a).all f = if n = 0 then true else f a := by
induction n <;> simp_all +contextual [mkArray_succ']
@[deprecated any_replicate (since := "2025-03-18")]
abbrev any_mkArray := @any_replicate
@[simp] theorem all_replicate {n : Nat} {a : α} :
(replicate n a).all f = if n = 0 then true else f a := by
induction n <;> simp_all +contextual [replicate_succ']
@[deprecated all_replicate (since := "2025-03-18")]
abbrev all_mkArray := @all_replicate
/-! ### toListRev -/
/-- A more efficient version of `arr.toList.reverse`; for verification purposes we immediately simplify it. -/
/--
Converts an array to a list that contains the same elements in the opposite order.
This is equivalent to, but more efficient than, `Array.toList ∘ List.reverse`.
Examples:
* `#[1, 2, 3].toListRev = [3, 2, 1]`
* `#["blue", "yellow"].toListRev = ["yellow", "blue"]`
-/
@[inline] def toListRev (xs : Array α) : List α := xs.foldl (fun l t => t :: l) []
@[simp] theorem toListRev_eq (xs : Array α) : xs.toListRev = xs.toList.reverse := by

View File

@@ -14,11 +14,12 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/--
Lexicographic comparator for arrays.
Compares arrays lexicographically with respect to a comparison `lt` on their elements.
`lex as bs lt` is true if
- `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`, or
- there is an index `i` such that `lt as[i] bs[i]`, and for all `j < i`, `as[j] == bs[j]`.
Specifically, `Array.lex as bs lt` is true if
* `bs` is larger than `as` and `as` is pairwise equivalent via `==` to the initial segment of `bs`,
or
* there is an index `i` such that `lt as[i] bs[i]`, and for all `j < i`, `as[j] == bs[j]`.
-/
def lex [BEq α] (as bs : Array α) (lt : α α Bool := by exact (· < ·)) : Bool := Id.run do
for h : i in [0 : min as.size bs.size] do

View File

@@ -292,12 +292,15 @@ theorem mapFinIdx_eq_mapFinIdx_iff {xs : Array α} {f g : (i : Nat) → α → (
(xs.mapFinIdx f).mapFinIdx g = xs.mapFinIdx (fun i a h => g i (f i a h) (by simpa using h)) := by
simp [mapFinIdx_eq_iff]
theorem mapFinIdx_eq_mkArray_iff {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {b : β} :
xs.mapFinIdx f = mkArray xs.size b (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {b : β} :
xs.mapFinIdx f = replicate xs.size b (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
rcases xs with l
rw [ toList_inj]
simp [List.mapFinIdx_eq_replicate_iff]
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapFinIdx_eq_mkArray_iff := @mapFinIdx_eq_replicate_iff
@[simp] theorem mapFinIdx_reverse {xs : Array α} {f : (i : Nat) α (h : i < xs.reverse.size) β} :
xs.reverse.mapFinIdx f = (xs.mapFinIdx (fun i a h => f (xs.size - 1 - i) a (by simp; omega))).reverse := by
rcases xs with l
@@ -431,12 +434,15 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
(xs.mapIdx f).mapIdx g = xs.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff]
theorem mapIdx_eq_mkArray_iff {xs : Array α} {f : Nat α β} {b : β} :
mapIdx f xs = mkArray xs.size b (i : Nat) (h : i < xs.size), f i xs[i] = b := by
theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat α β} {b : β} :
mapIdx f xs = replicate xs.size b (i : Nat) (h : i < xs.size), f i xs[i] = b := by
rcases xs with xs
rw [ toList_inj]
simp [List.mapIdx_eq_replicate_iff]
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapIdx_eq_mkArray_iff := @mapIdx_eq_replicate_iff
@[simp] theorem mapIdx_reverse {xs : Array α} {f : Nat α β} :
xs.reverse.mapIdx f = (mapIdx (fun i => f (xs.size - 1 - i)) xs).reverse := by
rcases xs with xs

View File

@@ -16,7 +16,8 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
@[simp] theorem ofFn_zero (f : Fin 0 α) : ofFn f = #[] := rfl
@[simp] theorem ofFn_zero (f : Fin 0 α) : ofFn f = #[] := by
simp [ofFn, ofFn.go]
theorem ofFn_succ (f : Fin (n+1) α) :
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f n, by omega) := by

View File

@@ -30,6 +30,16 @@ private def qpartition {n} (as : Vector α n) (lt : αα → Bool) (lo hi :
(i, ilo, as.swap i hi)
loop as lo lo
/--
Sorts an array using the Quicksort algorithm.
The optional parameter `lt` specifies an ordering predicate. It defaults to `LT.lt`, which must be
decidable to be used for sorting. Use `Array.qsortOrd` to sort the array according to the `Ord α`
instance.
The optional parameters `low` and `high` delimit the region of the array that is sorted. Both are
inclusive, and default to sorting the entire array.
-/
@[inline] def qsort (as : Array α) (lt : α α Bool := by exact (· < ·))
(low := 0) (high := as.size - 1) : Array α :=
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
@@ -50,7 +60,7 @@ private def qpartition {n} (as : Vector α n) (lt : αα → Bool) (lo hi :
set_option linter.unusedVariables.funArgs false in
/--
Sort an array using `compare` to compare elements.
Sorts an array using the Quicksort algorithm, using `Ord.compare` to compare elements.
-/
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
xs.qsort fun x y => compare x y |>.isLT

View File

@@ -39,7 +39,8 @@ theorem range'_ne_empty_iff (s : Nat) {n step : Nat} : range' s n step ≠ #[]
@[simp] theorem range'_zero : range' s 0 step = #[] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := rfl
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = #[s] := by
simp [range', ofFn, ofFn.go]
@[simp] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by
rw [ toList_inj]
@@ -77,7 +78,7 @@ theorem range'_append (s m n step : Nat) :
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append s m n 1
theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ #[s + step * n] := by
exact (range'_append s n 1 step).symm
simpa using (range'_append s n 1 step).symm
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ #[s + n] := by
simp [range'_concat]

View File

@@ -11,11 +11,16 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
/--
Set an element in an array, using a proof that the index is in bounds.
(This proof can usually be omitted, and will be synthesized automatically.)
Replaces the element at a given index in an array.
This will perform the update destructively provided that `a` has a reference
count of 1 when called.
No bounds check is performed, but the function requires a proof that the index is in bounds. This
proof can usually be omitted, and will be synthesized automatically.
The array is modified in-place if there are no other references to it.
Examples:
* `#[0, 1, 2].set 1 5 = #[0, 5, 2]`
* `#["orange", "apple"].set 1 "grape" = #["orange", "grape"]`
-/
@[extern "lean_array_fset"]
def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_elem_tactic) :
@@ -23,10 +28,15 @@ def Array.set (xs : Array α) (i : @& Nat) (v : α) (h : i < xs.size := by get_e
toList := xs.toList.set i v
/--
Set an element in an array, or do nothing if the index is out of bounds.
Replaces the element at the provided index in an array. The array is returned unmodified if the
index is out of bounds.
This will perform the update destructively provided that `a` has a reference
count of 1 when called.
The array is modified in-place if there are no other references to it.
Examples:
* `#[0, 1, 2].setIfInBounds 1 5 = #[0, 5, 2]`
* `#["orange", "apple"].setIfInBounds 1 "grape" = #["orange", "grape"]`
* `#["orange", "apple"].setIfInBounds 5 "grape" = #["orange", "apple"]`
-/
@[inline] def Array.setIfInBounds (xs : Array α) (i : Nat) (v : α) : Array α :=
dite (LT.lt i xs.size) (fun h => xs.set i v h) (fun _ => xs)

View File

@@ -7,6 +7,7 @@ prelude
import Init.Data.Array.Basic
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.missingDocs true
universe u v w
@@ -25,7 +26,18 @@ structure Subarray (α : Type u) where
start : Nat
/-- The ending index of the region of interest (exclusive). -/
stop : Nat
/--
The starting index is no later than the ending index.
The ending index is exclusive. If the starting and ending indices are equal, then the subarray is
empty.
-/
start_le_stop : start stop
/-- The stopping index is no later than the end of the array.
The ending index is exclusive. If it is equal to the size of the array, then the last element of
the array is in the subarray.
-/
stop_le_array_size : stop array.size
namespace Subarray
@@ -110,6 +122,12 @@ instance : EmptyCollection (Subarray α) :=
instance : Inhabited (Subarray α) :=
{}
/--
The run-time implementation of `ForIn.forIn` for `Subarray`, which allows it to be used with `for`
loops in `do`-notation.
This definition replaces `Subarray.forIn`.
-/
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (s : Subarray α) (b : β) (f : α β m (ForInStep β)) : m β :=
let sz := USize.ofNat s.stop
let rec @[specialize] loop (i : USize) (b : β) : m β := do
@@ -122,6 +140,10 @@ instance : Inhabited (Subarray α) :=
pure b
loop (USize.ofNat s.start) b
/--
The implementation of `ForIn.forIn` for `Subarray`, which allows it to be used with `for` loops in
`do`-notation.
-/
-- TODO: provide reference implementation
@[implemented_by Subarray.forInUnsafe]
protected opaque forIn {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (s : Subarray α) (b : β) (f : α β m (ForInStep β)) : m β :=

View File

@@ -149,10 +149,13 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array
· rintro ws, xs, ys, zs, h, rfl, rfl, h₁, h₂
exact ws, xs, ys, zs, by simp_all
@[simp] theorem zipWith_mkArray {a : α} {b : β} {m n : Nat} :
zipWith f (mkArray m a) (mkArray n b) = mkArray (min m n) (f a b) := by
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
simp [ List.toArray_replicate]
@[deprecated zipWith_replicate (since := "2025-03-18")]
abbrev zipWith_mkArray := @zipWith_replicate
theorem map_uncurry_zip_eq_zipWith (f : α β γ) (as : Array α) (bs : Array β) :
map (Function.uncurry f) (as.zip bs) = zipWith f as bs := by
cases as
@@ -270,10 +273,13 @@ theorem zip_eq_append_iff {as : Array α} {bs : Array β} :
as₁ as₂ bs₁ bs₂, as₁.size = bs₁.size as = as₁ ++ as₂ bs = bs₁ ++ bs₂ xs = zip as₁ bs₁ ys = zip as₂ bs₂ := by
simp [zip_eq_zipWith, zipWith_eq_append_iff]
@[simp] theorem zip_mkArray {a : α} {b : β} {m n : Nat} :
zip (mkArray m a) (mkArray n b) = mkArray (min m n) (a, b) := by
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
simp [ List.toArray_replicate]
@[deprecated zip_replicate (since := "2025-03-18")]
abbrev zip_mkArray := @zip_replicate
theorem zip_eq_zip_take_min (as : Array α) (bs : Array β) :
zip as bs = zip (as.take (min as.size bs.size)) (bs.take (min as.size bs.size)) := by
cases as
@@ -317,9 +323,12 @@ theorem map_zipWithAll {δ : Type _} (f : α → β) (g : Option γ → Option
simp [List.map_zipWithAll]
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (mkArray n a) (mkArray n b) = mkArray n (f a b) := by
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
simp [ List.toArray_replicate]
@[deprecated zipWithAll_replicate (since := "2025-03-18")]
abbrev zipWithAll_mkArray := @zipWithAll_replicate
/-! ### unzip -/
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
@@ -360,6 +369,9 @@ theorem zip_of_prod {as : Array α} {bs : Array β} {xs : Array (α × β)} (hl
(hr : xs.map Prod.snd = bs) : xs = as.zip bs := by
rw [ hl, hr, zip_unzip xs, unzip_fst, unzip_snd, zip_unzip, zip_unzip]
@[simp] theorem unzip_mkArray {n : Nat} {a : α} {b : β} :
unzip (mkArray n (a, b)) = (mkArray n a, mkArray n b) := by
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
ext1 <;> simp
@[deprecated unzip_replicate (since := "2025-03-18")]
abbrev unzip_mkArray := @unzip_replicate

View File

@@ -18,7 +18,7 @@ operations on `Fin` are already defined. Some other possible representations are
We define many of the bitvector operations from the
[`QF_BV` logic](https://smtlib.cs.uiowa.edu/logics-all.shtml#QF_BV).
of SMT-LIBv2.
of SMT-LIB v2.
-/
set_option linter.missingDocs true
@@ -96,16 +96,10 @@ This will be renamed `getMsb` after the existing deprecated alias is removed.
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
x.toNat.testBit i
@[deprecated getLsbD (since := "2024-08-29"), inherit_doc getLsbD]
def getLsb (x : BitVec w) (i : Nat) : Bool := x.getLsbD i
/-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/
@[inline] def getMsbD (x : BitVec w) (i : Nat) : Bool :=
i < w && x.getLsbD (w-1-i)
@[deprecated getMsbD (since := "2024-08-29"), inherit_doc getMsbD]
def getMsb (x : BitVec w) (i : Nat) : Bool := x.getMsbD i
/-- Return most-significant bit in bitvector. -/
@[inline] protected def msb (x : BitVec n) : Bool := getMsbD x 0
@@ -202,7 +196,7 @@ section arithmetic
Negation for bit vectors. This can be interpreted as either signed or unsigned negation
modulo `2^n`.
SMT-Lib name: `bvneg`.
SMT-LIB name: `bvneg`.
-/
protected def neg (x : BitVec n) : BitVec n := .ofNat n (2^n - x.toNat)
instance : Neg (BitVec n) := .neg
@@ -216,7 +210,7 @@ protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
Multiplication for bit vectors. This can be interpreted as either signed or unsigned
multiplication modulo `2^n`.
SMT-Lib name: `bvmul`.
SMT-LIB name: `bvmul`.
-/
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
instance : Mul (BitVec n) := .mul
@@ -231,7 +225,7 @@ instance : Div (BitVec n) := ⟨.udiv⟩
/--
Unsigned modulo for bit vectors.
SMT-Lib name: `bvurem`.
SMT-LIB name: `bvurem`.
-/
def umod (x y : BitVec n) : BitVec n :=
(x.toNat % y.toNat)#'(Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt)
@@ -239,10 +233,10 @@ instance : Mod (BitVec n) := ⟨.umod⟩
/--
Unsigned division for bit vectors using the
[SMT-Lib convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml)
[SMT-LIB convention](http://smtlib.cs.uiowa.edu/theories-FixedSizeBitVectors.shtml)
where division by zero returns the `allOnes` bitvector.
SMT-Lib name: `bvudiv`.
SMT-LIB name: `bvudiv`.
-/
def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then allOnes n else udiv x y
@@ -265,11 +259,11 @@ def sdiv (x y : BitVec n) : BitVec n :=
| true, true => udiv (.neg x) (.neg y)
/--
Signed division for bit vectors using SMTLIB rules for division by zero.
Signed division for bit vectors using SMT-LIB rules for division by zero.
Specifically, `smtSDiv x 0 = if x >= 0 then -1 else 1`
SMT-Lib name: `bvsdiv`.
SMT-LIB name: `bvsdiv`.
-/
def smtSDiv (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with
@@ -281,7 +275,7 @@ def smtSDiv (x y : BitVec n) : BitVec n :=
/--
Remainder for signed division rounding to zero.
SMT_Lib name: `bvsrem`.
SMT-LIB name: `bvsrem`.
-/
def srem (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with
@@ -293,7 +287,7 @@ def srem (x y : BitVec n) : BitVec n :=
/--
Remainder for signed division rounded to negative infinity.
SMT_Lib name: `bvsmod`.
SMT-LIB name: `bvsmod`.
-/
def smod (x y : BitVec m) : BitVec m :=
match x.msb, y.msb with
@@ -327,14 +321,14 @@ section relations
/--
Unsigned less-than for bit vectors.
SMT-Lib name: `bvult`.
SMT-LIB name: `bvult`.
-/
protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat
/--
Unsigned less-than-or-equal-to for bit vectors.
SMT-Lib name: `bvule`.
SMT-LIB name: `bvule`.
-/
protected def ule (x y : BitVec n) : Bool := x.toNat y.toNat
@@ -345,14 +339,14 @@ Signed less-than for bit vectors.
BitVec.slt 6#4 7 = true
BitVec.slt 7#4 8 = false
```
SMT-Lib name: `bvslt`.
SMT-LIB name: `bvslt`.
-/
protected def slt (x y : BitVec n) : Bool := x.toInt < y.toInt
/--
Signed less-than-or-equal-to for bit vectors.
SMT-Lib name: `bvsle`.
SMT-LIB name: `bvsle`.
-/
protected def sle (x y : BitVec n) : Bool := x.toInt y.toInt
@@ -384,7 +378,7 @@ def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toN
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
yield a new bitvector of size `hi - lo + 1`.
SMT-Lib name: `extract`.
SMT-LIB name: `extract`.
-/
def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x
@@ -415,7 +409,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
- truncating the high bits, if `v < w`.
SMT-Lib name: `zero_extend`.
SMT-LIB name: `zero_extend`.
-/
def setWidth (v : Nat) (x : BitVec w) : BitVec v :=
if h : w v then
@@ -428,7 +422,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
- truncating the high bits, if `v < w`.
SMT-Lib name: `zero_extend`.
SMT-LIB name: `zero_extend`.
-/
abbrev zeroExtend := @setWidth
@@ -437,7 +431,7 @@ Transform `x` of length `w` into a bitvector of length `v`, by either:
- zero extending, that is, adding zeros in the high bits until it has length `v`, if `v > w`, or
- truncating the high bits, if `v < w`.
SMT-Lib name: `zero_extend`.
SMT-LIB name: `zero_extend`.
-/
abbrev truncate := @setWidth
@@ -445,7 +439,7 @@ abbrev truncate := @setWidth
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant
bit in `x`. If `x` is an empty vector, then the sign is treated as zero.
SMT-Lib name: `sign_extend`.
SMT-LIB name: `sign_extend`.
-/
def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt
@@ -460,7 +454,7 @@ Bitwise AND for bit vectors.
0b1010#4 &&& 0b0110#4 = 0b0010#4
```
SMT-Lib name: `bvand`.
SMT-LIB name: `bvand`.
-/
protected def and (x y : BitVec n) : BitVec n :=
(x.toNat &&& y.toNat)#'(Nat.and_lt_two_pow x.toNat y.isLt)
@@ -473,7 +467,7 @@ Bitwise OR for bit vectors.
0b1010#4 ||| 0b0110#4 = 0b1110#4
```
SMT-Lib name: `bvor`.
SMT-LIB name: `bvor`.
-/
protected def or (x y : BitVec n) : BitVec n :=
(x.toNat ||| y.toNat)#'(Nat.or_lt_two_pow x.isLt y.isLt)
@@ -486,7 +480,7 @@ instance : OrOp (BitVec w) := ⟨.or⟩
0b1010#4 ^^^ 0b0110#4 = 0b1100#4
```
SMT-Lib name: `bvxor`.
SMT-LIB name: `bvxor`.
-/
protected def xor (x y : BitVec n) : BitVec n :=
(x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt)
@@ -498,7 +492,7 @@ Bitwise NOT for bit vectors.
```lean
~~~(0b0101#4) == 0b1010
```
SMT-Lib name: `bvnot`.
SMT-LIB name: `bvnot`.
-/
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
instance : Complement (BitVec w) := .not
@@ -507,7 +501,7 @@ instance : Complement (BitVec w) := ⟨.not⟩
Left shift for bit vectors. The low bits are filled with zeros. As a numeric operation, this is
equivalent to `x * 2^s`, modulo `2^n`.
SMT-Lib name: `bvshl` except this operator uses a `Nat` shift value.
SMT-LIB name: `bvshl` except this operator uses a `Nat` shift value.
-/
protected def shiftLeft (x : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (x.toNat <<< s)
instance : HShiftLeft (BitVec w) Nat (BitVec w) := .shiftLeft
@@ -516,7 +510,7 @@ instance : HShiftLeft (BitVec w) Nat (BitVec w) := ⟨.shiftLeft⟩
(Logical) right shift for bit vectors. The high bits are filled with zeros.
As a numeric operation, this is equivalent to `x / 2^s`, rounding down.
SMT-Lib name: `bvlshr` except this operator uses a `Nat` shift value.
SMT-LIB name: `bvlshr` except this operator uses a `Nat` shift value.
-/
def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
(x.toNat >>> s)#'(by
@@ -532,7 +526,7 @@ Arithmetic right shift for bit vectors. The high bits are filled with the
most-significant bit.
As a numeric operation, this is equivalent to `x.toInt >>> s`.
SMT-Lib name: `bvashr` except this operator uses a `Nat` shift value.
SMT-LIB name: `bvashr` except this operator uses a `Nat` shift value.
-/
def sshiftRight (x : BitVec n) (s : Nat) : BitVec n := .ofInt n (x.toInt >>> s)
@@ -544,7 +538,7 @@ Arithmetic right shift for bit vectors. The high bits are filled with the
most-significant bit.
As a numeric operation, this is equivalent to `a.toInt >>> s.toNat`.
SMT-Lib name: `bvashr`.
SMT-LIB name: `bvashr`.
-/
def sshiftRight' (a : BitVec n) (s : BitVec m) : BitVec n := a.sshiftRight s.toNat
@@ -560,7 +554,7 @@ bits wrapping around to fill the low bits.
```lean
rotateLeft 0b0011#4 3 = 0b1001
```
SMT-Lib name: `rotate_left` except this operator uses a `Nat` shift amount.
SMT-LIB name: `rotate_left` except this operator uses a `Nat` shift amount.
-/
def rotateLeft (x : BitVec w) (n : Nat) : BitVec w := rotateLeftAux x (n % w)
@@ -579,7 +573,7 @@ bottom `n` bits wrapping around to fill the high bits.
```lean
rotateRight 0b01001#5 1 = 0b10100
```
SMT-Lib name: `rotate_right` except this operator uses a `Nat` shift amount.
SMT-LIB name: `rotate_right` except this operator uses a `Nat` shift amount.
-/
def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
@@ -587,7 +581,7 @@ def rotateRight (x : BitVec w) (n : Nat) : BitVec w := rotateRightAux x (n % w)
Concatenation of bitvectors. This uses the "big endian" convention that the more significant
input is on the left, so `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
SMT-Lib name: `concat`.
SMT-LIB name: `concat`.
-/
def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
shiftLeftZeroExtend msbs m ||| setWidth' (Nat.le_add_left m n) lsbs
@@ -684,18 +678,26 @@ def ofBoolListLE : (bs : List Bool) → BitVec bs.length
/-- `uaddOverflow x y` returns `true` if addition of `x` and `y` results in *unsigned* overflow.
SMT-Lib name: `bvuaddo`.
SMT-LIB name: `bvuaddo`.
-/
def uaddOverflow {w : Nat} (x y : BitVec w) : Bool := x.toNat + y.toNat 2 ^ w
/-- `saddOverflow x y` returns `true` if addition of `x` and `y` results in *signed* overflow,
treating `x` and `y` as 2's complement signed bitvectors.
SMT-Lib name: `bvsaddo`.
SMT-LIB name: `bvsaddo`.
-/
def saddOverflow {w : Nat} (x y : BitVec w) : Bool :=
(x.toInt + y.toInt 2 ^ (w - 1)) || (x.toInt + y.toInt < - 2 ^ (w - 1))
/-- `negOverflow x` returns `true` if the negation of `x` results in overflow.
For a BitVec `x` with width `0 < w`, this only happens if `x = intMin`.
SMT-Lib name: `bvnego`.
-/
def negOverflow {w : Nat} (x : BitVec w) : Bool :=
x.toInt == - 2 ^ (w - 1)
/- ### reverse -/
/-- Reverse the bits in a bitvector. -/

View File

@@ -35,7 +35,7 @@ section arithmetic
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
modulo `2^n`.
SMT-Lib name: `bvadd`.
SMT-LIB name: `bvadd`.
-/
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
instance : Add (BitVec n) := BitVec.add

View File

@@ -91,7 +91,7 @@ First, we prove bitvector lemmas to unfold a high-level operation (such as multi
into already bitblastable operations (such as addition and left shift).
We then use these lemmas to prove the correctness of the circuit that `bv_decide` builds.
We use this workflow to implement bitblasting for all SMT-LIB2 operations.
We use this workflow to implement bitblasting for all SMT-LIB v2 operations.
## Main results
* `x + y : BitVec w` is `(adc x y false).2`.
@@ -513,6 +513,9 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
@[simp] theorem BitVec.setWidth_neg_of_le {x : BitVec v} (h : w v) : BitVec.setWidth w (-x) = -BitVec.setWidth w x := by
simp [ BitVec.signExtend_eq_setWidth_of_le _ h, BitVec.signExtend_neg_of_le h]
/-! ### abs -/
theorem msb_abs {w : Nat} {x : BitVec w} :
@@ -1296,6 +1299,15 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
simp
omega
theorem negOverflow_eq {w : Nat} (x : BitVec w) :
(negOverflow x) = (decide (0 < w) && (x == intMin w)) := by
simp only [negOverflow]
rcases w with _|w
· simp [toInt_of_zero_length, Int.min_eq_right]
· suffices - 2 ^ w = (intMin (w + 1)).toInt by simp [beq_eq_decide_eq, toInt_inj, this]
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod, Int.neg_inj]
rw_mod_cast [Nat.mod_eq_of_lt (by simp [Nat.pow_lt_pow_succ])]
/- ### umod -/
theorem getElem_umod {n d : BitVec w} (hi : i < w) :
@@ -1338,4 +1350,53 @@ theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x,
have := congrArg g h'
simpa [h] using this
/-! ### Lemmas that use Bitblasting circuits -/
theorem add_sub_comm {x y : BitVec w} : x + y - z = x - z + y := by
apply eq_of_toNat_eq
simp only [toNat_sub, toNat_add, add_mod_mod, mod_add_mod]
congr 1
omega
theorem sub_add_comm {x y : BitVec w} : x - y + z = x + z - y := by
rw [add_sub_comm]
theorem not_add_one {x : BitVec w} : ~~~ (x + 1#w) = ~~~ x - 1#w := by
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
theorem not_add_eq_not_neg {x y : BitVec w} : ~~~ (x + y) = ~~~ x - y := by
rw [not_eq_neg_add, not_eq_neg_add, neg_add]
simp only [sub_toAdd]
rw [BitVec.add_assoc, @BitVec.add_comm _ (-y), BitVec.add_assoc]
theorem not_sub_one_eq_not_add_one {x : BitVec w} : ~~~ (x - 1#w) = ~~~ x + 1#w := by
rw [not_eq_neg_add, not_eq_neg_add, neg_sub,
BitVec.add_sub_cancel, BitVec.sub_add_cancel]
theorem not_sub_eq_not_add {x y : BitVec w} : ~~~ (x - y) = ~~~ x + y := by
rw [BitVec.sub_toAdd, not_add_eq_not_neg, sub_neg]
/-- The value of `(carry i x y false)` can be computed by truncating `x` and `y`
to `len` bits where `len ≥ i`. -/
theorem carry_extractLsb'_eq_carry {w i len : Nat} (hi : i < len)
{x y : BitVec w} {b : Bool}:
(carry i (extractLsb' 0 len x) (extractLsb' 0 len y) b)
= (carry i x y b) := by
simp only [carry, extractLsb'_toNat, shiftRight_zero, toNat_false, Nat.add_zero, ge_iff_le,
decide_eq_decide]
have : 2 ^ i 2^len := by
apply Nat.pow_dvd_pow
omega
rw [Nat.mod_mod_of_dvd _ this, Nat.mod_mod_of_dvd _ this]
/--
The `[0..len)` low bits of `x + y` can be computed by truncating `x` and `y`
to `len` bits and then adding.
-/
theorem extractLsb'_add {w len : Nat} {x y : BitVec w} (hlen : len w) :
(x + y).extractLsb' 0 len = x.extractLsb' 0 len + y.extractLsb' 0 len := by
ext i hi
rw [getElem_extractLsb', Nat.zero_add, getLsbD_add (by omega)]
simp [getElem_add, carry_extractLsb'_eq_carry hi, getElem_extractLsb', Nat.zero_add]
end BitVec

View File

@@ -260,7 +260,7 @@ theorem msb_of_zero_length (h : w = 0) (x : BitVec w) : x.msb = false := by
theorem ofFin_ofNat (n : Nat) :
ofFin (no_index (OfNat.ofNat n : Fin (2^w))) = OfNat.ofNat n := by
simp only [OfNat.ofNat, Fin.ofNat', BitVec.ofNat, Nat.and_pow_two_sub_one_eq_mod]
simp only [OfNat.ofNat, Fin.ofNat', BitVec.ofNat, Nat.and_two_pow_sub_one_eq_mod]
theorem eq_of_toFin_eq : {x y : BitVec w}, x.toFin = y.toFin x = y
| _, _, _, _, rfl => rfl
@@ -1072,6 +1072,26 @@ theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h
apply eq_of_toNat_eq
simp [extractLsb']
theorem getLsbD_eq_extractLsb' (x : BitVec w) (i : Nat) :
x.getLsbD i = (x.extractLsb' i 1 == 1#1) := by
rw [Bool.eq_iff_iff]
simp [BitVec.eq_of_getLsbD_eq_iff]
theorem getElem_eq_extractLsb' (x : BitVec w) (i : Nat) (h : i < w) :
x[i] = (x.extractLsb' i 1 == 1#1) := by
rw [ getLsbD_eq_getElem, getLsbD_eq_extractLsb']
@[simp]
theorem extractLsb'_zero {w start len : Nat} : (0#w).extractLsb' start len = 0#len := by
apply eq_of_toNat_eq
simp [extractLsb']
@[simp]
theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
x.extractLsb' start 0 = 0#0 := by
ext i hi
omega
/-! ### allOnes -/
@[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
@@ -1553,6 +1573,22 @@ theorem not_self_ne {a : BitVec w} (h : 0 < w) : ~~~a ≠ a := by
rw [ne_comm]
simp [h]
theorem not_and {x y : BitVec w} : ~~~ (x &&& y) = ~~~ x ||| ~~~ y := by
ext i
simp
theorem not_or {x y : BitVec w} : ~~~ (x ||| y) = ~~~ x &&& ~~~ y := by
ext i
simp
theorem not_xor_left {x y : BitVec w} : ~~~ (x ^^^ y) = ~~~ x ^^^ y := by
ext i
simp
theorem not_xor_right {x y : BitVec w} : ~~~ (x ^^^ y) = x ^^^ ~~~ y := by
ext i
simp
/-! ### cast -/
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
@@ -1702,6 +1738,10 @@ theorem allOnes_shiftLeft_or_shiftLeft {x : BitVec w} {n : Nat} :
BitVec.allOnes w <<< n ||| x <<< n = BitVec.allOnes w <<< n := by
simp [ shiftLeft_or_distrib]
@[simp] theorem setWidth_shiftLeft_of_le {x : BitVec w} {y : Nat} (hi : i w) :
(x <<< y).setWidth i = x.setWidth i <<< y :=
eq_of_getElem_eq (fun j hj => Bool.eq_iff_iff.2 (by simp; omega))
/-! ### shiftLeft reductions from BitVec to Nat -/
@[simp]
@@ -1853,6 +1893,15 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
case succ nn ih =>
simp [BitVec.ushiftRight_eq, getMsbD_ushiftRight, BitVec.msb, ih, show nn + 1 > 0 by omega]
@[simp] theorem setWidth_ushiftRight {x : BitVec w} {y : Nat} (hi : w i) :
(x >>> y).setWidth i = x.setWidth i >>> y := by
refine eq_of_getElem_eq (fun j hj => ?_)
simp only [getElem_setWidth, getLsbD_ushiftRight, getElem_ushiftRight, getLsbD_setWidth,
Bool.iff_and_self, decide_eq_true_eq]
intro ha
have := lt_of_getLsbD ha
omega
/-! ### ushiftRight reductions from BitVec to Nat -/
@[simp]
@@ -2253,7 +2302,7 @@ private theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
rw [hk, testBit_toNat, getLsbD_signExtend, Nat.pow_add, Nat.mul_sub_one, Nat.add_comm (x.toNat)]
by_cases hx : x.msb
· simp only [hx, Bool.if_true_right, reduceIte,
Nat.testBit_mul_pow_two_add _ x.isLt,
Nat.testBit_two_pow_mul_add _ x.isLt,
testBit_toNat, Nat.testBit_two_pow_sub_one]
-- Case analysis on i being in the intervals [0..w), [w..w + k), [w+k..∞)
have hi : i < w (w i i < w + k) w + k i := by omega
@@ -2627,7 +2676,7 @@ theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start
· simp only [hstart, reduceDIte]
ext i hi
simp [getElem_extractLsb', getLsbD_append,
show ¬start + i < w by omega, reduceIte,
show ¬start + i < w by omega, reduceIte,
show start + i - w = start - w + i by omega]
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
@@ -2678,7 +2727,7 @@ theorem getMsbD_rev (x : BitVec w) (i : Fin w) :
/-- Variant of `toNat_cons` using `+` instead of `|||`. -/
theorem toNat_cons' {x : BitVec w} :
(cons a x).toNat = (a.toNat <<< w) + x.toNat := by
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.mul_add_lt_is_or, x.isLt]
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.two_pow_add_eq_or_of_lt, x.isLt]
theorem getLsbD_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
getLsbD (cons b x) i = if i = n then b else getLsbD x i := by
@@ -3108,6 +3157,9 @@ theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1#w := by
have hx : x.toNat < 2^w := x.isLt
rw [Nat.sub_sub, Nat.add_comm 1 x.toNat, Nat.sub_sub, Nat.sub_add_cancel (by omega)]
theorem not_eq_neg_add (x : BitVec w) : ~~~ x = -x - 1#w := by
rw [eq_sub_iff_add_eq, neg_eq_not_add, BitVec.add_comm]
@[simp]
theorem neg_neg {x : BitVec w} : - - x = x := by
by_cases h : x = 0#w
@@ -3168,6 +3220,14 @@ theorem add_neg_eq_sub {x y : BitVec w} : x + - y = (x - y) := by
apply eq_of_toInt_eq
simp [toInt_neg, Int.sub_eq_add_neg]
@[simp]
theorem sub_neg {x y : BitVec w} : x - - y = x + y := by
apply eq_of_toInt_eq
simp [toInt_neg, Int.bmod_neg]
theorem neg_sub {x y : BitVec w} : - (x - y) = - x + y := by
rw [sub_toAdd, neg_add, sub_neg]
/- ### add/sub injectivity -/
@[simp]
@@ -3327,7 +3387,7 @@ theorem mul_eq_and {a b : BitVec 1} : a * b = a &&& b := by
@[simp] protected theorem neg_mul (x y : BitVec w) : -x * y = -(x * y) := by
apply eq_of_toInt_eq
simp [toInt_neg]
simp [toInt_neg, Int.neg_mul]
@[simp] protected theorem mul_neg (x y : BitVec w) : x * -y = -(x * y) := by
rw [BitVec.mul_comm, BitVec.neg_mul, BitVec.mul_comm]
@@ -3336,11 +3396,23 @@ protected theorem neg_mul_neg (x y : BitVec w) : -x * -y = x * y := by simp
protected theorem neg_mul_comm (x y : BitVec w) : -x * y = x * -y := by simp
theorem mul_sub {x y z : BitVec w} :
x * (y - z) = x * y - x * z := by
rw [ add_neg_eq_sub, mul_add, BitVec.mul_neg, add_neg_eq_sub]
theorem neg_add_mul_eq_mul_not {x y : BitVec w} :
- (x + x * y) = x * ~~~ y := by
rw [neg_add, sub_toAdd, BitVec.mul_neg, neg_eq_not_add y, mul_add,
BitVec.mul_one, BitVec.add_comm, BitVec.add_assoc, BitVec.add_right_eq_self,
add_neg_eq_sub, BitVec.sub_self]
BitVec.mul_one, BitVec.add_comm, BitVec.add_assoc,
BitVec.add_right_eq_self, add_neg_eq_sub, BitVec.sub_self]
theorem neg_mul_not_eq_add_mul {x y : BitVec w} :
- (x * ~~~ y) = x + x * y := by
rw [not_eq_neg_add, mul_sub, neg_sub, BitVec.mul_neg, neg_neg,
BitVec.mul_one, BitVec.add_comm]
theorem neg_eq_neg_one_mul (b : BitVec w) : -b = -1#w * b :=
BitVec.eq_of_toInt_eq (by simp)
/-! ### le and lt -/
@@ -4102,6 +4174,22 @@ theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
have h1 : 1 < 2 ^ (w + 1) := Nat.one_lt_two_pow (by omega)
rw [Nat.mod_eq_of_lt h1, Nat.shiftLeft_eq, Nat.one_mul]
theorem toNat_twoPow_of_le {i w : Nat} (h : w i) : (twoPow w i).toNat = 0 := by
rw [toNat_twoPow]
apply Nat.mod_eq_zero_of_dvd
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
theorem toNat_twoPow_of_lt {i w : Nat} (h : i < w) : (twoPow w i).toNat = 2^i := by
rw [toNat_twoPow]
apply Nat.mod_eq_of_lt
apply Nat.pow_lt_pow_of_lt (by omega) (by omega)
theorem toNat_twoPow_eq_ite {i w : Nat} : (twoPow w i).toNat = if i < w then 2^i else 0 := by
by_cases h : i < w
· simp only [h, toNat_twoPow_of_lt, if_true]
· simp only [h, if_false]
rw [toNat_twoPow_of_le (by omega)]
@[simp]
theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j)) := by
rcases w with rfl | w
@@ -4120,6 +4208,33 @@ theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j
simp at hi
simp_all
@[simp]
theorem msb_twoPow {i w: Nat} :
(twoPow w i).msb = (decide (i < w) && decide (i = w - 1)) := by
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.sub_zero, getLsbD_twoPow,
Bool.and_iff_right_iff_imp, Bool.and_eq_true, decide_eq_true_eq, and_imp]
intros
omega
theorem toInt_twoPow {w i : Nat} :
(BitVec.twoPow w i).toInt = if w i then 0
else if i + 1 = w then (-(2^i : Nat) : Int) else 2^i := by
simp only [BitVec.toInt_eq_msb_cond, toNat_twoPow_eq_ite]
rcases w with _ | w
· simp
· by_cases h : i = w
· simp [h, show ¬ (w + 1 w) by omega]
omega
· by_cases h' : w + 1 i
· simp [h', show ¬ i < w + 1 by omega]
· simp [h, h', show i < w + 1 by omega, Int.natCast_pow]
theorem toFin_twoPow {w i : Nat} :
(BitVec.twoPow w i).toFin = Fin.ofNat' (2^w) (2^i) := by
rcases w with rfl | w
· simp [BitVec.twoPow, BitVec.toFin, toFin_shiftLeft, Fin.fin_one_eq_zero]
· simp [BitVec.twoPow, BitVec.toFin, toFin_shiftLeft, Nat.shiftLeft_eq]
@[simp]
theorem getElem_twoPow {i j : Nat} (h : j < w) : (twoPow w i)[j] = decide (j = i) := by
rw [getLsbD_eq_getElem, getLsbD_twoPow]
@@ -4133,14 +4248,6 @@ theorem getMsbD_twoPow {i j w: Nat} :
by_cases h₀ : i < w <;> by_cases h₁ : j < w <;>
simp [h₀, h₁] <;> omega
@[simp]
theorem msb_twoPow {i w: Nat} :
(twoPow w i).msb = (decide (i < w) && decide (i = w - 1)) := by
simp only [BitVec.msb, getMsbD_eq_getLsbD, Nat.sub_zero, getLsbD_twoPow,
Bool.and_iff_right_iff_imp, Bool.and_eq_true, decide_eq_true_eq, and_imp]
intros
omega
theorem and_twoPow (x : BitVec w) (i : Nat) :
x &&& (twoPow w i) = if x.getLsbD i then twoPow w i else 0#w := by
ext j h
@@ -4192,6 +4299,10 @@ theorem udiv_twoPow_eq_of_lt {w : Nat} {x : BitVec w} {k : Nat} (hk : k < w) : x
have : 2^k < 2^w := Nat.pow_lt_pow_of_lt (by decide) hk
simp [bitvec_to_nat, Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt this]
theorem shiftLeft_neg {x : BitVec w} {y : Nat} :
(-x) <<< y = - (x <<< y) := by
rw [shiftLeft_eq_mul_twoPow, shiftLeft_eq_mul_twoPow, BitVec.neg_mul]
/- ### cons -/
@[simp] theorem true_cons_zero : cons true 0#w = twoPow (w + 1) w := by
@@ -4312,6 +4423,15 @@ theorem replicate_succ' {x : BitVec w} :
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
simp [replicate_append_self]
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
apply BitVec.eq_of_toNat_eq
rw [toNat_setWidth]
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
by_cases h : i w
· rw [Nat.mod_eq_zero_of_dvd (Nat.pow_dvd_pow 2 h), Nat.mod_zero, Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 h)]
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
/-! ### intMin -/
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/

View File

@@ -12,4 +12,31 @@ namespace Fin
@[simp] theorem and_val (a b : Fin n) : (a &&& b).val = a.val &&& b.val :=
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt Nat.and_le_left a.isLt)
@[simp] theorem or_val_of_two_pow {w} (a b : Fin (2 ^ w)) : (a ||| b).val = a.val ||| b.val :=
Nat.mod_eq_of_lt (Nat.or_lt_two_pow a.isLt b.isLt)
@[simp] theorem or_val_of_uInt8Size (a b : Fin UInt8.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 8) a b
@[simp] theorem or_val_of_uInt16Size (a b : Fin UInt16.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 16) a b
@[simp] theorem or_val_of_uInt32Size (a b : Fin UInt32.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 32) a b
@[simp] theorem or_val_of_uInt64Size (a b : Fin UInt64.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow (w := 64) a b
@[simp] theorem or_val_of_uSizeSize (a b : Fin USize.size) : (a ||| b).val = a.val ||| b.val := or_val_of_two_pow a b
theorem or_val (a b : Fin n) : (a ||| b).val = (a.val ||| b.val) % n := rfl
@[simp] theorem xor_val_of_two_pow {w} (a b : Fin (2 ^ w)) : (a ^^^ b).val = a.val ^^^ b.val :=
Nat.mod_eq_of_lt (Nat.xor_lt_two_pow a.isLt b.isLt)
@[simp] theorem xor_val_of_uInt8Size (a b : Fin UInt8.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 8) a b
@[simp] theorem xor_val_of_uInt16Size (a b : Fin UInt16.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 16) a b
@[simp] theorem xor_val_of_uInt32Size (a b : Fin UInt32.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 32) a b
@[simp] theorem xor_val_of_uInt64Size (a b : Fin UInt64.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow (w := 64) a b
@[simp] theorem xor_val_of_uSizeSize (a b : Fin USize.size) : (a ^^^ b).val = a.val ^^^ b.val := xor_val_of_two_pow a b
theorem xor_val (a b : Fin n) : (a ^^^ b).val = (a.val ^^^ b.val) % n := rfl
@[simp] theorem shiftLeft_val (a b : Fin n) : (a <<< b).val = (a.val <<< b.val) % n := rfl
@[simp] theorem shiftRight_val (a b : Fin n) : (a >>> b).val = a.val >>> b.val :=
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt (Nat.shiftRight_le _ _) a.isLt)
end Fin

View File

@@ -141,7 +141,9 @@ theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) → α → m α) (x
| zero =>
rw [foldrM_loop_zero, foldrM_loop_succ, pure_bind]
conv => rhs; rw [bind_pure (f 0 x)]
congr; funext
congr
funext
try simp only [foldrM.loop] -- the try makes this proof work with and without opaque wf rec
| succ i ih =>
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
congr; funext; exact ih ..

View File

@@ -140,15 +140,27 @@ instance : ToString Float where
@[extern "lean_uint16_to_float"] opaque UInt16.toFloat (n : UInt16) : Float
/-- Obtains the `Float` whose value is the same as the given `UInt32`. -/
@[extern "lean_uint32_to_float"] opaque UInt32.toFloat (n : UInt32) : Float
/-- Obtains a `Float` whose value is near the given `UInt64`. It will be exactly the value of the
given `UInt64` if such a `Float` exists. If no such `Float` exists, the returned value will either
be the smallest `Float` this is larger than the given value, or the largest `Float` this is smaller
than the given value. -/
/--
Obtains a `Float` whose value is near the given `UInt64`.
It will be exactly the value of the given `UInt64` if such a `Float` exists. If no such `Float`
exists, the returned value will either be the smallest `Float` that is larger than the given value,
or the largest `Float` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
/-- Obtains a `Float` whose value is near the given `USize`. It will be exactly the value of the
given `USize` if such a `Float` exists. If no such `Float` exists, the returned value will either
be the smallest `Float` this is larger than the given value, or the largest `Float` this is smaller
than the given value. -/
/--
Obtains a `Float` whose value is near the given `USize`.
It will be exactly the value of the given `USize` if such a `Float` exists. If no such `Float`
exists, the returned value will either be the smallest `Float` that is larger than the given value,
or the largest `Float` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_usize_to_float"] opaque USize.toFloat (n : USize) : Float
instance : Inhabited Float where

View File

@@ -131,20 +131,37 @@ instance : ToString Float32 where
@[extern "lean_uint8_to_float32"] opaque UInt8.toFloat32 (n : UInt8) : Float32
/-- Obtains the `Float32` whose value is the same as the given `UInt16`. -/
@[extern "lean_uint16_to_float32"] opaque UInt16.toFloat32 (n : UInt16) : Float32
/-- Obtains a `Float32` whose value is near the given `UInt32`. It will be exactly the value of the
given `UInt32` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
than the given value. -/
/--
Obtains a `Float32` whose value is near the given `UInt32`.
It will be exactly the value of the given `UInt32` if such a `Float32` exists. If no such `Float32`
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_uint32_to_float32"] opaque UInt32.toFloat32 (n : UInt32) : Float32
/-- Obtains a `Float32` whose value is near the given `UInt64`. It will be exactly the value of the
given `UInt64` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
than the given value. -/
/--
Obtains a `Float32` whose value is near the given `UInt64`.
It will be exactly the value of the given `UInt64` if such a `Float32` exists. If no such `Float32`
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_uint64_to_float32"] opaque UInt64.toFloat32 (n : UInt64) : Float32
/-- Obtains a `Float32` whose value is near the given `USize`. It will be exactly the value of the
given `USize` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
than the given value. -/
/-- Obtains a `Float32` whose value is near the given `USize`.
It will be exactly the value of the given `USize` if such a `Float32` exists. If no such `Float32`
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_usize_to_float32"] opaque USize.toFloat32 (n : USize) : Float32
instance : Inhabited Float32 where

View File

@@ -39,6 +39,10 @@ instance {α : Type u} [ToFormat α] : ToFormat (Option α) :=
instance {α : Type u} {β : Type v} [ToFormat α] [ToFormat β] : ToFormat (Prod α β) where
format := fun (a, b) => Format.paren <| format a ++ "," ++ Format.line ++ format b
/--
Converts a string to a pretty-printer document, replacing newlines in the string with
`Std.Format.line`.
-/
def String.toFormat (s : String) : Std.Format :=
Std.Format.joinSep (s.splitOn "\n") Std.Format.line

View File

@@ -26,24 +26,27 @@ Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
-/
/--
The type of integers. It is defined as an inductive type based on the
natural number type `Nat` featuring two constructors: "a natural
number is an integer", and "the negation of a successor of a natural
number is an integer". The former represents integers between `0`
(inclusive) and `∞`, and the latter integers between `-∞` and `-1`
(inclusive).
The integers.
This type is special-cased by the compiler. The runtime has a special
representation for `Int` which stores "small" signed numbers directly,
and larger numbers use an arbitrary precision "bignum" library
(usually [GMP](https://gmplib.org/)). A "small number" is an integer
that can be encoded with 63 bits (31 bits on 32-bits architectures).
This type is special-cased by the compiler and overridden with an efficient implementation. The
runtime has a special representation for `Int` that stores small signed numbers directly, while
larger numbers use a fast arbitrary-precision arithmetic library (usually
[GMP](https://gmplib.org/)). A small number is an integer that can be encoded with one fewer bits
than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit
architectures).
-/
inductive Int : Type where
/-- A natural number is an integer (`0` to `∞`). -/
/--
A natural number is an integer.
This constructor covers the non-negative integers (from `0` to `∞`).
-/
| ofNat : Nat Int
/-- The negation of the successor of a natural number is an integer
(`-1` to `-∞`). -/
/--
The negation of the successor of a natural number is an integer.
This constructor covers the negative integers (from `-1` to `-∞`).
-/
| negSucc : Nat Int
attribute [extern "lean_nat_to_int"] Int.ofNat
@@ -78,15 +81,29 @@ protected theorem zero_ne_one : (0 : Int) ≠ 1 := nofun
theorem ofNat_two : ((2 : Nat) : Int) = 2 := rfl
/-- Negation of a natural number. -/
/--
Negation of natural numbers.
Examples:
* `Int.negOfNat 6 = -6`
* `Int.negOfNat 0 = 0`
-/
def negOfNat : Nat Int
| 0 => 0
| succ m => negSucc m
set_option bootstrap.genMatcherCode false in
/-- Negation of an integer.
/--
Negation of integers, usually accessed via the `-` prefix operator.
Implemented by efficient native code. -/
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Examples:
* `-(6 : Int) = -6`
* `-(-6 : Int) = 6`
* `(12 : Int).neg = -12`
-/
@[extern "lean_int_neg"]
protected def neg (n : @& Int) : Int :=
match n with
@@ -105,21 +122,30 @@ protected def neg (n : @& Int) : Int :=
instance instNegInt : Neg Int where
neg := Int.neg
/-- Subtraction of two natural numbers. -/
/--
Non-truncating subtraction of two natural numbers.
Examples:
* `Int.subNatNat 5 2 = 3`
* `Int.subNatNat 2 5 = -3`
* `Int.subNatNat 0 13 = -13`
-/
def subNatNat (m n : Nat) : Int :=
match (n - m : Nat) with
| 0 => ofNat (m - n) -- m ≥ n
| (succ k) => negSucc k
set_option bootstrap.genMatcherCode false in
/-- Addition of two integers.
/--
Addition of integers, usually accessed via the `+` operator.
```
#eval (7 : Int) + (6 : Int) -- 13
#eval (6 : Int) + (-6 : Int) -- 0
```
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Implemented by efficient native code. -/
Examples:
* `(7 : Int) + (6 : Int) = 13`
* `(6 : Int) + (-6 : Int) = 0`
-/
@[extern "lean_int_add"]
protected def add (m n : @& Int) : Int :=
match m, n with
@@ -132,15 +158,17 @@ instance : Add Int where
add := Int.add
set_option bootstrap.genMatcherCode false in
/-- Multiplication of two integers.
/--
Multiplication of integers, usually accessed via the `*` operator.
```
#eval (63 : Int) * (6 : Int) -- 378
#eval (6 : Int) * (-6 : Int) -- -36
#eval (7 : Int) * (0 : Int) -- 0
```
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Implemented by efficient native code. -/
Examples:
* `(63 : Int) * (6 : Int) = 378`
* `(6 : Int) * (-6 : Int) = -36`
* `(7 : Int) * (0 : Int) = 0`
-/
@[extern "lean_int_mul"]
protected def mul (m n : @& Int) : Int :=
match m, n with
@@ -152,48 +180,65 @@ protected def mul (m n : @& Int) : Int :=
instance : Mul Int where
mul := Int.mul
/-- Subtraction of two integers.
```
#eval (63 : Int) - (6 : Int) -- 57
#eval (7 : Int) - (0 : Int) -- 7
#eval (0 : Int) - (7 : Int) -- -7
```
/--
Subtraction of integers, usually accessed via the `-` operator.
Implemented by efficient native code. -/
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Examples:
* `(63 : Int) - (6 : Int) = 57`
* `(7 : Int) - (0 : Int) = 7`
* `(0 : Int) - (7 : Int) = -7`
-/
@[extern "lean_int_sub"]
protected def sub (m n : @& Int) : Int := m + (- n)
instance : Sub Int where
sub := Int.sub
/-- A proof that an `Int` is non-negative. -/
/--
An integer is non-negative if it is equal to a natural number.
-/
inductive NonNeg : Int Prop where
/-- Sole constructor, proving that `ofNat n` is positive. -/
/--
For all natural numbers `n`, `Int.ofNat n` is non-negative.
-/
| mk (n : Nat) : NonNeg (ofNat n)
/-- Definition of `a ≤ b`, encoded as `b - a ≥ 0`. -/
/--
Non-strict inequality of integers, usually accessed via the `≤` operator.
`a ≤ b` is defined as `b - a ≥ 0`, using `Int.NonNeg`.
-/
protected def le (a b : Int) : Prop := NonNeg (b - a)
instance instLEInt : LE Int where
le := Int.le
/-- Definition of `a < b`, encoded as `a + 1 ≤ b`. -/
/--
Strict inequality of integers, usually accessed via the `<` operator.
`a < b` when `a + 1 ≤ b`.
-/
protected def lt (a b : Int) : Prop := (a + 1) b
instance instLTInt : LT Int where
lt := Int.lt
set_option bootstrap.genMatcherCode false in
/-- Decides equality between two `Int`s.
/--
Decides whether two integers are equal. Usually accessed via the `DecidableEq Int` instance.
```
#eval (7 : Int) = (3 : Int) + (4 : Int) -- true
#eval (6 : Int) = (3 : Int) * (2 : Int) -- true
#eval ¬ (6 : Int) = (3 : Int) -- true
```
This function is overridden by the compiler with an efficient implementation. This definition is the
logical model.
Implemented by efficient native code. -/
Examples:
* `show (7 : Int) = (3 : Int) + (4 : Int) by decide`
* `if (6 : Int) = (3 : Int) * (2 : Int) then "yes" else "no" = "yes"`
* `(¬ (6 : Int) = (3 : Int)) = true`
-/
@[extern "lean_int_dec_eq"]
protected def decEq (a b : @& Int) : Decidable (a = b) :=
match a, b with
@@ -206,6 +251,7 @@ protected def decEq (a b : @& Int) : Decidable (a = b) :=
| isTrue h => isTrue <| h rfl
| isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h)
@[inherit_doc Int.decEq]
instance : DecidableEq Int := Int.decEq
set_option bootstrap.genMatcherCode false in
@@ -251,15 +297,17 @@ instance decLt (a b : @& Int) : Decidable (a < b) :=
decNonneg _
set_option bootstrap.genMatcherCode false in
/-- Absolute value (`Nat`) of an integer.
/--
The absolute value of an integer is its distance from `0`.
```
#eval (7 : Int).natAbs -- 7
#eval (0 : Int).natAbs -- 0
#eval (-11 : Int).natAbs -- 11
```
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Implemented by efficient native code. -/
Examples:
* `(7 : Int).natAbs = 7`
* `(0 : Int).natAbs = 0`
* `((-11 : Int).natAbs = 11`
-/
@[extern "lean_nat_abs"]
def natAbs (m : @& Int) : Nat :=
match m with
@@ -269,8 +317,17 @@ def natAbs (m : @& Int) : Nat :=
/-! ## sign -/
/--
Returns the "sign" of the integer as another integer: `1` for positive numbers,
`-1` for negative numbers, and `0` for `0`.
Returns the sign of the integer as another integer:
* `1` for positive numbers,
* `-1` for negative numbers, and
* `0` for `0`.
Examples:
* `Int.sign 34 = 1`
* `Int.sign 2 = 1`
* `Int.sign 0 = 0`
* `Int.sign -1 = -1`
* `Int.sign -362 = -1`
-/
def sign : Int Int
| Int.ofNat (succ _) => 1
@@ -279,22 +336,25 @@ def sign : Int → Int
/-! ## Conversion -/
/-- Turns an integer into a natural number, negative numbers become
`0`.
/--
Converts an integer into a natural number. Negative numbers are converted to `0`.
```
#eval (7 : Int).toNat -- 7
#eval (0 : Int).toNat -- 0
#eval (-7 : Int).toNat -- 0
```
Examples:
* `(7 : Int).toNat = 7`
* `(0 : Int).toNat = 0`
* `(-7 : Int).toNat = 0`
-/
def toNat : Int Nat
| ofNat n => n
| negSucc _ => 0
/--
* If `n : Nat`, then `Int.toNat? n = some n`
* If `n : Int` is negative, then `Int.toNat? n = none`.
Converts an integer into a natural number. Returns `none` for negative numbers.
Examples:
* `(7 : Int).toNat? = some 7`
* `(0 : Int).toNat? = some 0`
* `(-7 : Int).toNat? = none`
-/
def toNat? : Int Option Nat
| (n : Nat) => some n
@@ -314,14 +374,14 @@ instance : Dvd Int where
/-! ## Powers -/
/-- Power of an integer to some natural number.
/--
Power of an integer to a natural number, usually accessed via the `^` operator.
```
#eval (2 : Int) ^ 4 -- 16
#eval (10 : Int) ^ 0 -- 1
#eval (0 : Int) ^ 10 -- 0
#eval (-7 : Int) ^ 3 -- -343
```
Examples:
* `(2 : Int) ^ 4 = 16`
* `(10 : Int) ^ 0 = 1`
* `(0 : Int) ^ 10 = 0`
* `(-7 : Int) ^ 3 = -343`
-/
protected def pow (m : Int) : Nat Int
| 0 => 1

View File

@@ -12,15 +12,14 @@ namespace Int
/-! ## bit operations -/
/--
Bitwise not
Bitwise not, usually accessed via the `~~~` prefix operator.
Interprets the integer as an infinite sequence of bits in two's complement
and complements each bit.
```
~~~(0:Int) = -1
~~~(1:Int) = -2
~~~(-1:Int) = 0
```
Interprets the integer as an infinite sequence of bits in two's complement and complements each bit.
Examples:
* `~~~(0 : Int) = -1`
* `~~~(1 : Int) = -2`
* `~~~(-1 : Int) = 0`
-/
protected def not : Int Int
| Int.ofNat n => Int.negSucc n
@@ -29,17 +28,16 @@ protected def not : Int → Int
instance : Complement Int := .not
/--
Bitwise shift right.
Bitwise right shift, usually accessed via the `>>>` operator.
Conceptually, this treats the integer as an infinite sequence of bits in two's
complement and shifts the value to the right.
Interprets the integer as an infinite sequence of bits in two's complement and shifts the value to
the right.
```lean
( 0b0111:Int) >>> 1 = 0b0011
( 0b1000:Int) >>> 1 = 0b0100
(-0b1000:Int) >>> 1 = -0b0100
(-0b0111:Int) >>> 1 = -0b0100
```
Examples:
* `( 0b0111 : Int) >>> 1 = 0b0011`
* `( 0b1000 : Int) >>> 1 = 0b0100`
* `(-0b1000 : Int) >>> 1 = -0b0100`
* `(-0b0111 : Int) >>> 1 = -0b0100`
-/
protected def shiftRight : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n >>> s)

View File

@@ -22,7 +22,7 @@ In early versions of Lean, the typeclasses provided by `/` and `%`
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.
However we decided it was better to use `ediv` and `emod` for the default typeclass instances,
as they are consistent with the conventions used in SMTLib, and Mathlib,
as they are consistent with the conventions used in SMT-LIB, and Mathlib,
and often mathematical reasoning is easier with these conventions.
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
@@ -38,31 +38,29 @@ This pair satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`.
-/
/--
Integer division. This version of integer division uses the E-rounding convention
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x` for `y ≠ 0`.
Integer division that uses the E-rounding convention. Usually accessed via the `/` operator.
Division by zero is defined to be zero, rather than an error.
This means that `Int.ediv x y = floor (x / y)` when `y > 0` and `Int.ediv x y = ceil (x / y)` when `y < 0`.
In the E-rounding convention (Euclidean division), `Int.emod x y` satisfies `0 ≤ Int.emod x y < Int.natAbs y`
for `y ≠ 0` and `Int.ediv` is the unique function satisfying `Int.emod x y + (Int.edivx y) * y = x`
for `y ≠ 0`.
This is the function powering the `/` notation on integers.
This means that `Int.ediv x y` is `⌊x / y⌋` when `y > 0` and `⌈x / y⌉` when `y < 0`.
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Examples:
```
#eval (7 : Int) / (0 : Int) -- 0
#eval (0 : Int) / (7 : Int) -- 0
#eval (12 : Int) / (6 : Int) -- 2
#eval (12 : Int) / (-6 : Int) -- -2
#eval (-12 : Int) / (6 : Int) -- -2
#eval (-12 : Int) / (-6 : Int) -- 2
#eval (12 : Int) / (7 : Int) -- 1
#eval (12 : Int) / (-7 : Int) -- -1
#eval (-12 : Int) / (7 : Int) -- -2
#eval (-12 : Int) / (-7 : Int) -- 2
```
Implemented by efficient native code.
* `(7 : Int) / (0 : Int) = 0`
* `(0 : Int) / (7 : Int) = 0`
* `(12 : Int) / (6 : Int) = 2`
* `(12 : Int) / (-6 : Int) = -2`
* `(-12 : Int) / (6 : Int) = -2`
* `(-12 : Int) / (-6 : Int) = 2`
* `(12 : Int) / (7 : Int) = 1`
* `(12 : Int) / (-7 : Int) = -1`
* `(-12 : Int) / (7 : Int) = -2`
* `(-12 : Int) / (-7 : Int) = 2`
-/
@[extern "lean_int_ediv"]
def ediv : (@& Int) (@& Int) Int
@@ -73,29 +71,26 @@ def ediv : (@& Int) → (@& Int) → Int
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
/--
Integer modulus. This version of integer modulus uses the E-rounding convention
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
Integer modulus that uses the E-rounding convention. Usually accessed via the `%` operator.
This is the function powering the `%` notation on integers.
In the E-rounding convention (Euclidean division), `Int.emod x y` satisfies `0 ≤ Int.emod x y < Int.natAbs y`
for `y ≠ 0` and `Int.ediv` is the unique function satisfying `Int.emod x y + (Int.edivx y) * y = x`
for `y ≠ 0`.
This function is overridden by the compiler with an efficient implementation. This definition is
the logical model.
Examples:
```
#eval (7 : Int) % (0 : Int) -- 7
#eval (0 : Int) % (7 : Int) -- 0
#eval (12 : Int) % (6 : Int) -- 0
#eval (12 : Int) % (-6 : Int) -- 0
#eval (-12 : Int) % (6 : Int) -- 0
#eval (-12 : Int) % (-6 : Int) -- 0
#eval (12 : Int) % (7 : Int) -- 5
#eval (12 : Int) % (-7 : Int) -- 5
#eval (-12 : Int) % (7 : Int) -- 2
#eval (-12 : Int) % (-7 : Int) -- 2
```
Implemented by efficient native code.
* `(7 : Int) % (0 : Int) = 7`
* `(0 : Int) % (7 : Int) = 0`
* `(12 : Int) % (6 : Int) = 0`
* `(12 : Int) % (-6 : Int) = 0`
* `(-12 : Int) % (6 : Int) = 0`
* `(-12 : Int) % (-6 : Int) = 0`
* `(12 : Int) % (7 : Int) = 5`
* `(12 : Int) % (-7 : Int) = 5`
* `(-12 : Int) % (7 : Int) = 2`
* `(-12 : Int) % (-7 : Int) = 2`
-/
@[extern "lean_int_emod"]
def emod : (@& Int) (@& Int) Int
@@ -103,11 +98,15 @@ def emod : (@& Int) → (@& Int) → Int
| -[m+1], n => subNatNat (natAbs n) (succ (m % natAbs n))
/--
The Div and Mod syntax uses ediv and emod for compatibility with SMTLIb and mathematical
reasoning tends to be easier.
The `Div Int` and `Mod Int` instances use `Int.ediv` and `Int.emod` for compatibility with SMT-LIB and
because mathematical reasoning tends to be easier.
-/
instance : Div Int where
div := Int.ediv
/--
The `Div Int` and `Mod Int` instances use `Int.ediv` and `Int.emod` for compatibility with SMT-LIB and
because mathematical reasoning tends to be easier.
-/
instance : Mod Int where
mod := Int.emod
@@ -124,35 +123,27 @@ theorem negSucc_emod_negSucc {a b : Nat} : -[a+1] % -[b+1] = subNatNat (b + 1) (
/-! ### T-rounding division -/
/--
`tdiv` uses the [*"T-rounding"*][t-rounding]
(**T**runcation-rounding) convention, meaning that it rounds toward
zero. Also note that division by zero is defined to equal zero.
Integer division using the T-rounding convention.
The relation between integer division and modulo is found in
`Int.tmod_add_tdiv` which states that
`tmod a b + b * (tdiv a b) = a`, unconditionally.
In [the T-rounding convention][t-rounding] (division with truncation), all rounding is towards zero.
Division by 0 is defined to be 0. In this convention, `Int.tmod a b + b * (Int.tdiv a b) = a`.
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
Examples:
This function is overridden by the compiler with an efficient implementation. This definition is the
logical model.
```
#eval (7 : Int).tdiv (0 : Int) -- 0
#eval (0 : Int).tdiv (7 : Int) -- 0
#eval (12 : Int).tdiv (6 : Int) -- 2
#eval (12 : Int).tdiv (-6 : Int) -- -2
#eval (-12 : Int).tdiv (6 : Int) -- -2
#eval (-12 : Int).tdiv (-6 : Int) -- 2
#eval (12 : Int).tdiv (7 : Int) -- 1
#eval (12 : Int).tdiv (-7 : Int) -- -1
#eval (-12 : Int).tdiv (7 : Int) -- -1
#eval (-12 : Int).tdiv (-7 : Int) -- 1
```
Implemented by efficient native code.
Examples:
* `(7 : Int).tdiv (0 : Int) = 0`
* `(0 : Int).tdiv (7 : Int) = 0`
* `(12 : Int).tdiv (6 : Int) = 2`
* `(12 : Int).tdiv (-6 : Int) = -2`
* `(-12 : Int).tdiv (6 : Int) = -2`
* `(-12 : Int).tdiv (-6 : Int) = 2`
* `(12 : Int).tdiv (7 : Int) = 1`
* `(12 : Int).tdiv (-7 : Int) = -1`
* `(-12 : Int).tdiv (7 : Int) = -1`
* `(-12 : Int).tdiv (-7 : Int) = 1`
-/
@[extern "lean_int_div"]
def tdiv : (@& Int) (@& Int) Int
@@ -161,36 +152,32 @@ def tdiv : (@& Int) → (@& Int) → Int
| -[m +1], ofNat n => -ofNat (succ m / n)
| -[m +1], -[n +1] => ofNat (succ m / succ n)
/-- Integer modulo. This function uses the
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
to pair with `Int.tdiv`, meaning that `tmod a b + b * (tdiv a b) = a`
unconditionally (see [`Int.tmod_add_tdiv`][theo tmod_add_tdiv]). In
particular, `a % 0 = a`.
/-- Integer modulo using the T-rounding convention.
`tmod` satisfies `natAbs (tmod a b) = natAbs a % natAbs b`,
and when `b` does not divide `a`, `tmod a b` has the same sign as `a`.
In [the T-rounding convention][t-rounding] (division with truncation), all rounding is towards zero.
Division by 0 is defined to be 0 and `Int.tmod a 0 = a`.
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
In this convention, `Int.tmod a b + b * (Int.tdiv a b) = a`. Additionally,
`Int.natAbs (Int.tmod a b) = Int.natAbs a % Int.natAbs b`, and when `b` does not divide `a`,
`Int.tmod a b` has the same sign as `a`.
Examples:
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
```
#eval (7 : Int).tmod (0 : Int) -- 7
#eval (0 : Int).tmod (7 : Int) -- 0
This function is overridden by the compiler with an efficient implementation. This definition is the
logical model.
#eval (12 : Int).tmod (6 : Int) -- 0
#eval (12 : Int).tmod (-6 : Int) -- 0
#eval (-12 : Int).tmod (6 : Int) -- 0
#eval (-12 : Int).tmod (-6 : Int) -- 0
#eval (12 : Int).tmod (7 : Int) -- 5
#eval (12 : Int).tmod (-7 : Int) -- 5
#eval (-12 : Int).tmod (7 : Int) -- -5
#eval (-12 : Int).tmod (-7 : Int) -- -5
```
Implemented by efficient native code. -/
Examples:
* `(7 : Int).tmod (0 : Int) = 7`
* `(0 : Int).tmod (7 : Int) = 0`
* `(12 : Int).tmod (6 : Int) = 0`
* `(12 : Int).tmod (-6 : Int) = 0`
* `(-12 : Int).tmod (6 : Int) = 0`
* `(-12 : Int).tmod (-6 : Int) = 0`
* `(12 : Int).tmod (7 : Int) = 5`
* `(12 : Int).tmod (-7 : Int) = 5`
* `(-12 : Int).tmod (7 : Int) = -5`
* `(-12 : Int).tmod (-7 : Int) = -5`
-/
@[extern "lean_int_mod"]
def tmod : (@& Int) (@& Int) Int
| ofNat m, ofNat n => ofNat (m % n)
@@ -205,25 +192,22 @@ This pair satisfies `fdiv x y = floor (x / y)`.
-/
/--
Integer division. This version of division uses the F-rounding convention
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
Integer division using the F-rounding convention.
In the F-rounding convention (flooring division), `Int.fdiv x y` satisfies `Int.fdiv x y = ⌊x / y⌋`
and `Int.fmod` is the unique function satisfying `Int.fmod x y + (Int.fdiv x y) * y = x`.
Examples:
```
#eval (7 : Int).fdiv (0 : Int) -- 0
#eval (0 : Int).fdiv (7 : Int) -- 0
#eval (12 : Int).fdiv (6 : Int) -- 2
#eval (12 : Int).fdiv (-6 : Int) -- -2
#eval (-12 : Int).fdiv (6 : Int) -- -2
#eval (-12 : Int).fdiv (-6 : Int) -- 2
#eval (12 : Int).fdiv (7 : Int) -- 1
#eval (12 : Int).fdiv (-7 : Int) -- -2
#eval (-12 : Int).fdiv (7 : Int) -- -2
#eval (-12 : Int).fdiv (-7 : Int) -- 1
```
* `(7 : Int).fdiv (0 : Int) = 0`
* `(0 : Int).fdiv (7 : Int) = 0`
* `(12 : Int).fdiv (6 : Int) = 2`
* `(12 : Int).fdiv (-6 : Int) = -2`
* `(-12 : Int).fdiv (6 : Int) = -2`
* `(-12 : Int).fdiv (-6 : Int) = 2`
* `(12 : Int).fdiv (7 : Int) = 1`
* `(12 : Int).fdiv (-7 : Int) = -2`
* `(-12 : Int).fdiv (7 : Int) = -2`
* `(-12 : Int).fdiv (-7 : Int) = 1`
-/
def fdiv : Int Int Int
| 0, _ => 0
@@ -234,26 +218,26 @@ def fdiv : Int → Int → Int
| -[m+1], -[n+1] => ofNat (succ m / succ n)
/--
Integer modulus. This version of integer modulus uses the F-rounding convention
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
Integer modulus using the F-rounding convention.
In the F-rounding convention (flooring division), `Int.fdiv x y` satisfies `Int.fdiv x y = ⌊x / y⌋`
and `Int.fmod` is the unique function satisfying `Int.fmod x y + (Int.fdiv x y) * y = x`.
Examples:
```
#eval (7 : Int).fmod (0 : Int) -- 7
#eval (0 : Int).fmod (7 : Int) -- 0
* `(7 : Int).fmod (0 : Int) = 7`
* `(0 : Int).fmod (7 : Int) = 0`
#eval (12 : Int).fmod (6 : Int) -- 0
#eval (12 : Int).fmod (-6 : Int) -- 0
#eval (-12 : Int).fmod (6 : Int) -- 0
#eval (-12 : Int).fmod (-6 : Int) -- 0
* `(12 : Int).fmod (6 : Int) = 0`
* `(12 : Int).fmod (-6 : Int) = 0`
* `(-12 : Int).fmod (6 : Int) = 0`
* `(-12 : Int).fmod (-6 : Int) = 0`
* `(12 : Int).fmod (7 : Int) = 5`
* `(12 : Int).fmod (-7 : Int) = -2`
* `(-12 : Int).fmod (7 : Int) = 2`
* `(-12 : Int).fmod (-7 : Int) = -5`
#eval (12 : Int).fmod (7 : Int) -- 5
#eval (12 : Int).fmod (-7 : Int) -- -2
#eval (-12 : Int).fmod (7 : Int) -- 2
#eval (-12 : Int).fmod (-7 : Int) -- -5
```
-/
def fmod : Int Int Int
| 0, _ => 0
@@ -280,28 +264,24 @@ This function is used in `omega` as well as signed bitvectors.
-/
/--
Balanced modulus. This version of integer modulus uses the
balanced rounding convention, which guarantees that
`-m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
to `x` modulo `m`.
Balanced modulus.
If `m = 0`, then `bmod x m = x`.
This version of integer modulus uses the balanced rounding convention, which guarantees that
`-m / 2 ≤ Int.bmod x m < m/2` for `m ≠ 0` and `Int.bmod x m` is congruent to `x` modulo `m`.
If `m = 0`, then `Int.bmod x m = x`.
Examples:
```
#eval (7 : Int).bdiv 0 -- 0
#eval (0 : Int).bdiv 7 -- 0
#eval (12 : Int).bdiv 6 -- 2
#eval (12 : Int).bdiv 7 -- 2
#eval (12 : Int).bdiv 8 -- 2
#eval (12 : Int).bdiv 9 -- 1
#eval (-12 : Int).bdiv 6 -- -2
#eval (-12 : Int).bdiv 7 -- -2
#eval (-12 : Int).bdiv 8 -- -1
#eval (-12 : Int).bdiv 9 -- -1
```
* `(7 : Int).bmod 0 = 7`
* `(0 : Int).bmod 7 = 0`
* `(12 : Int).bmod 6 = 0`
* `(12 : Int).bmod 7 = -2`
* `(12 : Int).bmod 8 = -4`
* `(12 : Int).bmod 9 = 3`
* `(-12 : Int).bmod 6 = 0`
* `(-12 : Int).bmod 7 = 2`
* `(-12 : Int).bmod 8 = -4`
* `(-12 : Int).bmod 9 = -3`
-/
def bmod (x : Int) (m : Nat) : Int :=
let r := x % m
@@ -311,24 +291,21 @@ def bmod (x : Int) (m : Nat) : Int :=
r - m
/--
Balanced division. This returns the unique integer so that
`b * (Int.bdiv a b) + Int.bmod a b = a`.
Balanced division.
This returns the unique integer so that `b * (Int.bdiv a b) + Int.bmod a b = a`.
Examples:
```
#eval (7 : Int).bmod 0 -- 7
#eval (0 : Int).bmod 7 -- 0
#eval (12 : Int).bmod 6 -- 0
#eval (12 : Int).bmod 7 -- -2
#eval (12 : Int).bmod 8 -- -4
#eval (12 : Int).bmod 9 -- 3
#eval (-12 : Int).bmod 6 -- 0
#eval (-12 : Int).bmod 7 -- 2
#eval (-12 : Int).bmod 8 -- -4
#eval (-12 : Int).bmod 9 -- -3
```
* `(7 : Int).bdiv 0 = 0`
* `(0 : Int).bdiv 7 = 0`
* `(12 : Int).bdiv 6 = 2`
* `(12 : Int).bdiv 7 = 2`
* `(12 : Int).bdiv 8 = 2`
* `(12 : Int).bdiv 9 = 1`
* `(-12 : Int).bdiv 6 = -2`
* `(-12 : Int).bdiv 7 = -2`
* `(-12 : Int).bdiv 8 = -1`
* `(-12 : Int).bdiv 9 = -1`
-/
def bdiv (x : Int) (m : Nat) : Int :=
if m = 0 then

View File

@@ -122,17 +122,15 @@ protected theorem mul_dvd_mul_iff_right {a b c : Int} (h : a ≠ 0) : (b * a)
| ofNat _ => show ofNat _ = _ by simp
| -[_+1] => show -ofNat _ = _ by simp
unseal Nat.div in
@[simp] protected theorem tdiv_zero : a : Int, tdiv a 0 = 0
| ofNat _ => show ofNat _ = _ by simp
| -[_+1] => rfl
| -[_+1] => by simp [tdiv]
@[simp] theorem zero_fdiv (b : Int) : fdiv 0 b = 0 := by cases b <;> rfl
unseal Nat.div in
@[simp] protected theorem fdiv_zero : a : Int, fdiv a 0 = 0
| 0 => rfl
| succ _ => rfl
| succ _ => by simp [fdiv]
| -[_+1] => rfl
/-! ### preliminaries for div equivalences -/
@@ -558,7 +556,7 @@ protected theorem eq_ediv_of_mul_eq_left {a b c : Int}
theorem sign_ediv (a b : Int) : sign (a / b) = if 0 a a < b.natAbs then 0 else sign a * sign b := by
induction b using wlog_sign
case inv => simp; split <;> simp
case inv => simp; split <;> simp [Int.mul_neg]
case w b =>
match b with
| 0 => simp
@@ -699,7 +697,7 @@ protected theorem ediv_emod_unique {a b r q : Int} (h : 0 < b) :
protected theorem ediv_emod_unique' {a b r q : Int} (h : b < 0) :
a / b = q a % b = r r + b * q = a 0 r r < -b := by
have := Int.ediv_emod_unique (a := a) (b := -b) (r := r) (q := -q) (by omega)
simpa [Int.neg_inj]
simpa [Int.neg_inj, Int.neg_mul, Int.mul_neg]
@[simp] theorem mul_emod_mul_of_pos
{a : Int} (b c : Int) (H : 0 < a) : (a * b) % (a * c) = a * (b % c) := by
@@ -1013,11 +1011,11 @@ protected theorem ediv_le_ediv {a b c : Int} (H : 0 < c) (H' : a ≤ b) : a / c
-- `tdiv` analogues of `ediv` lemmas from `Bootstrap.lean`
unseal Nat.div in
@[simp] protected theorem tdiv_neg : a b : Int, a.tdiv (-b) = -(a.tdiv b)
| ofNat m, 0 => show ofNat (m / 0) = -(m / 0) by rw [Nat.div_zero]; rfl
| ofNat _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
| ofNat _, succ _ | -[_+1], 0 | -[_+1], -[_+1] => rfl
| ofNat _, succ _ | -[_+1], 0 => by simp [Int.tdiv, Int.neg_zero, Int.negSucc_eq]
| -[_+1], -[_+1] => by simp only [tdiv, neg_negSucc]
/-!
There are no lemmas
@@ -1112,10 +1110,11 @@ protected theorem eq_tdiv_of_mul_eq_left {a b c : Int}
@[simp] protected theorem tdiv_self {a : Int} (H : a 0) : a.tdiv a = 1 := by
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
unseal Nat.div in
@[simp] protected theorem neg_tdiv : a b : Int, (-a).tdiv b = -(a.tdiv b)
| 0, n => by simp [Int.neg_zero]
| succ _, (n:Nat) | -[_+1], 0 | -[_+1], -[_+1] => rfl
| succ _, (n:Nat) => by simp [tdiv, Int.negSucc_eq]
| -[_+1], 0 | -[_+1], -[_+1] => by
simp only [tdiv, neg_negSucc, Int.natCast_succ, Int.neg_neg]
| succ _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
@@ -1123,10 +1122,10 @@ protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
theorem sign_tdiv (a b : Int) : sign (a.tdiv b) = if natAbs a < natAbs b then 0 else sign a * sign b := by
induction b using wlog_sign
case inv => simp; split <;> simp
case inv => simp; split <;> simp [Int.mul_neg]
case w b =>
induction a using wlog_sign
case inv => simp; split <;> simp
case inv => simp; split <;> simp [Int.neg_mul]
case w a =>
rw [tdiv_eq_ediv_of_nonneg (by simp), sign_ediv]
simp
@@ -1187,9 +1186,9 @@ theorem lt_tmod_of_pos (a : Int) {b : Int} (H : 0 < b) : -b < tmod a b :=
theorem mul_tmod (a b n : Int) : (a * b).tmod n = (a.tmod n * b.tmod n).tmod n := by
induction a using wlog_sign
case inv => simp
case inv => simp [Int.neg_mul]
induction b using wlog_sign
case inv => simp
case inv => simp [Int.mul_neg]
induction n using wlog_sign
case inv => simp
simp only [ Int.natCast_mul, ofNat_tmod]
@@ -1332,14 +1331,14 @@ protected theorem tdiv_tmod_unique {a b r q : Int} (ha : 0 ≤ a) (hb : b ≠ 0)
· replace hb' : 0 < -b := by omega
have := Int.ediv_emod_unique (a := a) (q := -q) (r := r) hb'
simp at this
simp [this]
simp [this, Int.neg_mul, Int.mul_neg]
omega
protected theorem tdiv_tmod_unique' {a b r q : Int} (ha : a 0) (hb : b 0) :
a.tdiv b = q a.tmod b = r r + b * q = a -natAbs b < r r 0 := by
have := Int.tdiv_tmod_unique (a := -a) (q := -q) (r := -r) (by omega) hb
simp at this
simp [this]
simp [this, Int.mul_neg]
omega
@[simp] theorem mul_tmod_mul_of_pos
@@ -1645,9 +1644,9 @@ theorem fdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0
· have : 0 < a / b := ediv_pos_of_neg_of_neg (by omega) (by omega)
split <;> omega
unseal Nat.div in
theorem fdiv_nonpos_of_nonneg_of_nonpos : {a b : Int}, 0 a b 0 a.fdiv b 0
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => _
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => by
simp [fdiv, negSucc_le_zero]
@[deprecated fdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev fdiv_nonpos := @fdiv_nonpos_of_nonneg_of_nonpos
@@ -1972,7 +1971,7 @@ protected theorem fdiv_fmod_unique' {a b r q : Int} (h : b < 0) :
a.fdiv b = q a.fmod b = r r + b * q = a b < r r 0 := by
have := Int.fdiv_fmod_unique (a := -a) (b := -b) (r := -r) (q := q) (by omega)
simp at this
simp [this]
simp [this, Int.neg_mul]
omega
@[simp] theorem mul_fmod_mul_of_pos

View File

@@ -20,7 +20,21 @@ namespace Int
/-! ## gcd -/
/-- Computes the greatest common divisor of two integers, as a `Nat`. -/
/--
Computes the greatest common divisor of two integers as a natural number. The GCD of two integers is
the largest natural number that evenly divides both. However, the GCD of a number and `0` is the
number's absolute value.
This implementation uses `Nat.gcd`, which is overridden in both the kernel and the compiler to
efficiently evaluate using arbitrary-precision arithmetic.
Examples:
* `Int.gcd 10 15 = 5`
* `Int.gcd 10 (-15) = 5`
* `Int.gcd (-6) (-9) = 3`
* `Int.gcd 0 5 = 5`
* `Int.gcd (-7) 0 = 7`
-/
def gcd (m n : Int) : Nat := m.natAbs.gcd n.natAbs
theorem gcd_dvd_left {a b : Int} : (gcd a b : Int) a := by
@@ -41,7 +55,18 @@ theorem gcd_dvd_right {a b : Int} : (gcd a b : Int) b := by
/-! ## lcm -/
/-- Computes the least common multiple of two integers, as a `Nat`. -/
/--
Computes the least common multiple of two integers as a natural number. The LCM of two integers is
the smallest natural number that's evenly divisible by the absolute values of both.
Examples:
* `Int.lcm 9 6 = 18`
* `Int.lcm 9 (-6) = 18`
* `Int.lcm 9 3 = 9`
* `Int.lcm 9 (-3) = 9`
* `Int.lcm 0 3 = 0`
* `Int.lcm (-3) 0 = 0`
-/
def lcm (m n : Int) : Nat := m.natAbs.lcm n.natAbs
theorem lcm_ne_zero (hm : m 0) (hn : n 0) : lcm m n 0 := by

View File

@@ -490,21 +490,23 @@ protected theorem neg_mul_eq_neg_mul (a b : Int) : -(a * b) = -a * b :=
protected theorem neg_mul_eq_mul_neg (a b : Int) : -(a * b) = a * -b :=
Int.neg_eq_of_add_eq_zero <| by rw [ Int.mul_add, Int.add_right_neg, Int.mul_zero]
@[simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
-- Note, this is not a `@[simp]` lemma because it interferes with normalization in `simp +arith`.
protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
(Int.neg_mul_eq_neg_mul a b).symm
@[simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
-- Note, this is not a `@[simp]` lemma because it interferes with normalization in `simp +arith`.
protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
(Int.neg_mul_eq_mul_neg a b).symm
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp [Int.neg_mul, Int.mul_neg]
protected theorem neg_mul_comm (a b : Int) : -a * b = a * -b := by simp
protected theorem neg_mul_comm (a b : Int) : -a * b = a * -b := by simp [Int.neg_mul, Int.mul_neg]
protected theorem mul_sub (a b c : Int) : a * (b - c) = a * b - a * c := by
simp [Int.sub_eq_add_neg, Int.mul_add]
simp [Int.sub_eq_add_neg, Int.mul_add, Int.mul_neg]
protected theorem sub_mul (a b c : Int) : (a - b) * c = a * c - b * c := by
simp [Int.sub_eq_add_neg, Int.add_mul]
simp [Int.sub_eq_add_neg, Int.add_mul, Int.neg_mul]
@[simp] protected theorem one_mul : a : Int, 1 * a = a
| ofNat n => show ofNat (1 * n) = ofNat n by rw [Nat.one_mul]

View File

@@ -361,7 +361,7 @@ theorem Expr.denote_toPoly'_go (ctx : Context) (e : Expr) :
simp only [mul_def, denote]
rw [Int.mul_comm (denote _ _) _]
simpa [Int.mul_assoc] using ih
| case10 k a ih => simp [toPoly'.go, ih]
| case10 k a ih => simp [toPoly'.go, ih, Int.neg_mul, Int.mul_neg]
theorem Expr.denote_norm (ctx : Context) (e : Expr) : e.norm.denote ctx = e.denote ctx := by
simp [norm, toPoly', Expr.denote_toPoly'_go]
@@ -798,7 +798,7 @@ theorem dvd_solve_elim (ctx : Context) (d₁ : Int) (p₁ : Poly) (d₂ : Int) (
simp [dvd_solve_elim_cert]
split <;> simp
next a₁ x₁ p₁ a₂ x₂ p₂ =>
intro _ hd _; subst x₁ p; simp
intro _ hd _; subst x₁ p; simp [Int.neg_mul]
intro h₁ h₂
rw [Int.add_comm] at h₁ h₂
rw [Int.add_neg_eq_sub]
@@ -908,7 +908,7 @@ def Poly.coeff (p : Poly) (x : Var) : Int :=
| .num _ => 0
private theorem eq_add_coeff_insert (ctx : Context) (p : Poly) (x : Var) : p.denote ctx = (p.coeff x) * (x.denote ctx) + (p.insert (-p.coeff x) x).denote ctx := by
simp; rw [ Int.add_assoc, Int.add_neg_cancel_right]
simp; rw [ Int.add_assoc, Int.neg_mul, Int.add_neg_cancel_right]
private theorem dvd_of_eq' {a x p : Int} : a*x + p = 0 a p := by
intro h
@@ -977,7 +977,7 @@ theorem eq_dvd_subst (ctx : Context) (x : Var) (p₁ : Poly) (d₂ : Int) (p₂
have := eq_dvd_subst' h₁ h₂
rw [Int.sub_eq_add_neg, Int.add_comm] at this
apply abs_dvd
simp [this]
simp [this, Int.neg_mul]
def eq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
@@ -1017,7 +1017,7 @@ theorem eq_le_subst_nonpos (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly)
intro h
intro; subst p₃
intro h₁ h₂
simp [*, -Int.neg_nonpos_iff]
simp [*, -Int.neg_nonpos_iff, Int.neg_mul]
replace h₂ := Int.mul_le_mul_of_nonpos_left h₂ h; simp at h₂; clear h
rw [Int.mul_comm]
assumption
@@ -1658,6 +1658,29 @@ theorem emod_le (x y : Int) (n : Int) : emod_le_cert y n → x % y + n ≤ 0 :=
theorem natCast_nonneg (x : Nat) : (-1:Int) * NatCast.natCast x 0 := by
simp
theorem natCast_sub (x y : Nat)
: (NatCast.natCast (x - y) : Int)
=
if (NatCast.natCast y : Int) + (-1)*NatCast.natCast x 0 then
(NatCast.natCast x : Int) + -1*NatCast.natCast y
else
(0 : Int) := by
show ((x - y) : Int) = if (y : Int) + (-1)*x 0 then x + (-1)*y else 0
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
split
next h =>
replace h := Int.le_of_sub_nonpos h
rw [Int.ofNat_le] at h
rw [Int.ofNat_sub h]
next h =>
have : ¬ (y : Int) x := by
intro h
replace h := Int.sub_nonpos_of_le h
contradiction
rw [Int.ofNat_le] at this
rw [Lean.Omega.Int.ofNat_sub_eq_zero this]
private theorem dvd_le_tight' {d p b₁ b₂ : Int} (hd : d > 0) (h₁ : d p + b₁) (h₂ : p + b₂ 0)
: p + (b₁ - d*((b₁-b₂) / d)) 0 := by
have k, h := h₁
@@ -1757,6 +1780,22 @@ theorem dvd_norm_expr (ctx : Context) (d : Int) (e : Expr) (p : Poly)
: p == e.norm d e.denote ctx d p.denote' ctx := by
simp; intro; subst p; simp
theorem eq_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: norm_eq_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote' ctx = 0 := by
intro h₁ h₂; rwa [norm_eq ctx lhs rhs p h₁] at h₂
theorem not_eq_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: norm_eq_cert lhs rhs p ¬ lhs.denote ctx = rhs.denote ctx ¬ p.denote' ctx = 0 := by
simp [norm_eq_cert]
intro; subst p; simp
intro; rwa [Int.sub_eq_zero]
theorem of_not_dvd (a b : Int) : a != 0 ¬ (a b) b % a > 0 := by
simp; intro h₁ h₂
replace h₂ := Int.emod_pos_of_not_dvd h₂
simp [h₁] at h₂
assumption
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by

View File

@@ -67,4 +67,24 @@ theorem of_dvd (ctx : Context) (d : Nat) (e : Expr)
: d e.denote ctx Int.ofNat d e.denoteAsInt ctx := by
simp [Expr.denoteAsInt_eq, Int.ofNat_dvd]
theorem of_eq (ctx : Context) (lhs rhs : Expr)
: lhs.denote ctx = rhs.denote ctx lhs.denoteAsInt ctx = rhs.denoteAsInt ctx := by
rw [Expr.eq ctx lhs rhs]; simp
theorem of_not_eq (ctx : Context) (lhs rhs : Expr)
: ¬ lhs.denote ctx = rhs.denote ctx ¬ lhs.denoteAsInt ctx = rhs.denoteAsInt ctx := by
rw [Expr.eq ctx lhs rhs]; simp
theorem ofNat_toNat (a : Int) : (NatCast.natCast a.toNat : Int) = if a 0 then 0 else a := by
split
next h =>
rw [Int.toNat_of_nonpos h]; rfl
next h =>
simp at h
have := Int.toNat_of_nonneg (Int.le_of_lt h)
assumption
theorem Expr.denoteAsInt_nonneg (ctx : Context) (e : Expr) : e.denoteAsInt ctx 0 := by
simp [Expr.denoteAsInt_eq]
end Int.OfNat

View File

@@ -41,7 +41,7 @@ elements in the corresponding subtype `{ x // P x }`.
(l : List α) (P : α Prop) (H : x l, P x) : List {x // P x} := pmap Subtype.mk l H
/--
"Attaches" the proof that the elements of `l` are in fact elements of `l`, producing a new list with
Attaches the proof that the elements of `l` are in fact elements of `l`, producing a new list with
the same elements but in the subtype `{ x // x ∈ l }`.
`O(1)`.

View File

@@ -259,7 +259,7 @@ instance decidableLE [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List
inferInstanceAs (Decidable (Not _))
/--
Compare lists lexicographically with respect to a comparison on their elements.
Compares lists lexicographically with respect to a comparison on their elements.
The lexicographic order with respect to `lt` is:
* `[].lex (b :: bs)` is `true`
@@ -493,7 +493,7 @@ Returns the list of elements in `l` for which `p` returns `true`.
`O(|l|)`.
Examples:
* `[1, 2, 5, 2, 7, 7].filter (· > 2) = [5, 7, 7]`
* `[1, 2, 5, 2, 7, 7].filter (· > 2) = [5, 7, 7]`
* `[1, 2, 5, 2, 7, 7].filter (fun _ => false) = []`
* `[1, 2, 5, 2, 7, 7].filter (fun _ => true) = [1, 2, 5, 2, 7, 7]`
-/
@@ -766,10 +766,10 @@ Pads `l : List α` on the left with repeated occurrences of `a : α` until it is
already has at least `n` elements, it is returned unmodified.
Examples:
* `[1, 2, 3].leftPad 5 0 = [0, 0, 1, 2, 3]`
* `["red", "green", "blue"].leftPad 4 "blank" = ["blank", "red", "green", "blue"]`
* `["red", "green", "blue"].leftPad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].leftPad 1 "blank" = ["red", "green", "blue"]`
* `[1, 2, 3].leftpad 5 0 = [0, 0, 1, 2, 3]`
* `["red", "green", "blue"].leftpad 4 "blank" = ["blank", "red", "green", "blue"]`
* `["red", "green", "blue"].leftpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].leftpad 1 "blank" = ["red", "green", "blue"]`
-/
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
@@ -779,10 +779,10 @@ Pads `l : List α` on the right with repeated occurrences of `a : α` until it i
`l` already has at least `n` elements, it is returned unmodified.
Examples:
* `[1, 2, 3].rightPad 5 0 = [1, 2, 3, 0, 0]`
* `["red", "green", "blue"].rightPad 4 "blank" = ["red", "green", "blue", "blank"]`
* `["red", "green", "blue"].rightPad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].rightPad 1 "blank" = ["red", "green", "blue"]`
* `[1, 2, 3].rightpad 5 0 = [1, 2, 3, 0, 0]`
* `["red", "green", "blue"].rightpad 4 "blank" = ["red", "green", "blue", "blank"]`
* `["red", "green", "blue"].rightpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].rightpad 1 "blank" = ["red", "green", "blue"]`
-/
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
@@ -1451,25 +1451,25 @@ using `f` if the index is larger than the length of the List.
Examples:
```lean example
["circle", "square", "triangle"].modifyTailIdx List.reverse 1
["circle", "square", "triangle"].modifyTailIdx 1 List.reverse
```
```output
["circle", "triangle", "square"]
```
```lean example
["circle", "square", "triangle"].modifyTailIdx (fun xs => xs ++ xs) 1
["circle", "square", "triangle"].modifyTailIdx 1 (fun xs => xs ++ xs)
```
```output
["circle", "square", "triangle", "square", "triangle"]
```
```lean example
["circle", "square", "triangle"].modifyTailIdx (fun xs => xs ++ xs) 2
["circle", "square", "triangle"].modifyTailIdx 2 (fun xs => xs ++ xs)
```
```output
["circle", "square", "triangle", "triangle"]
```
```lean example
["circle", "square", "triangle"].modifyTailIdx (fun xs => xs ++ xs) 5
["circle", "square", "triangle"].modifyTailIdx 5 (fun xs => xs ++ xs)
```
```output
["circle", "square", "triangle"]
@@ -1509,9 +1509,9 @@ Replaces the element at the given index, if it exists, with the result of applyi
index is invalid, the list is returned unmodified.
Examples:
* `[1, 2, 3].modify (· * 10) 0 = [10, 2, 3]`
* `[1, 2, 3].modify (· * 10) 2 = [1, 2, 30]`
* `[1, 2, 3].modify (· * 10) 3 = [1, 2, 3]`
* `[1, 2, 3].modify 0 (· * 10) = [10, 2, 3]`
* `[1, 2, 3].modify 2 (· * 10) = [1, 2, 30]`
* `[1, 2, 3].modify 3 (· * 10) = [1, 2, 3]`
-/
def modify (l : List α) (i : Nat) (f : α α) : List α :=
l.modifyTailIdx i (modifyHead f)
@@ -1702,7 +1702,6 @@ Examples:
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = some 4`
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = none`
-/
def findIdx? (p : α Bool) (l : List α) : Option Nat :=
go l 0
where
@@ -1866,7 +1865,7 @@ def isPerm [BEq α] : List α → List α → Bool
/-! ### any -/
/--
Returns true if `p` returns `true` for any element of `l`.
Returns `true` if `p` returns `true` for any element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `true`.
@@ -1886,7 +1885,7 @@ def any : (l : List α) → (p : α → Bool) → Bool
/-! ### all -/
/--
Returns true if `p` returns `true` for every element of `l`.
Returns `true` if `p` returns `true` for every element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `false`.
@@ -1966,7 +1965,7 @@ Examples:
/--
Combines two lists into a list of pairs in which the first and second components are the
corresponding elements of each list. The resulting list is the length of the shorter of the inputs
corresponding elements of each list. The resulting list is the length of the shorter of the input
lists.
`O(min |xs| |ys|)`.

View File

@@ -46,7 +46,7 @@ Users that want to use `mapM` with `Applicative` should use `mapA` instead.
-/
/--
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
Applies the monadic action `f` to every element in the list, left-to-right, and returns the list of
results.
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
@@ -243,7 +243,7 @@ def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s
/--
Folds a monadic function over a list from the right, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in order, using `f`.
accumulated value is combined with the each element of the list in reverse order, using `f`.
Example:
```lean example

View File

@@ -90,7 +90,7 @@ theorem countP_le_length : countP p l ≤ l.length := by
simp only [countP_eq_length_filter, length_eq_zero_iff, filter_eq_nil_iff]
@[simp] theorem countP_eq_length {p} : countP p l = l.length a l, p a := by
rw [countP_eq_length_filter, filter_length_eq_length]
rw [countP_eq_length_filter, length_filter_eq_length_iff]
theorem countP_replicate (p : α Bool) (a : α) (n : Nat) :
countP p (replicate n a) = if p a then n else 0 := by

View File

@@ -15,8 +15,8 @@ namespace List
Lists all elements of `Fin n` in order, starting at `0`.
Examples:
* `List.finRange 0 = ([] : List Fin 0)`
* `List.finRange 2 = ([0, 1] : List Fin 2)`
* `List.finRange 0 = ([] : List (Fin 0))`
* `List.finRange 2 = ([0, 1] : List (Fin 2))`
-/
def finRange (n : Nat) : List (Fin n) := ofFn fun i => i

View File

@@ -540,11 +540,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_getElem (since := "2024-08-12")]
theorem findIdx_get {xs : List α} {w : xs.findIdx p < xs.length} :
p (xs.get xs.findIdx p, w) :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
theorem findIdx_lt_length_of_exists {xs : List α} (h : x xs, p x) :
xs.findIdx p < xs.length := by
induction xs with

View File

@@ -297,9 +297,9 @@ Replaces the element at the given index, if it exists, with the result of applyi
This is a tail-recursive version of `List.modify`.
Examples:
* `[1, 2, 3].modifyTR (· * 10) 0 = [10, 2, 3]`
* `[1, 2, 3].modifyTR (· * 10) 2 = [1, 2, 30]`
* `[1, 2, 3].modifyTR (· * 10) 3 = [1, 2, 3]`
* `[1, 2, 3].modifyTR 0 (· * 10) = [10, 2, 3]`
* `[1, 2, 3].modifyTR 2 (· * 10) = [1, 2, 30]`
* `[1, 2, 3].modifyTR 3 (· * 10) = [1, 2, 3]`
-/
def modifyTR (l : List α) (i : Nat) (f : α α) : List α := go l i #[] where
/-- Auxiliary for `modifyTR`: `modifyTR.go f l i acc = acc.toList ++ modify f i l`. -/

View File

@@ -815,14 +815,6 @@ theorem getElem_length_sub_one_eq_getLast (l : List α) (h : l.length - 1 < l.le
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
rw [ getLast_eq_getElem]
@[deprecated getLast_eq_getElem (since := "2024-07-15")]
theorem getLast_eq_get (l : List α) (h : l []) :
getLast l h = l.get l.length - 1, by
match l with
| [] => contradiction
| a :: l => exact Nat.le_refl _ := by
simp [getLast_eq_getElem]
theorem getLast_cons {a : α} {l : List α} : (h : l nil),
getLast (a :: l) (cons_ne_nil a l) = getLast l h := by
induction l <;> intros; {contradiction}; rfl
@@ -947,9 +939,12 @@ theorem head_eq_iff_head?_eq_some {xs : List α} (h) : xs.head h = a ↔ xs.head
theorem head?_eq_some_iff {xs : List α} {a : α} : xs.head? = some a ys, xs = a :: ys := by
cases xs <;> simp_all
@[simp] theorem head?_isSome : l.head?.isSome l [] := by
@[simp] theorem isSome_head? : l.head?.isSome l [] := by
cases l <;> simp
@[deprecated isSome_head? (since := "2025-03-18")]
abbrev head?_isSome := @isSome_head?
@[simp] theorem head_mem : {l : List α} (h : l []), head l h l
| [], h => absurd rfl h
| _::_, _ => .head ..
@@ -1098,8 +1093,6 @@ theorem forall_mem_map {f : α → β} {l : List α} {P : β → Prop} :
( (i) (_ : i l.map f), P i) (j) (_ : j l), P (f j) := by
simp
@[deprecated forall_mem_map (since := "2024-07-25")] abbrev forall_mem_map_iff := @forall_mem_map
@[simp] theorem map_eq_nil_iff {f : α β} {l : List α} : map f l = [] l = [] := by
constructor <;> exact fun _ => match l with | [] => rfl
@@ -1256,7 +1249,7 @@ theorem filter_eq_self {l} : filter p l = l ↔ ∀ a ∈ l, p a := by
intro h; exact Nat.lt_irrefl _ (h length_filter_le p l)
@[simp]
theorem filter_length_eq_length {l} : (filter p l).length = l.length a l, p a := by
theorem length_filter_eq_length_iff {l} : (filter p l).length = l.length a l, p a := by
induction l with
| nil => simp
| cons a l ih =>
@@ -1266,6 +1259,9 @@ theorem filter_length_eq_length {l} : (filter p l).length = l.length ↔ ∀ a
· have := Nat.ne_of_lt (Nat.lt_succ.mpr (length_filter_le p l))
simp_all
@[deprecated length_filter_eq_length_iff (since := "2024-09-05")]
abbrev filter_length_eq_length := @length_filter_eq_length_iff
@[simp] theorem mem_filter : x filter p as x as p x := by
induction as with
| nil => simp [filter]
@@ -1283,8 +1279,6 @@ theorem forall_mem_filter {l : List α} {p : α → Bool} {P : α → Prop} :
( (i) (_ : i l.filter p), P i) (j) (_ : j l), p j P j := by
simp
@[deprecated forall_mem_filter (since := "2024-07-25")] abbrev forall_mem_filter_iff := @forall_mem_filter
@[simp] theorem filter_filter (q) : l, filter p (filter q l) = filter (fun a => p a && q a) l
| [] => rfl
| a :: l => by by_cases hp : p a <;> by_cases hq : q a <;> simp [hp, hq, filter_filter _ l]
@@ -1457,8 +1451,6 @@ theorem forall_mem_filterMap {f : α → Option β} {l : List α} {P : β → Pr
intro a
rw [forall_comm]
@[deprecated forall_mem_filterMap (since := "2024-07-25")] abbrev forall_mem_filterMap_iff := @forall_mem_filterMap
@[simp] theorem filterMap_append {α β : Type _} (l l' : List α) (f : α Option β) :
filterMap f (l ++ l') = filterMap f l ++ filterMap f l' := by
induction l <;> simp [filterMap_cons]; split <;> simp [*]
@@ -1659,29 +1651,18 @@ theorem getLast_concat {a : α} : ∀ (l : List α), getLast (l ++ [a]) (by simp
@[simp] theorem nil_eq_append_iff : [] = a ++ b a = [] b = [] := by
rw [eq_comm, append_eq_nil_iff]
@[deprecated nil_eq_append_iff (since := "2024-07-24")] abbrev nil_eq_append := @nil_eq_append_iff
theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s []) (t : List α) : s ++ t [] := by simp_all
theorem append_ne_nil_of_right_ne_nil (s : List α) : t [] s ++ t [] := by simp_all
@[deprecated append_ne_nil_of_left_ne_nil (since := "2024-07-24")]
theorem append_ne_nil_of_ne_nil_left {s : List α} (h : s []) (t : List α) : s ++ t [] := by simp_all
@[deprecated append_ne_nil_of_right_ne_nil (since := "2024-07-24")]
theorem append_ne_nil_of_ne_nil_right (s : List α) : t [] s ++ t [] := by simp_all
theorem append_eq_cons_iff :
as ++ bs = x :: c (as = [] bs = x :: c) ( as', as = x :: as' c = as' ++ bs) := by
cases as with simp | cons a as => ?_
exact fun h => as, by simp [h], fun as', aeq, aseq, h => aeq, by rw [aseq, h]
@[deprecated append_eq_cons_iff (since := "2024-07-24")] abbrev append_eq_cons := @append_eq_cons_iff
theorem cons_eq_append_iff :
x :: cs = as ++ bs (as = [] bs = x :: cs) ( as', as = x :: as' cs = as' ++ bs) := by
rw [eq_comm, append_eq_cons_iff]
@[deprecated cons_eq_append_iff (since := "2024-07-24")] abbrev cons_eq_append := @cons_eq_append_iff
theorem append_eq_singleton_iff :
a ++ b = [x] (a = [] b = [x]) (a = [x] b = []) := by
cases a <;> cases b <;> simp
@@ -1696,9 +1677,6 @@ theorem append_eq_append_iff {ws xs ys zs : List α} :
| nil => simp_all
| cons a as ih => cases ys <;> simp [eq_comm, and_assoc, ih, and_or_left]
@[deprecated append_inj (since := "2024-07-24")] abbrev append_inj_of_length_left := @append_inj
@[deprecated append_inj' (since := "2024-07-24")] abbrev append_inj_of_length_right := @append_inj'
@[simp] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) :
head (l ++ l') w₁ = head l w₂ := by
match l, w₂ with
@@ -1746,8 +1724,6 @@ theorem tail_append {l l' : List α} : (l ++ l').tail = if l.isEmpty then l'.tai
(xs ++ ys).tail = xs.tail ++ ys := by
simp_all [tail_append]
@[deprecated tail_append_of_ne_nil (since := "2024-07-24")] abbrev tail_append_left := @tail_append_of_ne_nil
theorem set_append {s t : List α} :
(s ++ t).set i x = if i < s.length then s.set i x ++ t else s ++ t.set (i - s.length) x := by
induction s generalizing i with
@@ -2097,8 +2073,6 @@ theorem head?_flatMap {l : List α} {f : α → List β} :
(xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by
induction xs; {rfl}; simp_all [flatMap_cons, append_assoc]
@[deprecated flatMap_append (since := "2024-07-24")] abbrev append_bind := @flatMap_append
theorem flatMap_assoc {α β} (l : List α) (f : α List β) (g : β List γ) :
(l.flatMap f).flatMap g = l.flatMap fun x => (f x).flatMap g := by
induction l <;> simp [*]
@@ -2575,8 +2549,6 @@ theorem foldr_eq_foldrM (f : α → β → β) (b) (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 α) (f : α β) (l' : List β) :
l.foldl (fun xs y => f y :: xs) l' = (l.map f).reverse ++ l' := by
induction l generalizing l' <;> simp [*]
@@ -2841,7 +2813,7 @@ theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔
exact fun ys, h => ys.reverse, by simpa using h, fun ys, h => ys.reverse, by simpa using h
@[simp] theorem getLast?_isSome : l.getLast?.isSome l [] := by
rw [getLast?_eq_head?_reverse, head?_isSome]
rw [getLast?_eq_head?_reverse, isSome_head?]
simp
theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a xs := by
@@ -3537,14 +3509,6 @@ theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a :=
/-! ### Deprecations -/
@[deprecated "Deprecated without replacement." (since := "2024-07-09")]
theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
@[deprecated filter_flatten (since := "2024-08-26")]
theorem join_map_filter (p : α Bool) (l : List (List α)) :
(l.map (filter p)).flatten = (l.flatten).filter p := by
rw [filter_flatten]
@[deprecated getElem_eq_getElem?_get (since := "2024-09-04")] abbrev getElem_eq_getElem? :=
@getElem_eq_getElem?_get
@[deprecated flatten_eq_nil_iff (since := "2024-09-05")] abbrev join_eq_nil := @flatten_eq_nil_iff

View File

@@ -5,6 +5,7 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
-/
prelude
import Init.Data.List.Lemmas
import Init.Data.List.Pairwise
/-!
# Lemmas about `List.min?` and `List.max?.
@@ -38,6 +39,18 @@ theorem isSome_min?_of_mem {l : List α} [Min α] {a : α} (h : a ∈ l) :
l.min?.isSome := by
cases l <;> simp_all [List.min?_cons']
theorem min?_eq_head? {α : Type u} [Min α] {l : List α}
(h : l.Pairwise (fun a b => min a b = a)) : l.min? = l.head? := by
cases l with
| nil => rfl
| cons x l =>
rw [List.head?_cons, List.min?_cons', Option.some.injEq]
induction l generalizing x with
| nil => simp
| cons y l ih =>
have hx : min x y = x := List.rel_of_pairwise_cons h (List.mem_cons_self _ _)
rw [List.foldl_cons, ih _ (hx.symm h.sublist (by simp)), hx]
theorem min?_mem [Min α] (min_eq_or : a b : α, min a b = a min a b = b) :
{xs : List α} xs.min? = some a a xs := by
intro xs
@@ -78,17 +91,19 @@ theorem le_min?_iff [Min α] [LE α]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
-- and `le_min_iff`.
theorem min?_eq_some_iff [Min α] [LE α] [anti : Std.Antisymm ((· : α) ·)]
theorem min?_eq_some_iff [Min α] [LE α]
(le_refl : a : α, a a)
(min_eq_or : a b : α, min a b = a min a b = b)
(le_min_iff : a b c : α, a min b c a b a c) {xs : List α} :
(le_min_iff : a b c : α, a min b c a b a c) {xs : List α}
(anti : a b, a xs b xs a b b a a = b := by
exact fun a b _ _ => Std.Antisymm.antisymm a b) :
xs.min? = some a a xs b, b xs a b := by
refine fun h => min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _), ?_
intro h₁, h₂
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti.1 _ _
exact congrArg some <| anti _ _ (min?_mem min_eq_or rfl) h₁
((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
(h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl))

View File

@@ -36,12 +36,6 @@ theorem map_getElem_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pai
rwa [nil_append, (drop_append_of_le_length ?_), take_append_drop] at this
simp [Nat.min_eq_left (Nat.le_of_lt hd.isLt), his]
set_option linter.listVariables false in
@[deprecated map_getElem_sublist (since := "2024-07-30")]
theorem map_get_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (·.val < ·.val)) :
is.map (get l) <+ l := by
simpa using map_getElem_sublist h
set_option linter.listVariables false in
/-- Given a sublist `l' <+ l`, there exists an increasing list of indices `is` such that
`l' = is.map fun i => l[i]`. -/
@@ -58,12 +52,6 @@ theorem sublist_eq_map_getElem {l l' : List α} (h : l' <+ l) : ∃ is : List (F
refine 0, by simp [Nat.zero_lt_succ] :: is.map (·.succ), ?_
simp [Function.comp_def, pairwise_map, IH, get_eq_getElem, get_cons_zero, get_cons_succ']
set_option linter.listVariables false in
@[deprecated sublist_eq_map_getElem (since := "2024-07-30")]
theorem sublist_eq_map_get (h : l' <+ l) : is : List (Fin l.length),
l' = map (get l) is is.Pairwise (· < ·) := by
simpa using sublist_eq_map_getElem h
set_option linter.listVariables false in
theorem pairwise_iff_getElem : Pairwise R l
(i j : Nat) (_hi : i < l.length) (_hj : j < l.length) (_hij : i < j), R l[i] l[j] := by

View File

@@ -581,8 +581,6 @@ theorem reverse_zipWith (h : l.length = l'.length) :
have : tl.reverse.length = tl'.reverse.length := by simp [h]
simp [hl h, zipWith_append _ _ _ _ _ this]
@[deprecated reverse_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_reverse := @reverse_zipWith
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
rw [zipWith_eq_zipWith_take_min]

View File

@@ -137,8 +137,6 @@ theorem Pairwise.filterMap {S : β → β → Prop} (f : α → Option β)
Pairwise S (filterMap f l) :=
pairwise_filterMap.2 <| p.imp (H _ _)
@[deprecated Pairwise.filterMap (since := "2024-07-29")] abbrev Pairwise.filter_map := @Pairwise.filterMap
theorem pairwise_filter {p : α Prop} [DecidablePred p] {l : List α} :
Pairwise R (filter p l) Pairwise (fun x y => p x p y R x y) l := by
rw [ filterMap_eq_filter, pairwise_filterMap]

View File

@@ -57,9 +57,6 @@ theorem take_of_length_le {l : List α} (h : l.length ≤ i) : take i l = l := b
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
@[deprecated take_of_length_le (since := "2024-07-07")] abbrev take_length_le := @take_of_length_le
@[simp] theorem drop_length (l : List α) : drop l.length l = [] := drop_of_length_le (Nat.le_refl _)
@[simp] theorem take_length (l : List α) : take l.length l = l := take_of_length_le (Nat.le_refl _)
@@ -206,13 +203,6 @@ theorem take_succ_eq_append_getElem {i} {l : List α} (h : i < l.length) : l.tak
| x :: xs =>
simpa using take_append_getLast (x :: xs) (by simp)
@[deprecated take_succ_cons (since := "2024-07-25")]
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 {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₂
| [], _ => rfl
| _ :: l₁, l₂ => drop_left l₁ l₂
@@ -238,16 +228,6 @@ theorem take_succ {l : List α} {i : Nat} : l.take (i + 1) = l.take i ++ l[i]?.t
· 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 α) (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 i with
| zero => apply Nat.le_refl
| succ n =>
exact Trans.trans (lih _) (Nat.le_add_left _ _)
theorem dropLast_eq_take (l : List α) : l.dropLast = l.take (l.length - 1) := by
cases l with
| nil => simp [dropLast]

View File

@@ -538,11 +538,16 @@ private theorem popWhile_toArray_aux (p : α → Bool) (l : List α) :
· simp
· simp_all [List.set_eq_of_length_le]
@[simp] theorem toArray_replicate (n : Nat) (v : α) : (List.replicate n v).toArray = mkArray n v := rfl
@[simp] theorem toArray_replicate (n : Nat) (v : α) :
(List.replicate n v).toArray = Array.replicate n v := rfl
theorem _root_.Array.mkArray_eq_toArray_replicate : mkArray n v = (List.replicate n v).toArray := by
theorem _root_.Array.replicate_eq_toArray_replicate :
Array.replicate n v = (List.replicate n v).toArray := by
simp
@[deprecated _root_.Array.replicate_eq_toArray_replicate (since := "2025-03-18")]
abbrev _root_.Array.mkArray_eq_toArray_replicate := @_root_.Array.replicate_eq_toArray_replicate
@[simp] theorem flatMap_empty {β} (f : α Array β) : (#[] : Array α).flatMap f = #[] := rfl
theorem flatMap_toArray_cons {β} (f : α Array β) (a : α) (as : List α) :

View File

@@ -135,8 +135,6 @@ theorem take_zipWith : (zipWith f l l').take i = zipWith f (l.take i) (l'.take 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 i = zipWith f (l.drop i) (l'.drop i) := by
induction l generalizing l' i with
| nil => simp
@@ -147,14 +145,10 @@ theorem drop_zipWith : (zipWith f l l').drop i = zipWith f (l.drop i) (l'.drop i
· simp
· simp [hl]
@[deprecated drop_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_drop := @drop_zipWith
@[simp]
theorem tail_zipWith : (zipWith f l l').tail = zipWith f l.tail l'.tail := by
rw [ drop_one]; simp [drop_zipWith]
@[deprecated tail_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_tail := @tail_zipWith
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
@@ -295,8 +289,6 @@ theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip
· have := of_mem_zip h
exact Mem.tail _ this.1, Mem.tail _ this.2
@[deprecated of_mem_zip (since := "2024-07-28")] abbrev mem_zip := @of_mem_zip
theorem map_fst_zip :
(l₁ : List α) (l₂ : List β), l₁.length l₂.length map Prod.fst (zip l₁ l₂) = l₁
| [], _, _ => rfl
@@ -420,9 +412,6 @@ theorem map_zipWithAll {δ : Type _} (f : α → β) (g : Option γ → Option
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
induction l <;> simp_all
@[deprecated unzip_fst (since := "2024-07-28")] abbrev unzip_left := @unzip_fst
@[deprecated unzip_snd (since := "2024-07-28")] abbrev unzip_right := @unzip_snd
theorem unzip_eq_map : l : List (α × β), unzip l = (l.map Prod.fst, l.map Prod.snd)
| [] => rfl
| (a, b) :: l => by simp only [unzip_cons, map_cons, unzip_eq_map l]

View File

@@ -23,35 +23,69 @@ private theorem rec_eq_recCompiled : @Nat.rec = @Nat.recCompiled :=
funext fun _ => funext fun _ => funext fun succ => funext fun t =>
Nat.recOn t rfl (fun n ih => congrArg (succ n) ih)
/-- Recursor identical to `Nat.rec` but uses notations `0` for `Nat.zero` and `· + 1` for `Nat.succ`.
Used as the default `Nat` eliminator by the `induction` tactic. -/
/--
A recursor for `Nat` that uses the notations `0` for `Nat.zero` and `n + 1` for `Nat.succ`.
It is otherwise identical to the default recursor `Nat.rec`. It is used by the `induction` tactic
by default for `Nat`.
-/
@[elab_as_elim, induction_eliminator]
protected abbrev recAux {motive : Nat Sort u} (zero : motive 0) (succ : (n : Nat) motive n motive (n + 1)) (t : Nat) : motive t :=
Nat.rec zero succ t
/-- Recursor identical to `Nat.casesOn` but uses notations `0` for `Nat.zero` and `· + 1` for `Nat.succ`.
Used as the default `Nat` eliminator by the `cases` tactic. -/
/--
A case analysis principle for `Nat` that uses the notations `0` for `Nat.zero` and `n + 1` for
`Nat.succ`.
It is otherwise identical to the default recursor `Nat.casesOn`. It is used as the default `Nat`
case analysis principle for `Nat` by the `cases` tactic.
-/
@[elab_as_elim, cases_eliminator]
protected abbrev casesAuxOn {motive : Nat Sort u} (t : Nat) (zero : motive 0) (succ : (n : Nat) motive (n + 1)) : motive t :=
Nat.casesOn t zero succ
/--
`Nat.repeat f n a` is `f^(n) a`; that is, it iterates `f` `n` times on `a`.
Applies a function to a starting value the specified number of times.
In other words, `f` is iterated `n` times on `a`.
Examples:
* `Nat.repeat f 3 a = f <| f <| f <| a`
* `Nat.repeat (· ++ "!") 4 "Hello" = "Hello!!!!"`
-/
@[specialize] def repeat {α : Type u} (f : α α) : (n : Nat) (a : α) α
| 0, a => a
| succ n, a => f (repeat f n a)
/-- Tail-recursive version of `Nat.repeat`. -/
/--
Applies a function to a starting value the specified number of times.
In other words, `f` is iterated `n` times on `a`.
This is a tail-recursive version of `Nat.repeat` that's used at runtime.
Examples:
* `Nat.repeatTR f 3 a = f <| f <| f <| a`
* `Nat.repeatTR (· ++ "!") 4 "Hello" = "Hello!!!!"`
-/
@[inline] def repeatTR {α : Type u} (f : α α) (n : Nat) (a : α) : α :=
let rec @[specialize] loop
| 0, a => a
| succ n, a => loop n (f a)
loop n a
/-- Boolean less-than of natural numbers. -/
/--
The Boolean less-than comparison on natural numbers.
This function is overridden in both the kernel and the compiler to efficiently evaluate using the
arbitrary-precision arithmetic library. The definition provided here is the logical model.
Examples:
* `Nat.blt 2 5 = true`
* `Nat.blt 5 2 = false`
* `Nat.blt 5 5 = false`
-/
def blt (a b : Nat) : Bool :=
ble a.succ b
@@ -785,9 +819,15 @@ instance {n m : Nat} [NeZero n] : NeZero (n^m) :=
/-! # min/max -/
/--
`Nat.min a b` is the minimum of `a` and `b`:
* if `a ≤ b` then `Nat.min a b = a`
* if `ba` then `Nat.min a b = b`
Returns the lesser of two natural numbers. Usually accessed via `Min.min`.
Returns `n` if `nm`, or `m` if `m ≤ n`.
Examples:
* `min 0 5 = 0`
* `min 4 5 = 4`
* `min 4 3 = 3`
* `min 8 8 = 8`
-/
protected abbrev min (n m : Nat) := min n m
@@ -796,9 +836,15 @@ protected theorem min_def {n m : Nat} : min n m = if n ≤ m then n else m := rf
instance : Max Nat := maxOfLe
/--
`Nat.max a b` is the maximum of `a` and `b`:
* if `a ≤ b` then `Nat.max a b = b`
* if `ba` then `Nat.max a b = a`
Returns the greater of two natural numbers. Usually accessed via `Max.max`.
Returns `m` if `nm`, or `n` if `m ≤ n`.
Examples:
* `max 0 5 = 5`
* `max 4 5 = 5`
* `max 4 3 = 4`
* `max 8 8 = 8`
-/
protected abbrev max (n m : Nat) := max n m

View File

@@ -13,6 +13,12 @@ namespace Nat
theorem bitwise_rec_lemma {n : Nat} (hNe : n 0) : n / 2 < n :=
Nat.div_lt_self (Nat.zero_lt_of_ne_zero hNe) (Nat.lt_succ_self _)
/--
A helper for implementing bitwise operators on `Nat`.
Each bit of the resulting `Nat` is the result of applying `f` to the corresponding bits of the input
`Nat`s, up to the position of the highest set bit in either input.
-/
def bitwise (f : Bool Bool Bool) (n m : Nat) : Nat :=
if n = 0 then
if f false true then m else 0
@@ -30,16 +36,56 @@ def bitwise (f : Bool → Bool → Bool) (n m : Nat) : Nat :=
r+r
decreasing_by apply bitwise_rec_lemma; assumption
/--
Bitwise and. Usually accessed via the `&&&` operator.
Each bit of the resulting value is set if the corresponding bit is set in both of the inputs.
-/
@[extern "lean_nat_land"]
def land : @& Nat @& Nat Nat := bitwise and
/--
Bitwise or. Usually accessed via the `|||` operator.
Each bit of the resulting value is set if the corresponding bit is set in at least one of the inputs.
-/
@[extern "lean_nat_lor"]
def lor : @& Nat @& Nat Nat := bitwise or
/--
Bitwise exclusive or. Usually accessed via the `^^^` operator.
Each bit of the resulting value is set if the corresponding bit is set in exactly one of the inputs.
-/
@[extern "lean_nat_lxor"]
def xor : @& Nat @& Nat Nat := bitwise bne
/--
Shifts the binary representation of a value left by the specified number of bits. Usually accessed
via the `<<<` operator.
Examples:
* `1 <<< 2 = 4`
* `1 <<< 3 = 8`
* `0 <<< 3 = 0`
* `0xf1 <<< 4 = 0xf10`
-/
@[extern "lean_nat_shiftl"]
def shiftLeft : @& Nat @& Nat Nat
| n, 0 => n
| n, succ m => shiftLeft (2*n) m
/--
Shifts the binary representation of a value right by the specified number of bits. Usually accessed
via the `>>>` operator.
Examples:
* `4 >>> 2 = 1`
* `8 >>> 2 = 2`
* `8 >>> 3 = 1`
* `0 >>> 3 = 0`
* `0xf13a >>> 8 = 0xf1`
-/
@[extern "lean_nat_shiftr"]
def shiftRight : @& Nat @& Nat Nat
| n, 0 => n
@@ -84,7 +130,9 @@ We define an operation for testing individual bits in the binary representation
of a number.
-/
/-- `testBit m n` returns whether the `(n+1)` least significant bit is `1` or `0`-/
/--
Returns `true` if the `(n+1)`th least significant bit is `1`, or `false` if it is `0`.
-/
def testBit (m n : Nat) : Bool :=
-- `1 &&& n` is faster than `n &&& 1` for big `n`.
1 &&& (m >>> n) != 0

View File

@@ -36,7 +36,10 @@ private theorem two_mul_sub_one {n : Nat} (n_pos : n > 0) : (2*n - 1) % 2 = 1 :=
/-! ### Preliminaries -/
/--
An induction principal that works on division by two.
An induction principle for the natural numbers with two cases:
* `n = 0`, and the motive is satisfied for `0`
* `n > 0`, and the motive should be satisfied for `n` on the assumption that it is satisfied for
`n / 2`.
-/
noncomputable def div2Induction {motive : Nat Sort u}
(n : Nat) (ind : (n : Nat), (n > 0 motive (n/2)) motive n) : motive n := by
@@ -493,19 +496,25 @@ theorem and_lt_two_pow (x : Nat) {y n : Nat} (right : y < 2^n) : (x &&& y) < 2^n
exact pow_le_pow_right Nat.zero_lt_two i_ge_n
simp [testBit_and, yf]
@[simp] theorem and_pow_two_sub_one_eq_mod (x n : Nat) : x &&& 2^n - 1 = x % 2^n := by
@[simp] theorem and_two_pow_sub_one_eq_mod (x n : Nat) : x &&& 2^n - 1 = x % 2^n := by
apply eq_of_testBit_eq
intro i
simp only [testBit_and, testBit_mod_two_pow]
cases testBit x i <;> simp
@[deprecated and_pow_two_sub_one_eq_mod (since := "2024-09-11")] abbrev and_pow_two_is_mod := @and_pow_two_sub_one_eq_mod
@[deprecated and_two_pow_sub_one_eq_mod (since := "2025-03-18")]
abbrev and_pow_two_sub_one_eq_mod := @and_two_pow_sub_one_eq_mod
@[deprecated and_two_pow_sub_one_eq_mod (since := "2024-09-11")]
abbrev and_pow_two_is_mod := @and_two_pow_sub_one_eq_mod
theorem and_pow_two_sub_one_of_lt_two_pow {x : Nat} (lt : x < 2^n) : x &&& 2^n - 1 = x := by
rw [and_pow_two_sub_one_eq_mod]
theorem and_two_pow_sub_one_of_lt_two_pow {x : Nat} (lt : x < 2^n) : x &&& 2^n - 1 = x := by
rw [and_two_pow_sub_one_eq_mod]
apply Nat.mod_eq_of_lt lt
@[deprecated and_pow_two_sub_one_of_lt_two_pow (since := "2024-09-11")] abbrev and_two_pow_identity := @and_pow_two_sub_one_of_lt_two_pow
@[deprecated and_two_pow_sub_one_of_lt_two_pow (since := "2025-03-18")]
abbrev and_pow_two_sub_one_of_lt_two_pow := @and_two_pow_sub_one_of_lt_two_pow
@[deprecated and_two_pow_sub_one_of_lt_two_pow (since := "2024-09-11")]
abbrev and_two_pow_identity := @and_two_pow_sub_one_of_lt_two_pow
@[simp] theorem and_mod_two_eq_one : (a &&& b) % 2 = 1 a % 2 = 1 b % 2 = 1 := by
simp only [mod_two_eq_one_iff_testBit_zero]
@@ -648,7 +657,7 @@ theorem xor_div_two : (a ^^^ b) / 2 = a / 2 ^^^ b / 2 :=
/-! ### Arithmetic -/
theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat) :
theorem testBit_two_pow_mul_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat) :
testBit (2 ^ i * a + b) j =
if j < i then
testBit b j
@@ -676,19 +685,26 @@ theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat)
Nat.div_eq_of_lt b_lt,
Nat.two_pow_pos i]
theorem testBit_mul_pow_two :
@[deprecated testBit_two_pow_mul_add (since := "2025-03-18")]
abbrev testBit_mul_pow_two_add := @testBit_two_pow_mul_add
theorem testBit_two_pow_mul :
testBit (2 ^ i * a) j = (decide (j i) && testBit a (j-i)) := by
have gen := testBit_mul_pow_two_add a (Nat.two_pow_pos i) j
have gen := testBit_two_pow_mul_add a (Nat.two_pow_pos i) j
simp at gen
rw [gen]
cases Nat.lt_or_ge j i with
| _ p => simp [p, Nat.not_le_of_lt, Nat.not_lt_of_le]
theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^i * a ||| b := by
@[deprecated testBit_two_pow_mul (since := "2025-03-18")]
abbrev testBit_mul_pow_two := @testBit_two_pow_mul
theorem two_pow_add_eq_or_of_lt {b : Nat} (b_lt : b < 2^i) (a : Nat) :
2^i * a + b = 2^i * a ||| b := by
apply eq_of_testBit_eq
intro j
simp only [testBit_mul_pow_two_add _ b_lt,
testBit_or, testBit_mul_pow_two]
simp only [testBit_two_pow_mul_add _ b_lt,
testBit_or, testBit_two_pow_mul]
if j_lt : j < i then
simp [Nat.not_le_of_lt, j_lt]
else
@@ -698,11 +714,14 @@ theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^
_ 2 ^ j := Nat.pow_le_pow_right Nat.zero_lt_two i_le
simp [i_le, j_lt, testBit_lt_two_pow, b_lt_j]
@[deprecated two_pow_add_eq_or_of_lt (since := "2025-03-18")]
abbrev mul_add_lt_is_or := @two_pow_add_eq_or_of_lt
/-! ### shiftLeft and shiftRight -/
@[simp] theorem testBit_shiftLeft (x : Nat) : testBit (x <<< i) j =
(decide (j i) && testBit x (j-i)) := by
simp [shiftLeft_eq, Nat.mul_comm _ (2^_), testBit_mul_pow_two]
simp [shiftLeft_eq, Nat.mul_comm _ (2^_), testBit_two_pow_mul]
@[simp] theorem testBit_shiftRight (x : Nat) : testBit (x >>> i) j = testBit x (i+j) := by
simp [testBit, shiftRight_add]

View File

@@ -8,33 +8,79 @@ import Init.Control.Basic
import Init.Data.Nat.Basic
import Init.Omega
set_option linter.missingDocs true
namespace Nat
universe u v
/--
Executes a monadic action on all the numbers less than some bound, in increasing order.
Example:
````lean example
#eval Nat.forM 5 fun i _ => IO.println i
````
````output
0
1
2
3
4
````
-/
@[inline] def forM {m} [Monad m] (n : Nat) (f : (i : Nat) i < n m Unit) : m Unit :=
let rec @[specialize] loop : i, i n m Unit
| 0, _ => pure ()
| i+1, h => do f (n-i-1) (by omega); loop i (Nat.le_of_succ_le h)
loop n (by simp)
/--
Executes a monadic action on all the numbers less than some bound, in decreasing order.
Example:
````lean example
#eval Nat.forRevM 5 fun i _ => IO.println i
````
````output
4
3
2
1
0
````
-/
@[inline] def forRevM {m} [Monad m] (n : Nat) (f : (i : Nat) i < n m Unit) : m Unit :=
let rec @[specialize] loop : i, i n m Unit
| 0, _ => pure ()
| i+1, h => do f i (by omega); loop i (Nat.le_of_succ_le h)
loop n (by simp)
/--
Iterates the application of a monadic function `f` to a starting value `init`, `n` times. At each
step, `f` is applied to the current value and to the next natural number less than `n`, in
increasing order.
-/
@[inline] def foldM {α : Type u} {m : Type u Type v} [Monad m] (n : Nat) (f : (i : Nat) i < n α m α) (init : α) : m α :=
let rec @[specialize] loop : i, i n α m α
| 0, h, a => pure a
| i+1, h, a => f (n-i-1) (by omega) a >>= loop i (Nat.le_of_succ_le h)
loop n (by omega) init
/--
Iterates the application of a monadic function `f` to a starting value `init`, `n` times. At each
step, `f` is applied to the current value and to the next natural number less than `n`, in
decreasing order.
-/
@[inline] def foldRevM {α : Type u} {m : Type u Type v} [Monad m] (n : Nat) (f : (i : Nat) i < n α m α) (init : α) : m α :=
let rec @[specialize] loop : i, i n α m α
| 0, h, a => pure a
| i+1, h, a => f i (by omega) a >>= loop i (Nat.le_of_succ_le h)
loop n (by omega) init
/--
Checks whether the monadic predicate `p` returns `true` for all numbers less that the given bound.
Numbers are checked in increasing order until `p` returns false, after which no further are checked.
-/
@[inline] def allM {m} [Monad m] (n : Nat) (p : (i : Nat) i < n m Bool) : m Bool :=
let rec @[specialize] loop : i, i n m Bool
| 0, _ => pure true
@@ -44,6 +90,11 @@ universe u v
| false => pure false
loop n (by simp)
/--
Checks whether there is some number less that the given bound for which the monadic predicate `p`
returns `true`. Numbers are checked in increasing order until `p` returns true, after which
no further are checked.
-/
@[inline] def anyM {m} [Monad m] (n : Nat) (p : (i : Nat) i < n m Bool) : m Bool :=
let rec @[specialize] loop : i, i n m Bool
| 0, _ => pure false

View File

@@ -20,21 +20,76 @@ instance : Dvd Nat where
theorem div_rec_lemma {x y : Nat} : 0 < y y x x - y < x :=
fun ypos, ylex => sub_lt (Nat.lt_of_lt_of_le ypos ylex) ypos
@[extern "lean_nat_div"]
theorem div_rec_fuel_lemma {x y fuel : Nat} (hy : 0 < y) (hle : y x) (hfuel : x < fuel + 1) :
x - y < fuel :=
Nat.lt_of_lt_of_le (div_rec_lemma hy, hle) (Nat.le_of_lt_succ hfuel)
/--
Division of natural numbers, discarding the remainder. Division by `0` returns `0`. Usually accessed
via the `/` operator.
This operation is sometimes called “floor division.”
This function is overridden at runtime with an efficient implementation. This definition is
the logical model.
Examples:
* `21 / 3 = 7`
* `21 / 5 = 4`
* `0 / 22 = 0`
* `5 / 0 = 0`
-/
@[extern "lean_nat_div", irreducible]
protected def div (x y : @& Nat) : Nat :=
if 0 < y y x then
Nat.div (x - y) y + 1
if hy : 0 < y then
let rec
go (fuel : Nat) (x : Nat) (hfuel : x < fuel) : Nat :=
match fuel with
| 0 => by contradiction
| succ fuel =>
if h : y x then
go fuel (x - y) (div_rec_fuel_lemma hy h hfuel) + 1
else
0
termination_by structural fuel
go (x + 1) x (Nat.lt_succ_self _)
else
0
decreasing_by apply div_rec_lemma; assumption
instance instDiv : Div Nat := Nat.div
theorem div_eq (x y : Nat) : x / y = if 0 < y y x then (x - y) / y + 1 else 0 := by
show Nat.div x y = _
rw [Nat.div]
rfl
private theorem div.go.fuel_congr (x y fuel1 fuel2 : Nat) (hy : 0 < y) (h1 : x < fuel1) (h2 : x < fuel2) :
Nat.div.go y hy fuel1 x h1 = Nat.div.go y hy fuel2 x h2 := by
match fuel1, fuel2 with
| 0, _ => contradiction
| _, 0 => contradiction
| succ fuel1, succ fuel2 =>
simp only [Nat.div.go]
split
next => rw [Nat.div.go.fuel_congr]
next => rfl
termination_by structural fuel1
theorem div_eq (x y : Nat) : x / y = if 0 < y y x then (x - y) / y + 1 else 0 := by
show Nat.div _ _ = ite _ (Nat.div _ _ + 1) _
unfold Nat.div
split
next =>
rw [Nat.div.go]
split
next =>
simp only [and_self, reduceIte, *]
congr 1
apply div.go.fuel_congr
next =>
simp only [and_false, reduceIte, *]
next =>
simp only [false_and, reduceIte, *]
/--
An induction principle customized for reasoning about the recursion pattern of natural number
division by iterated subtraction.
-/
def div.inductionOn.{u}
{motive : Nat Nat Sort u}
(x y : Nat)
@@ -72,22 +127,100 @@ theorem div_lt_self {n k : Nat} (hLtN : 0 < n) (hLtK : 1 < k) : n / k < n := by
have := Nat.add_le_of_le_sub hKN this
exact Nat.lt_of_lt_of_le (Nat.add_lt_add_left hLtK _) this
@[extern "lean_nat_mod"]
protected def modCore (x y : @& Nat) : Nat :=
/--
The modulo operator, which computes the remainder when dividing one natural number by another.
Usually accessed via the `%` operator. When the divisor is `0`, the result is the dividend rather
than an error.
This is the core implementation of `Nat.mod`. It computes the correct result for any two closed
natural numbers, but it does not have some convenient [definitional
reductions](lean-manual://section/type-system) when the `Nat`s contain free variables. The wrapper
`Nat.mod` handles those cases specially and then calls `Nat.modCore`.
This function is overridden at runtime with an efficient implementation. This definition is the
logical model.
-/
@[extern "lean_nat_mod", irreducible]
protected noncomputable def modCore (x y : Nat) : Nat :=
if hy : 0 < y then
let rec
go (fuel : Nat) (x : Nat) (hfuel : x < fuel) : Nat :=
match fuel with
| 0 => by contradiction
| succ fuel =>
if h : y x then
go fuel (x - y) (div_rec_fuel_lemma hy h hfuel)
else
x
termination_by structural fuel
go (x + 1) x (Nat.lt_succ_self _)
else
x
private theorem modCore.go.fuel_congr (x y fuel1 fuel2 : Nat) (hy : 0 < y) (h1 : x < fuel1) (h2 : x < fuel2) :
Nat.modCore.go y hy fuel1 x h1 = Nat.modCore.go y hy fuel2 x h2 := by
match fuel1, fuel2 with
| 0, _ => contradiction
| _, 0 => contradiction
| succ fuel1, succ fuel2 =>
simp only [Nat.modCore.go]
split
next => rw [Nat.modCore.go.fuel_congr]
next => rfl
termination_by structural fuel1
protected theorem modCore_eq (x y : Nat) : Nat.modCore x y =
if 0 < y y x then
Nat.modCore (x - y) y
else
x
decreasing_by apply div_rec_lemma; assumption
x := by
unfold Nat.modCore
split
next =>
rw [Nat.modCore.go]
split
next =>
simp only [and_self, reduceIte, *]
apply modCore.go.fuel_congr
next =>
simp only [and_false, reduceIte, *]
next =>
simp only [false_and, reduceIte, *]
/--
The modulo operator, which computes the remainder when dividing one natural number by another.
Usually accessed via the `%` operator. When the divisor is `0`, the result is the dividend rather
than an error.
`Nat.mod` is a wrapper around `Nat.modCore` that special-cases two situations, giving better
definitional reductions:
* `Nat.mod 0 m` should reduce to `m`, for all terms `m : Nat`.
* `Nat.mod n (m + n + 1)` should reduce to `n` for concrete `Nat` literals `n`.
These reductions help `Fin n` literals work well, because the `OfNat` instance for `Fin` uses
`Nat.mod`. In particular, `(0 : Fin (n + 1)).val` should reduce definitionally to `0`. `Nat.modCore`
can handle all numbers, but its definitional reductions are not as convenient.
This function is overridden at runtime with an efficient implementation. This definition is the
logical model.
Examples:
* `7 % 2 = 1`
* `9 % 3 = 0`
* `5 % 7 = 5`
* `5 % 0 = 5`
* `show ∀ (n : Nat), 0 % n = 0 from fun _ => rfl`
* `show ∀ (m : Nat), 5 % (m + 6) = 5 from fun _ => rfl`
-/
@[extern "lean_nat_mod"]
protected def mod : @& Nat @& Nat Nat
/-
Nat.modCore is defined by well-founded recursion and thus irreducible. Nevertheless it is
desirable if trivial `Nat.mod` calculations, namely
Nat.modCore is defined with fuel and thus does not reduce with open terms very well.
Nevertheless it is desirable for trivial `Nat.mod` calculations, namely
* `Nat.mod 0 m` for all `m`
* `Nat.mod n (m+n)` for concrete literals `n`
reduce definitionally.
* `Nat.mod n (m + n + 1)` for concrete literals `n`,
to reduce definitionally.
This property is desirable for `Fin n` literals, as it means `(ofNat 0 : Fin n).val = 0` by
definition.
-/
@@ -103,17 +236,20 @@ protected theorem modCore_eq_mod (n m : Nat) : Nat.modCore n m = n % m := by
show Nat.modCore n m = Nat.mod n m
match n, m with
| 0, _ =>
rw [Nat.modCore]
rw [Nat.modCore_eq]
exact if_neg fun hlt, hle => Nat.lt_irrefl _ (Nat.lt_of_lt_of_le hlt hle)
| (_ + 1), _ =>
rw [Nat.mod]; dsimp
refine iteInduction (fun _ => rfl) (fun h => ?false) -- cannot use `split` this early yet
rw [Nat.modCore]
rw [Nat.modCore_eq]
exact if_neg fun _hlt, hle => h hle
theorem mod_eq (x y : Nat) : x % y = if 0 < y y x then (x - y) % y else x := by
rw [Nat.modCore_eq_mod, Nat.modCore_eq_mod, Nat.modCore]
rw [Nat.modCore_eq_mod, Nat.modCore_eq_mod, Nat.modCore_eq]
/--
An induction principle customized for reasoning about the recursion pattern of `Nat.mod`.
-/
def mod.inductionOn.{u}
{motive : Nat Nat Sort u}
(x y : Nat)

View File

@@ -168,4 +168,40 @@ theorem add_div_of_dvd_add_add_one (h : c a + b + 1) : (a + b) / c = a / c +
have := mod_add_mod_lt_of_add_mod_eq_sub_one w ((mod_eq_sub_iff Nat.zero_lt_one w).mpr h)
omega
theorem div_lt_of_lt {a b c : Nat} (ha : a < c) : a / b < c := by
obtain (rfl|hb) := Nat.eq_zero_or_pos b
· simp
omega
· rw [Nat.div_lt_iff_lt_mul hb, Nat.mul_one a]
apply Nat.mul_lt_mul_of_lt_of_le ha (by omega) (by omega)
theorem div_mod_eq_div {a b c : Nat} (ha : a < c) : (a / b) % c = a / b :=
Nat.mod_eq_of_lt (Nat.div_lt_of_lt ha)
theorem div_mod_eq_mod_div_mod {a b c : Nat} (ha : a < c) (hb : b < c) :
(a / b) % c = a % c / (b % c) := by
rw [Nat.mod_eq_of_lt (Nat.div_lt_of_lt ha), Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb]
theorem mod_mod_eq_mod_of_lt_right {a b c : Nat} (ha : a < c) : (a % b) % c = a % b :=
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt (Nat.mod_le _ _) ha)
theorem mod_mod_eq_mod_mod_mod {a b c : Nat} (ha : a < c) (hb : b < c) :
(a % b) % c = (a % c) % (b % c) := by
rw [Nat.mod_mod_eq_mod_of_lt_right ha, Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb]
theorem mod_mod_eq_mod_mod_of_dvd {a b c : Nat} (h : b c) : a % b % c = a % c % b := by
refine Or.elim (Nat.eq_zero_or_pos b) (by rintro rfl; simp) (fun hb => ?_)
refine Or.elim (Nat.eq_zero_or_pos c) (by rintro rfl; simp) (fun hc => ?_)
rw [Nat.mod_mod_of_dvd _ h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le (Nat.mod_lt a hb) (Nat.le_of_dvd hc h))]
theorem mod_mod_of_dvd' {a b c : Nat} (h : b c) : a % b % c = a % b := by
rw [Nat.mod_mod_eq_mod_mod_of_dvd h, Nat.mod_mod_of_dvd _ h]
theorem mod_mod_eq_mod_mod_mod_of_dvd {a b c : Nat} (hb : b c) :
(a % b) % c = (a % c) % (b % c) := by
refine (Decidable.em (b = c)).elim (by rintro rfl; simp) (fun hb' => ?_)
refine Or.elim (Nat.eq_zero_or_pos c) (by rintro rfl; simp) (fun hc => ?_)
have : b < c := Nat.lt_of_le_of_ne (Nat.le_of_dvd hc hb) hb'
rw [Nat.mod_mod_of_dvd' hb, Nat.mod_eq_of_lt this, Nat.mod_mod_of_dvd _ hb]
end Nat

View File

@@ -13,14 +13,30 @@ universe u
namespace Nat
/--
`Nat.fold` evaluates `f` on the numbers up to `n` exclusive, in increasing order:
* `Nat.fold f 3 init = init |> f 0 |> f 1 |> f 2`
Iterates the application of a function `f` to a starting value `init`, `n` times. At each step, `f`
is applied to the current value and to the next natural number less than `n`, in increasing order.
Examples:
* `Nat.fold 3 f init = (init |> f 0 (by simp) |> f 1 (by simp) |> f 2 (by simp))`
* `Nat.fold 4 (fun i _ xs => xs.push i) #[] = #[0, 1, 2, 3]`
* `Nat.fold 0 (fun i _ xs => xs.push i) #[] = #[]`
-/
@[specialize] def fold {α : Type u} : (n : Nat) (f : (i : Nat) i < n α α) (init : α) α
| 0, f, a => a
| succ n, f, a => f n (by omega) (fold n (fun i h => f i (by omega)) a)
/-- Tail-recursive version of `Nat.fold`. -/
/--
Iterates the application of a function `f` to a starting value `init`, `n` times. At each step, `f`
is applied to the current value and to the next natural number less than `n`, in increasing order.
This is a tail-recursive version of `Nat.fold` that's used at runtime.
Examples:
* `Nat.foldTR 3 f init = (init |> f 0 (by simp) |> f 1 (by simp) |> f 2 (by simp))`
* `Nat.foldTR 4 (fun i _ xs => xs.push i) #[] = #[0, 1, 2, 3]`
* `Nat.foldTR 0 (fun i _ xs => xs.push i) #[] = #[]`
-/
@[inline] def foldTR {α : Type u} (n : Nat) (f : (i : Nat) i < n α α) (init : α) : α :=
let rec @[specialize] loop : j, j n α α
| 0, h, a => a
@@ -28,31 +44,72 @@ namespace Nat
loop n (by omega) init
/--
`Nat.foldRev` evaluates `f` on the numbers up to `n` exclusive, in decreasing order:
* `Nat.foldRev f 3 init = f 0 <| f 1 <| f 2 <| init`
Iterates the application of a function `f` to a starting value `init`, `n` times. At each step, `f`
is applied to the current value and to the next natural number less than `n`, in decreasing order.
Examples:
* `Nat.foldRev 3 f init = (f 0 (by simp) <| f 1 (by simp) <| f 2 (by simp) init)`
* `Nat.foldRev 4 (fun i _ xs => xs.push i) #[] = #[3, 2, 1, 0]`
* `Nat.foldRev 0 (fun i _ xs => xs.push i) #[] = #[]`
-/
@[specialize] def foldRev {α : Type u} : (n : Nat) (f : (i : Nat) i < n α α) (init : α) α
| 0, f, a => a
| succ n, f, a => foldRev n (fun i h => f i (by omega)) (f n (by omega) a)
/-- `any f n = true` iff there is `i in [0, n-1]` s.t. `f i = true` -/
/--
Checks whether there is some number less that the given bound for which `f` returns `true`.
Examples:
* `Nat.any 4 (fun i _ => i < 5) = true`
* `Nat.any 7 (fun i _ => i < 5) = true`
* `Nat.any 7 (fun i _ => i % 2 = 0) = true`
* `Nat.any 1 (fun i _ => i % 2 = 1) = false`
-/
@[specialize] def any : (n : Nat) (f : (i : Nat) i < n Bool) Bool
| 0, f => false
| succ n, f => any n (fun i h => f i (by omega)) || f n (by omega)
/-- Tail-recursive version of `Nat.any`. -/
/--
Checks whether there is some number less that the given bound for which `f` returns `true`.
This is a tail-recursive equivalent of `Nat.any` that's used at runtime.
Examples:
* `Nat.anyTR 4 (fun i _ => i < 5) = true`
* `Nat.anyTR 7 (fun i _ => i < 5) = true`
* `Nat.anyTR 7 (fun i _ => i % 2 = 0) = true`
* `Nat.anyTR 1 (fun i _ => i % 2 = 1) = false`
-/
@[inline] def anyTR (n : Nat) (f : (i : Nat) i < n Bool) : Bool :=
let rec @[specialize] loop : (i : Nat) i n Bool
| 0, h => false
| succ m, h => f (n - succ m) (by omega) || loop m (by omega)
loop n (by omega)
/-- `all f n = true` iff every `i in [0, n-1]` satisfies `f i = true` -/
/--
Checks whether `f` returns `true` for every number strictly less than a bound.
Examples:
* `Nat.all 4 (fun i _ => i < 5) = true`
* `Nat.all 7 (fun i _ => i < 5) = false`
* `Nat.all 7 (fun i _ => i % 2 = 0) = false`
* `Nat.all 1 (fun i _ => i % 2 = 0) = true`
-/
@[specialize] def all : (n : Nat) (f : (i : Nat) i < n Bool) Bool
| 0, f => true
| succ n, f => all n (fun i h => f i (by omega)) && f n (by omega)
/-- Tail-recursive version of `Nat.all`. -/
/--
Checks whether `f` returns `true` for every number strictly less than a bound.
This is a tail-recursive equivalent of `Nat.all` that's used at runtime.
Examples:
* `Nat.allTR 4 (fun i _ => i < 5) = true`
* `Nat.allTR 7 (fun i _ => i < 5) = false`
* `Nat.allTR 7 (fun i _ => i % 2 = 0) = false`
* `Nat.allTR 1 (fun i _ => i % 2 = 0) = true`
-/
@[inline] def allTR (n : Nat) (f : (i : Nat) i < n Bool) : Bool :=
let rec @[specialize] loop : (i : Nat) i n Bool
| 0, h => true

View File

@@ -11,22 +11,19 @@ import Init.RCases
namespace Nat
/--
Computes the greatest common divisor of two natural numbers.
Computes the greatest common divisor of two natural numbers. The GCD of two natural numbers is the
largest natural number that evenly divides both.
This reference implementation via the Euclidean algorithm
is overridden in both the kernel and the compiler to efficiently
evaluate using the "bignum" representation (see `Nat`).
The definition provided here is the logical model
(and it is soundness-critical that they coincide).
In particular, the GCD of a number and `0` is the number itself.
The GCD of two natural numbers is the largest natural number
that divides both arguments.
In particular, the GCD of a number and `0` is the number itself:
```
example : Nat.gcd 10 15 = 5 := rfl
example : Nat.gcd 0 5 = 5 := rfl
example : Nat.gcd 7 0 = 7 := rfl
```
This reference implementation via the Euclidean algorithm is overridden in both the kernel and the
compiler to efficiently evaluate using arbitrary-precision arithmetic. The definition provided here
is the logical model.
Examples:
* `Nat.gcd 10 15 = 5`
* `Nat.gcd 0 5 = 5`
* `Nat.gcd 7 0 = 7`
-/
@[extern "lean_nat_gcd"]
def gcd (m n : @& Nat) : Nat :=

View File

@@ -17,7 +17,16 @@ that should be added to this file.
namespace Nat
/-- The least common multiple of `m` and `n`, defined using `gcd`. -/
/--
The least common multiple of `m` and `n` is the smallest natural number that's evenly divisible by
both `m` and `n`. Returns `0` if either `m` or `n` is `0`.
Examples:
* `Nat.lcm 9 6 = 18`
* `Nat.lcm 9 3 = 9`
* `Nat.lcm 0 3 = 0`
* `Nat.lcm 3 0 = 0`
-/
def lcm (m n : Nat) : Nat := m * n / gcd m n
theorem lcm_comm (m n : Nat) : lcm m n = lcm n m := by

View File

@@ -20,9 +20,6 @@ and later these lemmas should be organised into other files more systematically.
namespace Nat
@[deprecated and_forall_add_one (since := "2024-07-30")] abbrev and_forall_succ := @and_forall_add_one
@[deprecated or_exists_add_one (since := "2024-07-30")] abbrev or_exists_succ := @or_exists_add_one
@[simp] theorem exists_ne_zero {P : Nat Prop} : ( n, ¬ n = 0 P n) n, P (n + 1) :=
fun n, h, w => by cases n with | zero => simp at h | succ n => exact n, w,
fun n, w => n + 1, by simp, w
@@ -580,9 +577,15 @@ theorem mul_mod (a b n : Nat) : a * b % n = (a % n) * (b % n) % n := by
have := (add_mul_mod_self_left (m % n + k) n (m / n)).symm
rwa [Nat.add_right_comm, mod_add_div] at this
@[simp] theorem mul_mod_mod (m n l : Nat) : (m * (n % l)) % l = (m * n) % l := by
rw [mul_mod, mod_mod, mul_mod]
@[simp] theorem add_mod_mod (m n k : Nat) : (m + n % k) % k = (m + n) % k := by
rw [Nat.add_comm, mod_add_mod, Nat.add_comm]
@[simp] theorem mod_mul_mod (m n l : Nat) : ((m % l) * n) % l = (m * n) % l := by
rw [Nat.mul_comm, mul_mod_mod, Nat.mul_comm]
theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
rw [add_mod_mod, mod_add_mod]
@@ -595,9 +598,6 @@ theorem add_mod (a b n : Nat) : (a + b) % n = ((a % n) + (b % n)) % n := by
| zero => simp_all
| succ k => omega
@[simp] theorem mod_mul_mod {a b c : Nat} : (a % c * b) % c = a * b % c := by
rw [mul_mod, mod_mod, mul_mod]
theorem mod_eq_sub (x w : Nat) : x % w = x - w * (x / w) := by
conv => rhs; congr; rw [ mod_add_div x w]
simp

View File

@@ -32,7 +32,7 @@ inductive Expr where
| add (a b : Expr)
| mulL (k : Nat) (a : Expr)
| mulR (a : Expr) (k : Nat)
deriving Inhabited
deriving Inhabited, BEq
def Expr.denote (ctx : Context) : Expr Nat
| .add a b => Nat.add (denote ctx a) (denote ctx b)

View File

@@ -18,9 +18,18 @@ theorem log2_terminates : ∀ n, n ≥ 2 → n / 2 < n
simp
/--
Computes `⌊max 0 (log₂ n)⌋`.
Base-two logarithm of natural numbers. Returns `⌊max 0 (log₂ n)⌋`.
`log2 0 = log2 1 = 0`, `log2 2 = 1`, ..., `log2 (2^i) = i`, etc.
This function is overridden at runtime with an efficient implementation. This definition is
the logical model.
Examples:
* `Nat.log2 0 = 0`
* `Nat.log2 1 = 0`
* `Nat.log2 2 = 1`
* `Nat.log2 4 = 2`
* `Nat.log2 7 = 2`
* `Nat.log2 8 = 3`
-/
@[extern "lean_nat_log2"]
def log2 (n : @& Nat) : Nat :=

View File

@@ -13,6 +13,16 @@ theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n)
rw [this, Nat.sub_add_eq]
exact Nat.sub_lt (Nat.zero_lt_sub_of_lt h₂) h₁
/--
Returns the least power of two that's greater than or equal to `n`.
Examples:
* `Nat.nextPowerOfTwo 0 = 1`
* `Nat.nextPowerOfTwo 1 = 1`
* `Nat.nextPowerOfTwo 2 = 2`
* `Nat.nextPowerOfTwo 3 = 4`
* `Nat.nextPowerOfTwo 5 = 8`
-/
def nextPowerOfTwo (n : Nat) : Nat :=
go 1 (by decide)
where
@@ -24,11 +34,17 @@ where
termination_by n - power
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
/--
A natural number `n` is a power of two if there exists some `k : Nat` such that `n = 2 ^ k`.
-/
def isPowerOfTwo (n : Nat) := k, n = 2 ^ k
theorem one_isPowerOfTwo : isPowerOfTwo 1 :=
theorem isPowerOfTwo_one : isPowerOfTwo 1 :=
0, by decide
@[deprecated isPowerOfTwo_one (since := "2025-03-18")]
abbrev one_isPowerOfTwo := @isPowerOfTwo_one
theorem mul2_isPowerOfTwo_of_isPowerOfTwo (h : isPowerOfTwo n) : isPowerOfTwo (n * 2) :=
have k, h := h
k+1, by simp [h, Nat.pow_succ]
@@ -41,7 +57,7 @@ theorem pos_of_isPowerOfTwo (h : isPowerOfTwo n) : n > 0 := by
theorem isPowerOfTwo_nextPowerOfTwo (n : Nat) : n.nextPowerOfTwo.isPowerOfTwo := by
apply isPowerOfTwo_go
apply one_isPowerOfTwo
apply isPowerOfTwo_one
where
isPowerOfTwo_go (power : Nat) (h₁ : power > 0) (h₂ : power.isPowerOfTwo) : (nextPowerOfTwo.go n power h₁).isPowerOfTwo := by
unfold nextPowerOfTwo.go

View File

@@ -17,6 +17,16 @@ import Init.Data.Nat.Log2
Note the use of `nat_lit`; there is no wrapping `OfNat.ofNat` in the resulting term.
-/
class OfScientific (α : Type u) where
/--
Produces a value from the given mantissa, exponent sign, and decimal exponent. For the exponent
sign, `true` indicates a negative exponent.
Examples:
- `1.23` is syntax for `OfScientific.ofScientific (nat_lit 123) true (nat_lit 2)`
- `121e100` is syntax for `OfScientific.ofScientific (nat_lit 121) false (nat_lit 100)`
Note the use of `nat_lit`; there is no wrapping `OfNat.ofNat` in the resulting term.
-/
ofScientific (mantissa : Nat) (exponentSign : Bool) (decimalExponent : Nat) : α
/-- Computes `m * 2^e`. -/
@@ -45,6 +55,9 @@ protected opaque Float.ofScientific (m : Nat) (s : Bool) (e : Nat) : Float :=
instance : OfScientific Float where
ofScientific := Float.ofScientific
/--
Converts a natural number into a 64-bit floating point number.
-/
@[export lean_float_of_nat]
def Float.ofNat (n : Nat) : Float :=
OfScientific.ofScientific n false 0
@@ -55,7 +68,7 @@ def Float.ofInt : Int → Float
instance : OfNat Float n := Float.ofNat n
abbrev Nat.toFloat (n : Nat) : Float :=
@[inherit_doc Float.ofNat] abbrev Nat.toFloat (n : Nat) : Float :=
Float.ofNat n
/-- Computes `m * 2^e`. -/
@@ -76,6 +89,9 @@ protected opaque Float32.ofScientific (m : Nat) (s : Bool) (e : Nat) : Float32 :
instance : OfScientific Float32 where
ofScientific := Float32.ofScientific
/--
Converts a natural number into a 32-bit floating point number.
-/
@[export lean_float32_of_nat]
def Float32.ofNat (n : Nat) : Float32 :=
OfScientific.ofScientific n false 0
@@ -86,5 +102,5 @@ def Float32.ofInt : Int → Float32
instance : OfNat Float32 n := Float32.ofNat n
abbrev Nat.toFloat32 (n : Nat) : Float32 :=
@[inherit_doc Float32.ofNat] abbrev Nat.toFloat32 (n : Nat) : Float32 :=
Float32.ofNat n

View File

@@ -138,6 +138,10 @@ theorem bind_comm {f : α → β → Option γ} (a : Option α) (b : Option β)
theorem bind_assoc (x : Option α) (f : α Option β) (g : β Option γ) :
(x.bind f).bind g = x.bind fun y => (f y).bind g := by cases x <;> rfl
theorem bind_congr {α β} {o : Option α} {f g : α Option β} :
(h : a, o = some a f a = g a) o.bind f = o.bind g := by
cases o <;> simp
theorem join_eq_some : x.join = some a x = some (some a) := by
simp [bind_eq_some]
@@ -229,10 +233,13 @@ theorem map_inj_right {f : α → β} {o o' : Option α} (w : ∀ x y, f x = f y
theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
theorem isSome_filter_of_isSome (p : α Bool) (o : Option α) (h : (o.filter p).isSome) :
theorem isSome_of_isSome_filter (p : α Bool) (o : Option α) (h : (o.filter p).isSome) :
o.isSome := by
cases o <;> simp at h
@[deprecated isSome_of_isSome_filter (since := "2025-03-18")]
abbrev isSome_filter_of_isSome := @isSome_of_isSome_filter
@[simp] theorem filter_eq_none {p : α Bool} :
o.filter p = none o = none a, a o ¬ p a := by
cases o <;> simp [filter_some]
@@ -295,9 +302,12 @@ theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) :
@[simp] theorem guard_eq_some [DecidablePred p] : guard p a = some b a = b p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[simp] theorem guard_isSome [DecidablePred p] : (Option.guard p a).isSome p a :=
@[simp] theorem isSome_guard [DecidablePred p] : (Option.guard p a).isSome p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[deprecated isSome_guard (since := "2025-03-18")]
abbrev guard_isSome := @isSome_guard
@[simp] theorem guard_eq_none [DecidablePred p] : Option.guard p a = none ¬ p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@@ -366,9 +376,12 @@ theorem choice_eq {α : Type _} [Subsingleton α] (a : α) : choice α = some a
rw [dif_pos (a : Nonempty α)]
simp; apply Subsingleton.elim
theorem choice_isSome_iff_nonempty {α : Type _} : (choice α).isSome Nonempty α :=
theorem isSome_choice_iff_nonempty {α : Type _} : (choice α).isSome Nonempty α :=
fun h => (choice α).get h, fun h => by simp only [choice, dif_pos h, isSome_some]
@[deprecated isSome_choice_iff_nonempty (since := "2025-03-18")]
abbrev choice_isSome_iff_nonempty := @isSome_choice_iff_nonempty
end choice
@[simp] theorem toList_some (a : α) : (a : Option α).toList = [a] := rfl

View File

@@ -34,11 +34,15 @@ structure StdGen where
instance : Inhabited StdGen := { s1 := 0, s2 := 0 }
/-- The range of values returned by `StdGen` -/
def stdRange := (1, 2147483562)
instance : Repr StdGen where
reprPrec | s1, s2, _ => Std.Format.bracket "" (repr s1 ++ ", " ++ repr s2) ""
/--
The next value from a `StdGen`, paired with an updated generator state.
-/
def stdNext : StdGen Nat × StdGen
| s1, s2 =>
let k : Int := Int.ofNat (s1 / 53668)
@@ -51,6 +55,9 @@ def stdNext : StdGen → Nat × StdGen
let z' : Nat := if z < 1 then (z + 2147483562).toNat else z.toNat % 2147483562
(z', s1'', s2'')
/--
Splits a `StdGen` into two separate states.
-/
def stdSplit : StdGen StdGen × StdGen
| g@s1, s2 =>
let newS1 := if s1 = 2147483562 then 1 else s1 + 1
@@ -66,7 +73,7 @@ instance : RandomGen StdGen := {
split := stdSplit
}
/-- Return a standard number generator. -/
/-- Returns a standard number generator. -/
def mkStdGen (s : Nat := 0) : StdGen :=
let q := s / 2147483562
let s1 := s % 2147483562
@@ -86,7 +93,7 @@ private partial def randNatAux {gen : Type u} [RandomGen gen] (genLo genMag : Na
let v' := v*genMag + (x - genLo)
randNatAux genLo genMag (r' / genMag - 1) (v', g')
/-- Generate a random natural number in the interval [lo, hi]. -/
/-- Generates a random natural number in the interval [lo, hi]. -/
def randNat {gen : Type u} [RandomGen gen] (g : gen) (lo hi : Nat) : Nat × gen :=
let lo' := if lo > hi then hi else lo
let hi' := if lo > hi then lo else hi
@@ -104,7 +111,7 @@ def randNat {gen : Type u} [RandomGen gen] (g : gen) (lo hi : Nat) : Nat × gen
let v' := lo' + (v % k)
(v', g')
/-- Generate a random Boolean. -/
/-- Generates a random Boolean. -/
def randBool {gen : Type u} [RandomGen gen] (g : gen) : Bool × gen :=
let (v, g') := randNat g 0 1
(v = 1, g')
@@ -113,9 +120,18 @@ initialize IO.stdGenRef : IO.Ref StdGen ←
let seed := UInt64.toNat (ByteArray.toUInt64LE! ( IO.getRandomBytes 8))
IO.mkRef (mkStdGen seed)
/--
Seeds the random number generator state used by `IO.rand`.
-/
def IO.setRandSeed (n : Nat) : BaseIO Unit :=
IO.stdGenRef.set (mkStdGen n)
/--
Returns a pseudorandom number between `lo` and `hi`, using and updating a saved random generator
state.
This state can be seeded using `IO.setRandSeed`.
-/
def IO.rand (lo hi : Nat) : BaseIO Nat := do
let gen IO.stdGenRef.get
let (r, gen) := randNat gen lo hi

View File

@@ -129,6 +129,17 @@ We have pure functions for calculating the decimal representation of a `Nat` (`t
a fast variant that handles small numbers (`USize`) via C code (`lean_string_of_usize`).
-/
/--
Returns a single digit representation of `n`, which is assumed to be in a base less than or equal to
`16`. Returns `'*'` if `n > 15`.
Examples:
* `Nat.digitChar 5 = '5'`
* `Nat.digitChar 12 = 'c'`
* `Nat.digitChar 15 = 'f'`
* `Nat.digitChar 16 = '*'`
* `Nat.digitChar 85 = '*'`
-/
def digitChar (n : Nat) : Char :=
if n = 0 then '0' else
if n = 1 then '1' else
@@ -156,9 +167,29 @@ def toDigitsCore (base : Nat) : Nat → Nat → List Char → List Char
if n' = 0 then d::ds
else toDigitsCore base fuel n' (d::ds)
/--
Returns the decimal representation of a natural number as a list of digit characters in the given
base. If the base is greater than `16` then `'*'` is returned for digits greater than `0xf`.
Examples:
* `Nat.toDigits 10 0xff = ['2', '5', '5']`
* `Nat.toDigits 8 0xc = ['1', '4']`
* `Nat.toDigits 16 0xcafe = ['c', 'a', 'f', 'e']`
* `Nat.toDigits 80 200 = ['2', '*']`
-/
def toDigits (base : Nat) (n : Nat) : List Char :=
toDigitsCore base (n+1) n []
/--
Converts a word-sized unsigned integer into a decimal string.
This function is overridden at runtime with an efficient implementation.
Examples:
* `USize.repr 0 = "0"`
* `USize.repr 28 = "28"`
* `USize.repr 307 = "307"`
-/
@[extern "lean_string_of_usize"]
protected def _root_.USize.repr (n : @& USize) : String :=
(toDigits 10 n.toNat).asString
@@ -172,10 +203,22 @@ private def reprFast (n : Nat) : String :=
if h : n < USize.size then (USize.ofNatLT n h).repr
else (toDigits 10 n).asString
/--
Converts a natural number to its decimal string representation.
-/
@[implemented_by reprFast]
protected def repr (n : Nat) : String :=
(toDigits 10 n).asString
/--
Converts a natural number less than `10` to the corresponding Unicode superscript digit character.
Returns `'*'` for other numbers.
Examples:
* `Nat.superDigitChar 3 = '³'`
* `Nat.superDigitChar 7 = '⁷'`
* `Nat.superDigitChar 10 = '*'`
-/
def superDigitChar (n : Nat) : Char :=
if n = 0 then '' else
if n = 1 then '¹' else
@@ -196,12 +239,37 @@ partial def toSuperDigitsAux : Nat → List Char → List Char
if n' = 0 then d::ds
else toSuperDigitsAux n' (d::ds)
/--
Converts a natural number to the list of Unicode superscript digit characters that corresponds to
its decimal representation.
Examples:
* `Nat.toSuperDigits 0 = ['⁰']`
* `Nat.toSuperDigits 35 = ['³', '⁵']`
-/
def toSuperDigits (n : Nat) : List Char :=
toSuperDigitsAux n []
/--
Converts a natural number to a string that contains the its decimal representation as Unicode
superscript digit characters.
Examples:
* `Nat.toSuperscriptString 0 = "⁰"`
* `Nat.toSuperscriptString 35 = "³⁵"`
-/
def toSuperscriptString (n : Nat) : String :=
(toSuperDigits n).asString
/--
Converts a natural number less than `10` to the corresponding Unicode subscript digit character.
Returns `'*'` for other numbers.
Examples:
* `Nat.subDigitChar 3 = '₃'`
* `Nat.subDigitChar 7 = '₇'`
* `Nat.subDigitChar 10 = '*'`
-/
def subDigitChar (n : Nat) : Char :=
if n = 0 then '' else
if n = 1 then '' else
@@ -222,9 +290,25 @@ partial def toSubDigitsAux : Nat → List Char → List Char
if n' = 0 then d::ds
else toSubDigitsAux n' (d::ds)
/--
Converts a natural number to the list of Unicode subscript digit characters that corresponds to
its decimal representation.
Examples:
* `Nat.toSubDigits 0 = ['₀']`
* `Nat.toSubDigits 35 = ['₃', '₅']`
-/
def toSubDigits (n : Nat) : List Char :=
toSubDigitsAux n []
/--
Converts a natural number to a string that contains the its decimal representation as Unicode
subscript digit characters.
Examples:
* `Nat.toSubscriptString 0 = "₀"`
* `Nat.toSubscriptString 35 = "₃₅"`
-/
def toSubscriptString (n : Nat) : String :=
(toSubDigits n).asString
@@ -233,6 +317,9 @@ end Nat
instance : Repr Nat where
reprPrec n _ := Nat.repr n
/--
Returns the decimal string representation of an integer.
-/
protected def Int.repr : Int String
| ofNat m => Nat.repr m
| negSucc m => "-" ++ Nat.repr (succ m)
@@ -274,6 +361,14 @@ instance : Repr Char where
protected def Char.repr (c : Char) : String :=
c.quote
/--
Converts a string to its corresponding Lean string literal syntax. Double quotes are added to each
end, and internal characters are escaped as needed.
Examples:
* `"abc".quote = "\"abc\""`
* `"\"".quote = "\"\\\"\""`
-/
def String.quote (s : String) : String :=
if s.isEmpty then "\"\""
else s.foldl (fun s c => s ++ c.quoteCore) "\"" ++ "\""

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.UInt.Bitwise
import Init.Data.SInt.Lemmas
set_option hygiene false in
@@ -55,3 +56,51 @@ theorem Bool.toBitVec_toISize {b : Bool} :
· simp [toISize]
· apply BitVec.eq_of_toNat_eq
simp [toISize]
@[simp] theorem UInt8.toInt8_add (a b : UInt8) : (a + b).toInt8 = a.toInt8 + b.toInt8 := rfl
@[simp] theorem UInt16.toInt16_add (a b : UInt16) : (a + b).toInt16 = a.toInt16 + b.toInt16 := rfl
@[simp] theorem UInt32.toInt32_add (a b : UInt32) : (a + b).toInt32 = a.toInt32 + b.toInt32 := rfl
@[simp] theorem UInt64.toInt64_add (a b : UInt64) : (a + b).toInt64 = a.toInt64 + b.toInt64 := rfl
@[simp] theorem USize.toISize_add (a b : USize) : (a + b).toISize = a.toISize + b.toISize := rfl
@[simp] theorem UInt8.toInt8_neg (a : UInt8) : (-a).toInt8 = -a.toInt8 := rfl
@[simp] theorem UInt16.toInt16_neg (a : UInt16) : (-a).toInt16 = -a.toInt16 := rfl
@[simp] theorem UInt32.toInt32_neg (a : UInt32) : (-a).toInt32 = -a.toInt32 := rfl
@[simp] theorem UInt64.toInt64_neg (a : UInt64) : (-a).toInt64 = -a.toInt64 := rfl
@[simp] theorem USize.toISize_neg (a : USize) : (-a).toISize = -a.toISize := rfl
@[simp] theorem UInt8.toInt8_sub (a b : UInt8) : (a - b).toInt8 = a.toInt8 - b.toInt8 := rfl
@[simp] theorem UInt16.toInt16_sub (a b : UInt16) : (a - b).toInt16 = a.toInt16 - b.toInt16 := rfl
@[simp] theorem UInt32.toInt32_sub (a b : UInt32) : (a - b).toInt32 = a.toInt32 - b.toInt32 := rfl
@[simp] theorem UInt64.toInt64_sub (a b : UInt64) : (a - b).toInt64 = a.toInt64 - b.toInt64 := rfl
@[simp] theorem USize.toISize_sub (a b : USize) : (a - b).toISize = a.toISize - b.toISize := rfl
@[simp] theorem UInt8.toInt8_mul (a b : UInt8) : (a * b).toInt8 = a.toInt8 * b.toInt8 := rfl
@[simp] theorem UInt16.toInt16_mul (a b : UInt16) : (a * b).toInt16 = a.toInt16 * b.toInt16 := rfl
@[simp] theorem UInt32.toInt32_mul (a b : UInt32) : (a * b).toInt32 = a.toInt32 * b.toInt32 := rfl
@[simp] theorem UInt64.toInt64_mul (a b : UInt64) : (a * b).toInt64 = a.toInt64 * b.toInt64 := rfl
@[simp] theorem USize.toISize_mul (a b : USize) : (a * b).toISize = a.toISize * b.toISize := rfl
@[simp] theorem UInt8.toInt8_and (a b : UInt8) : (a &&& b).toInt8 = a.toInt8 &&& b.toInt8 := rfl
@[simp] theorem UInt16.toInt16_and (a b : UInt16) : (a &&& b).toInt16 = a.toInt16 &&& b.toInt16 := rfl
@[simp] theorem UInt32.toInt32_and (a b : UInt32) : (a &&& b).toInt32 = a.toInt32 &&& b.toInt32 := rfl
@[simp] theorem UInt64.toInt64_and (a b : UInt64) : (a &&& b).toInt64 = a.toInt64 &&& b.toInt64 := rfl
@[simp] theorem USize.toISize_and (a b : USize) : (a &&& b).toISize = a.toISize &&& b.toISize := rfl
@[simp] theorem UInt8.toInt8_or (a b : UInt8) : (a ||| b).toInt8 = a.toInt8 ||| b.toInt8 := rfl
@[simp] theorem UInt16.toInt16_or (a b : UInt16) : (a ||| b).toInt16 = a.toInt16 ||| b.toInt16 := rfl
@[simp] theorem UInt32.toInt32_or (a b : UInt32) : (a ||| b).toInt32 = a.toInt32 ||| b.toInt32 := rfl
@[simp] theorem UInt64.toInt64_or (a b : UInt64) : (a ||| b).toInt64 = a.toInt64 ||| b.toInt64 := rfl
@[simp] theorem USize.toISize_or (a b : USize) : (a ||| b).toISize = a.toISize ||| b.toISize := rfl
@[simp] theorem UInt8.toInt8_xor (a b : UInt8) : (a ^^^ b).toInt8 = a.toInt8 ^^^ b.toInt8 := rfl
@[simp] theorem UInt16.toInt16_xor (a b : UInt16) : (a ^^^ b).toInt16 = a.toInt16 ^^^ b.toInt16 := rfl
@[simp] theorem UInt32.toInt32_xor (a b : UInt32) : (a ^^^ b).toInt32 = a.toInt32 ^^^ b.toInt32 := rfl
@[simp] theorem UInt64.toInt64_xor (a b : UInt64) : (a ^^^ b).toInt64 = a.toInt64 ^^^ b.toInt64 := rfl
@[simp] theorem USize.toISize_xor (a b : USize) : (a ^^^ b).toISize = a.toISize ^^^ b.toISize := rfl
@[simp] theorem UInt8.toInt8_not (a : UInt8) : (~~~a).toInt8 = ~~~a.toInt8 := rfl
@[simp] theorem UInt16.toInt16_not (a : UInt16) : (~~~a).toInt16 = ~~~a.toInt16 := rfl
@[simp] theorem UInt32.toInt32_not (a : UInt32) : (~~~a).toInt32 = ~~~a.toInt32 := rfl
@[simp] theorem UInt64.toInt64_not (a : UInt64) : (~~~a).toInt64 = ~~~a.toInt64 := rfl
@[simp] theorem USize.toISize_not (a : USize) : (~~~a).toISize = ~~~a.toISize := rfl

View File

@@ -7,6 +7,8 @@ prelude
import Init.Data.Float
import Init.Data.SInt.Basic
set_option linter.missingDocs true
/--
Truncates the value to the nearest integer, rounding towards zero.
If NaN, returns `0`.
@@ -59,13 +61,25 @@ If smaller than the minimum value for `ISize` (including -Inf), returns the mini
@[extern "lean_int16_to_float"] opaque Int16.toFloat (n : Int16) : Float
/-- Obtains the `Float` whose value is the same as the given `Int32`. -/
@[extern "lean_int32_to_float"] opaque Int32.toFloat (n : Int32) : Float
/-- Obtains a `Float` whose value is near the given `Int64`. It will be exactly the value of the
given `Int64` if such a `Float` exists. If no such `Float` exists, the returned value will either
be the smallest `Float` this is larger than the given value, or the largest `Float` this is smaller
than the given value. -/
/--
Obtains a `Float` whose value is near the given `Int64`.
It will be exactly the value of the given `Int64` if such a `Float` exists. If no such `Float`
exists, the returned value will either be the smallest `Float` that is larger than the given value,
or the largest `Float` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_int64_to_float"] opaque Int64.toFloat (n : Int64) : Float
/-- Obtains a `Float` whose value is near the given `ISize`. It will be exactly the value of the
given `ISize` if such a `Float` exists. If no such `Float` exists, the returned value will either
be the smallest `Float` this is larger than the given value, or the largest `Float` this is smaller
than the given value. -/
/--
Obtains a `Float` whose value is near the given `ISize`.
It will be exactly the value of the given `ISize` if such a `Float` exists. If no such `Float`
exists, the returned value will either be the smallest `Float` that is larger than the given value,
or the largest `Float` that is smaller than the given value.
This function is opaque in the kernel, but is overridden at runtime with an efficient
implementation.
-/
@[extern "lean_isize_to_float"] opaque ISize.toFloat (n : ISize) : Float

View File

@@ -7,6 +7,8 @@ prelude
import Init.Data.Float32
import Init.Data.SInt.Basic
set_option linter.missingDocs true
/--
Truncates the value to the nearest integer, rounding towards zero.
If NaN, returns `0`.
@@ -57,18 +59,27 @@ If smaller than the minimum value for `ISize` (including -Inf), returns the mini
@[extern "lean_int8_to_float32"] opaque Int8.toFloat32 (n : Int8) : Float32
/-- Obtains the `Float32` whose value is the same as the given `Int16`. -/
@[extern "lean_int16_to_float32"] opaque Int16.toFloat32 (n : Int16) : Float32
/-- Obtains a `Float32` whose value is near the given `Int32`. It will be exactly the value of the
given `Int32` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
than the given value. -/
/--
Obtains a `Float32` whose value is near the given `Int32`.
It will be exactly the value of the given `Int32` if such a `Float32` exists. If no such `Float32`
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
-/
@[extern "lean_int32_to_float32"] opaque Int32.toFloat32 (n : Int32) : Float32
/-- Obtains a `Float32` whose value is near the given `Int64`. It will be exactly the value of the
given `Int64` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
than the given value. -/
/--
Obtains a `Float32` whose value is near the given `Int64`.
It will be exactly the value of the given `Int64` if such a `Float32` exists. If no such `Float32`
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
-/
@[extern "lean_int64_to_float32"] opaque Int64.toFloat32 (n : Int64) : Float32
/-- Obtains a `Float32` whose value is near the given `ISize`. It will be exactly the value of the
given `ISize` if such a `Float32` exists. If no such `Float32` exists, the returned value will either
be the smallest `Float32` this is larger than the given value, or the largest `Float32` this is smaller
than the given value. -/
/--
Obtains a `Float32` whose value is near the given `ISize`.
It will be exactly the value of the given `ISize` if such a `Float32` exists. If no such `Float32`
exists, the returned value will either be the smallest `Float32` that is larger than the given
value, or the largest `Float32` that is smaller than the given value.
-/
@[extern "lean_isize_to_float32"] opaque ISize.toFloat32 (n : ISize) : Float32

File diff suppressed because it is too large Load Diff

View File

@@ -9,15 +9,31 @@ import Init.Data.UInt.Lemmas
namespace String
/-- Interpret the string as the decimal representation of a natural number.
/--
Interprets a string as the decimal representation of a natural number, returning it. Panics if the
string does not contain a decimal natural number.
Panics if the string is not a string of digits. -/
A string can be interpreted as a decimal natural number if it is not empty and all the characters in
it are digits.
Use `String.isNat` to check whether `String.toNat!` would return a value. `String.toNat?` is a safer
alternative that returns `none` instead of panicking when the string is not a natural number.
Examples:
* `"0".toNat! = 0`
* `"5".toNat! = 5`
* `"587".toNat! = 587`
-/
def toNat! (s : String) : Nat :=
if s.isNat then
s.foldl (fun n c => n*10 + (c.toNat - '0'.toNat)) 0
else
panic! "Nat expected"
/--
Decodes the UTF-8 character sequence that starts at a given index in a byte array, or `none` if
index `i` is out of bounds or is not the start of a valid UTF-8 character.
-/
def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
let c a[i]?
if c &&& 0x80 == 0 then
@@ -28,7 +44,7 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
let r := ((c &&& 0x1f).toUInt32 <<< 6) ||| (c1 &&& 0x3f).toUInt32
guard (0x80 r)
-- TODO: Prove h from the definition of r once we have the necessary lemmas
if h : r < 0xd800 then some r, .inl (UInt32.toNat_lt_of_lt (by decide) h) else none
if h : r < 0xd800 then some r, .inl ((UInt32.lt_ofNat_iff (by decide)).1 h) else none
else if c &&& 0xf0 == 0xe0 then
let c1 a[i+1]?
let c2 a[i+2]?
@@ -42,8 +58,8 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
if h : r < 0xd800 0xdfff < r r < 0x110000 then
have :=
match h with
| .inl h => Or.inl (UInt32.toNat_lt_of_lt (by decide) h)
| .inr h => Or.inr UInt32.lt_toNat_of_lt (by decide) h.left, UInt32.toNat_lt_of_lt (by decide) h.right
| .inl h => Or.inl ((UInt32.lt_ofNat_iff (by decide)).1 h)
| .inr h => Or.inr (UInt32.ofNat_lt_iff (by decide)).1 h.left, (UInt32.lt_ofNat_iff (by decide)).1 h.right
some r, this
else
none
@@ -58,12 +74,14 @@ def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
((c2 &&& 0x3f).toUInt32 <<< 6) |||
(c3 &&& 0x3f).toUInt32
if h : 0x10000 r r < 0x110000 then
some r, .inr Nat.lt_of_lt_of_le (by decide) (UInt32.le_toNat_of_le (by decide) h.left), UInt32.toNat_lt_of_lt (by decide) h.right
some r, .inr Nat.lt_of_lt_of_le (by decide) ((UInt32.ofNat_le_iff (by decide)).1 h.left), (UInt32.lt_ofNat_iff (by decide)).1 h.right
else none
else
none
/-- Returns true if the given byte array consists of valid UTF-8. -/
/--
Checks whether an array of bytes is a valid UTF-8 encoding of a string.
-/
@[extern "lean_string_validate_utf8"]
def validateUTF8 (a : @& ByteArray) : Bool :=
(loop 0).isSome
@@ -76,7 +94,11 @@ where
termination_by a.size - i
decreasing_by exact Nat.sub_lt_sub_left _ (Nat.lt_add_of_pos_right c.utf8Size_pos)
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`. -/
/--
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
the corresponding string. Invalid UTF-8 characters in the byte array result in `(default : Char)`,
or `'A'`, in the string.
-/
@[extern "lean_string_from_utf8_unchecked"]
def fromUTF8 (a : @& ByteArray) (h : validateUTF8 a) : String :=
loop 0 ""
@@ -89,16 +111,23 @@ where
termination_by a.size - i
decreasing_by exact Nat.sub_lt_sub_left _ (Nat.lt_add_of_pos_right c.utf8Size_pos)
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
or returns `none` if `a` is not properly UTF-8 encoded. -/
/--
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
the corresponding string, or returns `none` if the array is not a valid UTF-8 encoding of a string.
-/
@[inline] def fromUTF8? (a : ByteArray) : Option String :=
if h : validateUTF8 a then fromUTF8 a h else none
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
or panics if `a` is not properly UTF-8 encoded. -/
/--
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
the corresponding string, or panics if the array is not a valid UTF-8 encoding of a string.
-/
@[inline] def fromUTF8! (a : ByteArray) : String :=
if h : validateUTF8 a then fromUTF8 a h else panic! "invalid UTF-8 string"
/--
Returns the sequence of bytes in a character's UTF-8 encoding.
-/
def utf8EncodeChar (c : Char) : List UInt8 :=
let v := c.val
if v 0x7f then
@@ -122,7 +151,9 @@ def utf8EncodeChar (c : Char) : List UInt8 :=
cases Decidable.em (c.val 0x7ff) <;> simp [*]
cases Decidable.em (c.val 0xffff) <;> simp [*]
/-- Converts the given `String` to a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded byte array. -/
/--
Encodes a string in UTF-8 as an array of bytes.
-/
@[extern "lean_string_to_utf8"]
def toUTF8 (a : @& String) : ByteArray :=
a.data.flatMap utf8EncodeChar
@@ -131,7 +162,11 @@ def toUTF8 (a : @& String) : ByteArray :=
simp [toUTF8, ByteArray.size, Array.size, utf8ByteSize, List.flatMap]
induction s.data <;> simp [List.map, List.flatten, utf8ByteSize.go, Nat.add_comm, *]
/-- Accesses a byte in the UTF-8 encoding of the `String`. O(1) -/
/--
Accesses the indicated byte in the UTF-8 encoding of a string.
At runtime, this function is implemented by efficient, constant-time code.
-/
@[extern "lean_string_get_byte_fast"]
def getUtf8Byte (s : @& String) (n : Nat) (h : n < s.utf8ByteSize) : UInt8 :=
(toUTF8 s)[n]'(size_toUTF8 _ h)
@@ -154,12 +189,21 @@ macro_rules
namespace Iterator
/-- Advance the given iterator until the predicate returns true or the end of the string is reached. -/
/--
Moves the iterator forward until the Boolean predicate `p` returns `true` for the iterator's current
character or until the end of the string is reached. Does nothing if the current character already
satisfies `p`.
-/
@[specialize] def find (it : Iterator) (p : Char Bool) : Iterator :=
if it.atEnd then it
else if p it.curr then it
else find it.next p
/--
Iterates over a string, updating a state at each character using the provided function `f`, until
`f` returns `none`. Begins with the state `init`. Returns the state and character for which `f`
returns `none`.
-/
@[specialize] def foldUntil (it : Iterator) (init : α) (f : α Char Option α) : α × Iterator :=
if it.atEnd then
(init, it)
@@ -202,14 +246,33 @@ where
else saveLine it.next (r.push it.curr)
termination_by (it, 0)
/--
Consistently de-indents the lines in a string, removing the same amount of leading whitespace from
each line such that the least-indented line has no leading whitespace.
The number of leading whitespace characters to remove from each line is determined by counting the
number of leading space (`' '`) and tab (`'\t'`) characters on lines after the first line that also
contain non-whitespace characters. No distinction is made between tab and space characters; both
count equally.
The least number of leading whitespace characters found is then removed from the beginning of each
line. The first line's leading whitespace is not counted when determining how far to de-indent the
string, but leading whitespace is removed from it.
Examples:
* `"Here:\n fun x =>\n x + 1".removeLeadingSpaces = "Here:\nfun x =>\n x + 1"`
* `"Here:\n\t\tfun x =>\n\t \tx + 1".removeLeadingSpaces = "Here:\nfun x =>\n \tx + 1"`
* `"Here:\n\t\tfun x =>\n \n\t \tx + 1".removeLeadingSpaces = "Here:\nfun x =>\n\n \tx + 1"`
-/
def removeLeadingSpaces (s : String) : String :=
let n := findLeadingSpacesSize s
if n == 0 then s else removeNumLeadingSpaces n s
/--
Replaces each `\r\n` with `\n` to normalize line endings,
but does not validate that there are no isolated `\r` characters.
It is an optimized version of `String.replace text "\r\n" "\n"`.
Replaces each `\r\n` with `\n` to normalize line endings, but does not validate that there are no
isolated `\r` characters.
This is an optimized version of `String.replace text "\r\n" "\n"`.
-/
def crlfToLf (text : String) : String :=
go "" 0 0

View File

@@ -128,6 +128,27 @@ instance {α : Type u} {β : α → Type v} [ToString α] [∀ x, ToString (β x
instance {α : Type u} {p : α Prop} [ToString α] : ToString (Subtype p) := fun s =>
toString (val s)
/--
Interprets a string as the decimal representation of an integer, returning it. Returns `none` if the
string does not contain a decimal integer.
A string can be interpreted as a decimal integer if it is not empty, its first character is either
`'-'` or a digit, and all remaining characters are digits.
Use `String.isInt` to check whether `String.toInt?` would return `some`. `String.toInt!` is an
alternative that panics instead of returning `none` when the string is not an integer.
Examples:
* `"".toInt? = none`
* `"0".toInt? = some 0`
* `"5".toInt? = some 5`
* `"-5".toInt? = some (-5)`
* `"587".toInt? = some 587`
* `"-587".toInt? = some (-587)`
* `" 5".toInt? = none`
* `"2-3".toInt? = none`
* `"0xff".toInt? = none`
-/
def String.toInt? (s : String) : Option Int := do
if s.get 0 = '-' then do
let v (s.toSubstring.drop 1).toNat?;
@@ -135,12 +156,48 @@ def String.toInt? (s : String) : Option Int := do
else
Int.ofNat <$> s.toNat?
/--
Checks whether the string can be interpreted as the decimal representation of an integer.
A string can be interpreted as a decimal integer if it is not empty, its first character is
`'-'` or a digit, and all subsequent characters are digits. Leading `+` characters are not allowed.
Use `String.toInt?` or `String.toInt!` to convert such a string to an integer.
Examples:
* `"".isInt = false`
* `"0".isInt = true`
* `"-0".isInt = true`
* `"5".isInt = true`
* `"587".isInt = true`
* `"-587".isInt = true`
* `"+587".isInt = false`
* `" 5".isInt = false`
* `"2-3".isInt = false`
* `"0xff".isInt = false`
-/
def String.isInt (s : String) : Bool :=
if s.get 0 = '-' then
(s.toSubstring.drop 1).isNat
else
s.isNat
/--
Interprets a string as the decimal representation of an integer, returning it. Panics if the string
does not contain a decimal integer.
A string can be interpreted as a decimal integer if it is not empty, its first character is `'-'` or
a digit, and all remaining characters are digits.
Use `String.isInt` to check whether `String.toInt!` would return a value. `String.toInt?` is a safer
alternative that returns `none` instead of panicking when the string is not an integer.
Examples:
* `"0".toInt! = 0`
* `"5".toInt! = 5`
* `"587".toInt! = 587`
* `"-587".toInt! = -587`
-/
def String.toInt! (s : String) : Int :=
match s.toInt? with
| some v => v

View File

@@ -7,6 +7,8 @@ prelude
import Init.Data.UInt.BasicAux
import Init.Data.BitVec.Basic
set_option linter.missingDocs true
open Nat
/-- Converts a `Fin UInt8.size` into the corresponding `UInt8`. -/
@@ -18,29 +20,110 @@ def UInt8.mk (bitVec : BitVec 8) : UInt8 :=
def UInt8.ofNatCore (n : Nat) (h : n < UInt8.size) : UInt8 :=
UInt8.ofNatLT n h
/--
Adds two 8-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_add"]
def UInt8.add (a b : UInt8) : UInt8 := a.toBitVec + b.toBitVec
/--
Subtracts one 8-bit unsigned integer from another, wrapping around on underflow. Usually accessed
via the `-` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_sub"]
def UInt8.sub (a b : UInt8) : UInt8 := a.toBitVec - b.toBitVec
/--
Multiplies two 8-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_mul"]
def UInt8.mul (a b : UInt8) : UInt8 := a.toBitVec * b.toBitVec
/--
Unsigned division for 8-bit unsigned integers, discarding the remainder. Usually accessed
via the `/` operator.
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_div"]
def UInt8.div (a b : UInt8) : UInt8 := BitVec.udiv a.toBitVec b.toBitVec
/--
The modulo operator for 8-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
When the divisor is `0`, the result is the dividend rather than an error.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt8.mod 5 2 = 1`
* `UInt8.mod 4 2 = 0`
* `UInt8.mod 4 0 = 4`
-/
@[extern "lean_uint8_mod"]
def UInt8.mod (a b : UInt8) : UInt8 := BitVec.umod a.toBitVec b.toBitVec
set_option linter.missingDocs false in
@[deprecated UInt8.mod (since := "2024-09-23")]
def UInt8.modn (a : UInt8) (n : Nat) : UInt8 := Fin.modn a.toFin n
/--
Bitwise and for 8-bit unsigned integers. Usually accessed via the `&&&` operator.
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_land"]
def UInt8.land (a b : UInt8) : UInt8 := a.toBitVec &&& b.toBitVec
/--
Bitwise or for 8-bit unsigned integers. Usually accessed via the `|||` operator.
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_lor"]
def UInt8.lor (a b : UInt8) : UInt8 := a.toBitVec ||| b.toBitVec
/--
Bitwise exclusive or for 8-bit unsigned integers. Usually accessed via the `^^^` operator.
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_xor"]
def UInt8.xor (a b : UInt8) : UInt8 := a.toBitVec ^^^ b.toBitVec
/--
Bitwise left shift for 8-bit unsigned integers. Usually accessed via the `<<<` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_shift_left"]
def UInt8.shiftLeft (a b : UInt8) : UInt8 := a.toBitVec <<< (mod b 8).toBitVec
/--
Bitwise right shift for 8-bit unsigned integers. Usually accessed via the `>>>` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_shift_right"]
def UInt8.shiftRight (a b : UInt8) : UInt8 := a.toBitVec >>> (mod b 8).toBitVec
/--
Strict inequality of 8-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `<` operator.
-/
def UInt8.lt (a b : UInt8) : Prop := a.toBitVec < b.toBitVec
/--
Non-strict inequality of 8-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `≤` operator.
-/
def UInt8.le (a b : UInt8) : Prop := a.toBitVec b.toBitVec
instance : Add UInt8 := UInt8.add
@@ -55,10 +138,28 @@ instance : Div UInt8 := ⟨UInt8.div⟩
instance : LT UInt8 := UInt8.lt
instance : LE UInt8 := UInt8.le
/--
Bitwise complement, also known as bitwise negation, for 8-bit unsigned integers. Usually accessed
via the `~~~` prefix operator.
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_complement"]
def UInt8.complement (a : UInt8) : UInt8 := ~~~a.toBitVec
/--
Negation of 8-bit unsigned integers, computed modulo `UInt8.size`.
`UInt8.neg a` is equivalent to `255 - a + 1`.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_neg"]
def UInt8.neg (a : UInt8) : UInt8 := -a.toBitVec
instance : Complement UInt8 := UInt8.complement
instance : Neg UInt8 := UInt8.neg
instance : AndOp UInt8 := UInt8.land
instance : OrOp UInt8 := UInt8.lor
instance : Xor UInt8 := UInt8.xor
@@ -71,10 +172,33 @@ Converts `true` to `1` and `false` to `0`.
@[extern "lean_bool_to_uint8"]
def Bool.toUInt8 (b : Bool) : UInt8 := if b then 1 else 0
/--
Decides whether one 8-bit unsigned integer is strictly less than another. Usually accessed via the
`DecidableLT UInt8` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (6 : UInt8) < 7 then "yes" else "no") = "yes"`
* `(if (5 : UInt8) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : UInt8) < 7) by decide`
-/
@[extern "lean_uint8_dec_lt"]
def UInt8.decLt (a b : UInt8) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
/--
Decides whether one 8-bit unsigned integer is less than or equal to another. Usually accessed via the
`DecidableLE UInt8` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (15 : UInt8) ≤ 15 then "yes" else "no") = "yes"`
* `(if (15 : UInt8) ≤ 5 then "yes" else "no") = "no"`
* `(if (5 : UInt8) ≤ 15 then "yes" else "no") = "yes"`
* `show (7 : UInt8) ≤ 7 by decide`
-/
@[extern "lean_uint8_dec_le"]
def UInt8.decLe (a b : UInt8) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
@@ -93,29 +217,110 @@ def UInt16.mk (bitVec : BitVec 16) : UInt16 :=
def UInt16.ofNatCore (n : Nat) (h : n < UInt16.size) : UInt16 :=
UInt16.ofNatLT n h
/--
Adds two 16-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_add"]
def UInt16.add (a b : UInt16) : UInt16 := a.toBitVec + b.toBitVec
/--
Subtracts one 16-bit unsigned integer from another, wrapping around on underflow. Usually accessed
via the `-` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_sub"]
def UInt16.sub (a b : UInt16) : UInt16 := a.toBitVec - b.toBitVec
/--
Multiplies two 16-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_mul"]
def UInt16.mul (a b : UInt16) : UInt16 := a.toBitVec * b.toBitVec
/--
Unsigned division for 16-bit unsigned integers, discarding the remainder. Usually accessed
via the `/` operator.
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_div"]
def UInt16.div (a b : UInt16) : UInt16 := BitVec.udiv a.toBitVec b.toBitVec
/--
The modulo operator for 16-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
When the divisor is `0`, the result is the dividend rather than an error.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt16.mod 5 2 = 1`
* `UInt16.mod 4 2 = 0`
* `UInt16.mod 4 0 = 4`
-/
@[extern "lean_uint16_mod"]
def UInt16.mod (a b : UInt16) : UInt16 := BitVec.umod a.toBitVec b.toBitVec
set_option linter.missingDocs false in
@[deprecated UInt16.mod (since := "2024-09-23")]
def UInt16.modn (a : UInt16) (n : Nat) : UInt16 := Fin.modn a.toFin n
/--
Bitwise and for 16-bit unsigned integers. Usually accessed via the `&&&` operator.
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_land"]
def UInt16.land (a b : UInt16) : UInt16 := a.toBitVec &&& b.toBitVec
/--
Bitwise or for 16-bit unsigned integers. Usually accessed via the `|||` operator.
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_lor"]
def UInt16.lor (a b : UInt16) : UInt16 := a.toBitVec ||| b.toBitVec
/--
Bitwise exclusive or for 8-bit unsigned integers. Usually accessed via the `^^^` operator.
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_xor"]
def UInt16.xor (a b : UInt16) : UInt16 := a.toBitVec ^^^ b.toBitVec
/--
Bitwise left shift for 16-bit unsigned integers. Usually accessed via the `<<<` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_shift_left"]
def UInt16.shiftLeft (a b : UInt16) : UInt16 := a.toBitVec <<< (mod b 16).toBitVec
/--
Bitwise right shift for 16-bit unsigned integers. Usually accessed via the `>>>` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_shift_right"]
def UInt16.shiftRight (a b : UInt16) : UInt16 := a.toBitVec >>> (mod b 16).toBitVec
/--
Strict inequality of 16-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `<` operator.
-/
def UInt16.lt (a b : UInt16) : Prop := a.toBitVec < b.toBitVec
/--
Non-strict inequality of 16-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `≤` operator.
-/
def UInt16.le (a b : UInt16) : Prop := a.toBitVec b.toBitVec
instance : Add UInt16 := UInt16.add
@@ -130,10 +335,28 @@ instance : Div UInt16 := ⟨UInt16.div⟩
instance : LT UInt16 := UInt16.lt
instance : LE UInt16 := UInt16.le
/--
Bitwise complement, also known as bitwise negation, for 16-bit unsigned integers. Usually accessed
via the `~~~` prefix operator.
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_complement"]
def UInt16.complement (a : UInt16) : UInt16 := ~~~a.toBitVec
/--
Negation of 16-bit unsigned integers, computed modulo `UInt16.size`.
`UInt16.neg a` is equivalent to `65_535 - a + 1`.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_neg"]
def UInt16.neg (a : UInt16) : UInt16 := -a.toBitVec
instance : Complement UInt16 := UInt16.complement
instance : Neg UInt16 := UInt16.neg
instance : AndOp UInt16 := UInt16.land
instance : OrOp UInt16 := UInt16.lor
instance : Xor UInt16 := UInt16.xor
@@ -147,11 +370,34 @@ Converts `true` to `1` and `false` to `0`.
def Bool.toUInt16 (b : Bool) : UInt16 := if b then 1 else 0
set_option bootstrap.genMatcherCode false in
/--
Decides whether one 16-bit unsigned integer is strictly less than another. Usually accessed via the
`DecidableLT UInt16` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (6 : UInt16) < 7 then "yes" else "no") = "yes"`
* `(if (5 : UInt16) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : UInt16) < 7) by decide`
-/
@[extern "lean_uint16_dec_lt"]
def UInt16.decLt (a b : UInt16) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
set_option bootstrap.genMatcherCode false in
/--
Decides whether one 16-bit unsigned integer is less than or equal to another. Usually accessed via the
`DecidableLE UInt16` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (15 : UInt16) ≤ 15 then "yes" else "no") = "yes"`
* `(if (15 : UInt16) ≤ 5 then "yes" else "no") = "no"`
* `(if (5 : UInt16) ≤ 15 then "yes" else "no") = "yes"`
* `show (7 : UInt16) ≤ 7 by decide`
-/
@[extern "lean_uint16_dec_le"]
def UInt16.decLe (a b : UInt16) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
@@ -170,29 +416,110 @@ def UInt32.mk (bitVec : BitVec 32) : UInt32 :=
def UInt32.ofNatCore (n : Nat) (h : n < UInt32.size) : UInt32 :=
UInt32.ofNatLT n h
/--
Adds two 32-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_add"]
def UInt32.add (a b : UInt32) : UInt32 := a.toBitVec + b.toBitVec
/--
Subtracts one 32-bit unsigned integer from another, wrapping around on underflow. Usually accessed
via the `-` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_sub"]
def UInt32.sub (a b : UInt32) : UInt32 := a.toBitVec - b.toBitVec
/--
Multiplies two 32-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_mul"]
def UInt32.mul (a b : UInt32) : UInt32 := a.toBitVec * b.toBitVec
/--
Unsigned division for 32-bit unsigned integers, discarding the remainder. Usually accessed
via the `/` operator.
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_div"]
def UInt32.div (a b : UInt32) : UInt32 := BitVec.udiv a.toBitVec b.toBitVec
/--
The modulo operator for 32-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
When the divisor is `0`, the result is the dividend rather than an error.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt32.mod 5 2 = 1`
* `UInt32.mod 4 2 = 0`
* `UInt32.mod 4 0 = 4`
-/
@[extern "lean_uint32_mod"]
def UInt32.mod (a b : UInt32) : UInt32 := BitVec.umod a.toBitVec b.toBitVec
set_option linter.missingDocs false in
@[deprecated UInt32.mod (since := "2024-09-23")]
def UInt32.modn (a : UInt32) (n : Nat) : UInt32 := Fin.modn a.toFin n
/--
Bitwise and for 32-bit unsigned integers. Usually accessed via the `&&&` operator.
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_land"]
def UInt32.land (a b : UInt32) : UInt32 := a.toBitVec &&& b.toBitVec
/--
Bitwise or for 32-bit unsigned integers. Usually accessed via the `|||` operator.
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_lor"]
def UInt32.lor (a b : UInt32) : UInt32 := a.toBitVec ||| b.toBitVec
/--
Bitwise exclusive or for 32-bit unsigned integers. Usually accessed via the `^^^` operator.
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_xor"]
def UInt32.xor (a b : UInt32) : UInt32 := a.toBitVec ^^^ b.toBitVec
/--
Bitwise left shift for 32-bit unsigned integers. Usually accessed via the `<<<` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_shift_left"]
def UInt32.shiftLeft (a b : UInt32) : UInt32 := a.toBitVec <<< (mod b 32).toBitVec
/--
Bitwise right shift for 32-bit unsigned integers. Usually accessed via the `>>>` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_shift_right"]
def UInt32.shiftRight (a b : UInt32) : UInt32 := a.toBitVec >>> (mod b 32).toBitVec
/--
Strict inequality of 32-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `<` operator.
-/
def UInt32.lt (a b : UInt32) : Prop := a.toBitVec < b.toBitVec
/--
Non-strict inequality of 32-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `≤` operator.
-/
def UInt32.le (a b : UInt32) : Prop := a.toBitVec b.toBitVec
instance : Add UInt32 := UInt32.add
@@ -207,10 +534,28 @@ instance : Div UInt32 := ⟨UInt32.div⟩
instance : LT UInt32 := UInt32.lt
instance : LE UInt32 := UInt32.le
/--
Bitwise complement, also known as bitwise negation, for 32-bit unsigned integers. Usually accessed
via the `~~~` prefix operator.
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_complement"]
def UInt32.complement (a : UInt32) : UInt32 := ~~~a.toBitVec
/--
Negation of 32-bit unsigned integers, computed modulo `UInt32.size`.
`UInt32.neg a` is equivalent to `429_4967_295 - a + 1`.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_neg"]
def UInt32.neg (a : UInt32) : UInt32 := -a.toBitVec
instance : Complement UInt32 := UInt32.complement
instance : Neg UInt32 := UInt32.neg
instance : AndOp UInt32 := UInt32.land
instance : OrOp UInt32 := UInt32.lor
instance : Xor UInt32 := UInt32.xor
@@ -232,29 +577,110 @@ def UInt64.mk (bitVec : BitVec 64) : UInt64 :=
def UInt64.ofNatCore (n : Nat) (h : n < UInt64.size) : UInt64 :=
UInt64.ofNatLT n h
/--
Adds two 64-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_add"]
def UInt64.add (a b : UInt64) : UInt64 := a.toBitVec + b.toBitVec
/--
Subtracts one 64-bit unsigned integer from another, wrapping around on underflow. Usually accessed
via the `-` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_sub"]
def UInt64.sub (a b : UInt64) : UInt64 := a.toBitVec - b.toBitVec
/--
Multiplies two 64-bit unsigned integers, wrapping around on overflow. Usually accessed via the `*`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_mul"]
def UInt64.mul (a b : UInt64) : UInt64 := a.toBitVec * b.toBitVec
/--
Unsigned division for 64-bit unsigned integers, discarding the remainder. Usually accessed
via the `/` operator.
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_div"]
def UInt64.div (a b : UInt64) : UInt64 := BitVec.udiv a.toBitVec b.toBitVec
/--
The modulo operator for 64-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
When the divisor is `0`, the result is the dividend rather than an error.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt64.mod 5 2 = 1`
* `UInt64.mod 4 2 = 0`
* `UInt64.mod 4 0 = 4`
-/
@[extern "lean_uint64_mod"]
def UInt64.mod (a b : UInt64) : UInt64 := BitVec.umod a.toBitVec b.toBitVec
set_option linter.missingDocs false in
@[deprecated UInt64.mod (since := "2024-09-23")]
def UInt64.modn (a : UInt64) (n : Nat) : UInt64 := Fin.modn a.toFin n
/--
Bitwise and for 64-bit unsigned integers. Usually accessed via the `&&&` operator.
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_land"]
def UInt64.land (a b : UInt64) : UInt64 := a.toBitVec &&& b.toBitVec
/--
Bitwise or for 64-bit unsigned integers. Usually accessed via the `|||` operator.
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_lor"]
def UInt64.lor (a b : UInt64) : UInt64 := a.toBitVec ||| b.toBitVec
/--
Bitwise exclusive or for 64-bit unsigned integers. Usually accessed via the `^^^` operator.
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_xor"]
def UInt64.xor (a b : UInt64) : UInt64 := a.toBitVec ^^^ b.toBitVec
/--
Bitwise left shift for 64-bit unsigned integers. Usually accessed via the `<<<` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_shift_left"]
def UInt64.shiftLeft (a b : UInt64) : UInt64 := a.toBitVec <<< (mod b 64).toBitVec
/--
Bitwise right shift for 64-bit unsigned integers. Usually accessed via the `>>>` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_shift_right"]
def UInt64.shiftRight (a b : UInt64) : UInt64 := a.toBitVec >>> (mod b 64).toBitVec
/--
Strict inequality of 64-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `<` operator.
-/
def UInt64.lt (a b : UInt64) : Prop := a.toBitVec < b.toBitVec
/--
Non-strict inequality of 64-bit unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `≤` operator.
-/
def UInt64.le (a b : UInt64) : Prop := a.toBitVec b.toBitVec
instance : Add UInt64 := UInt64.add
@@ -269,10 +695,28 @@ instance : Div UInt64 := ⟨UInt64.div⟩
instance : LT UInt64 := UInt64.lt
instance : LE UInt64 := UInt64.le
/--
Bitwise complement, also known as bitwise negation, for 64-bit unsigned integers. Usually accessed
via the `~~~` prefix operator.
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_complement"]
def UInt64.complement (a : UInt64) : UInt64 := ~~~a.toBitVec
/--
Negation of 32-bit unsigned integers, computed modulo `UInt64.size`.
`UInt64.neg a` is equivalent to `18_446_744_073_709_551_615 - a + 1`.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_neg"]
def UInt64.neg (a : UInt64) : UInt64 := -a.toBitVec
instance : Complement UInt64 := UInt64.complement
instance : Neg UInt64 := UInt64.neg
instance : AndOp UInt64 := UInt64.land
instance : OrOp UInt64 := UInt64.lor
instance : Xor UInt64 := UInt64.xor
@@ -285,10 +729,33 @@ Converts `true` to `1` and `false` to `0`.
@[extern "lean_bool_to_uint64"]
def Bool.toUInt64 (b : Bool) : UInt64 := if b then 1 else 0
/--
Decides whether one 64-bit unsigned integer is strictly less than another. Usually accessed via the
`DecidableLT UInt64` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (6 : UInt64) < 7 then "yes" else "no") = "yes"`
* `(if (5 : UInt64) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : UInt64) < 7) by decide`
-/
@[extern "lean_uint64_dec_lt"]
def UInt64.decLt (a b : UInt64) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
/--
Decides whether one 64-bit unsigned integer is less than or equal to another. Usually accessed via the
`DecidableLE UInt64` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (15 : UInt64) ≤ 15 then "yes" else "no") = "yes"`
* `(if (15 : UInt64) ≤ 5 then "yes" else "no") = "no"`
* `(if (5 : UInt64) ≤ 15 then "yes" else "no") = "yes"`
* `show (7 : UInt64) ≤ 7 by decide`
-/
@[extern "lean_uint64_dec_le"]
def UInt64.decLe (a b : UInt64) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))
@@ -318,53 +785,152 @@ theorem usize_size_le : USize.size ≤ 18446744073709551616 :=
theorem le_usize_size : 4294967296 USize.size :=
USize.le_size
/--
Multiplies two word-sized unsigned integers, wrapping around on overflow. Usually accessed via the
`*` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_mul"]
def USize.mul (a b : USize) : USize := a.toBitVec * b.toBitVec
/--
Unsigned division for word-sized unsigned integers, discarding the remainder. Usually accessed
via the `/` operator.
This operation is sometimes called “floor division.” Division by zero is defined to be zero.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_div"]
def USize.div (a b : USize) : USize := a.toBitVec / b.toBitVec
/--
The modulo operator for word-sized unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
When the divisor is `0`, the result is the dividend rather than an error.
This function is overridden at runtime with an efficient implementation.
Examples:
* `USize.mod 5 2 = 1`
* `USize.mod 4 2 = 0`
* `USize.mod 4 0 = 4`
-/
@[extern "lean_usize_mod"]
def USize.mod (a b : USize) : USize := a.toBitVec % b.toBitVec
set_option linter.missingDocs false in
@[deprecated USize.mod (since := "2024-09-23")]
def USize.modn (a : USize) (n : Nat) : USize := Fin.modn a.toFin n
/--
Bitwise and for word-sized unsigned integers. Usually accessed via the `&&&` operator.
Each bit of the resulting integer is set if the corresponding bits of both input integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_land"]
def USize.land (a b : USize) : USize := a.toBitVec &&& b.toBitVec
/--
Bitwise or for word-sized unsigned integers. Usually accessed via the `|||` operator.
Each bit of the resulting integer is set if at least one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_lor"]
def USize.lor (a b : USize) : USize := a.toBitVec ||| b.toBitVec
/--
Bitwise exclusive or for word-sized unsigned integers. Usually accessed via the `^^^` operator.
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
integers are set.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_xor"]
def USize.xor (a b : USize) : USize := a.toBitVec ^^^ b.toBitVec
/--
Bitwise left shift for word-sized unsigned integers. Usually accessed via the `<<<` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_shift_left"]
def USize.shiftLeft (a b : USize) : USize := a.toBitVec <<< (mod b (USize.ofNat System.Platform.numBits)).toBitVec
/--
Bitwise right shift for word-sized unsigned integers. Usually accessed via the `>>>` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_shift_right"]
def USize.shiftRight (a b : USize) : USize := a.toBitVec >>> (mod b (USize.ofNat System.Platform.numBits)).toBitVec
/--
Upcast a `Nat` less than `2^32` to a `USize`.
This is lossless because `USize.size` is either `2^32` or `2^64`.
This function is overridden with a native implementation.
Converts a natural number to a `USize`. Overflow is impossible on any supported platform because
`USize.size` is either `2^32` or `2^64`.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_of_nat"]
def USize.ofNat32 (n : @& Nat) (h : n < 4294967296) : USize :=
USize.ofNatLT n (Nat.lt_of_lt_of_le h USize.le_size)
/--
Converts 8-bit unsigned integers to word-sized unsigned integers.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_to_usize"]
def UInt8.toUSize (a : UInt8) : USize :=
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
/--
Converts word-sized unsigned integers to 8-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_to_uint8"]
def USize.toUInt8 (a : USize) : UInt8 := a.toNat.toUInt8
/--
Converts 16-bit unsigned integers to word-sized unsigned integers.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_to_usize"]
def UInt16.toUSize (a : UInt16) : USize :=
USize.ofNat32 a.toBitVec.toNat (Nat.lt_trans a.toBitVec.isLt (by decide))
/--
Converts word-sized unsigned integers to 16-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_to_uint16"]
def USize.toUInt16 (a : USize) : UInt16 := a.toNat.toUInt16
/--
Converts 32-bit unsigned integers to word-sized unsigned integers.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_to_usize"]
def UInt32.toUSize (a : UInt32) : USize := USize.ofNat32 a.toBitVec.toNat a.toBitVec.isLt
/--
Converts word-sized unsigned integers to 32-bit unsigned integers. Wraps around on overflow, which
might occur on 64-bit architectures.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_to_uint32"]
def USize.toUInt32 (a : USize) : UInt32 := a.toNat.toUInt32
/-- Converts a `UInt64` to a `USize` by reducing modulo `USize.size`. -/
/--
Converts 64-bit unsigned integers to word-sized unsigned integers. On 32-bit machines, this may
overflow, which results in the value wrapping around (that is, it is reduced modulo `USize.size`).
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_to_usize"]
def UInt64.toUSize (a : UInt64) : USize := a.toNat.toUSize
/--
Upcast a `USize` to a `UInt64`.
This is lossless because `USize.size` is either `2^32` or `2^64`.
This function is overridden with a native implementation.
Converts word-sized unsigned integers to 32-bit unsigned integers. This cannot overflow because
`USize.size` is either `2^32` or `2^64`.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_to_uint64"]
def USize.toUInt64 (a : USize) : UInt64 :=
@@ -378,10 +944,26 @@ instance : HMod USize Nat USize := ⟨USize.modn⟩
instance : Div USize := USize.div
/--
Bitwise complement, also known as bitwise negation, for word-sized unsigned integers. Usually
accessed via the `~~~` prefix operator.
Each bit of the resulting integer is the opposite of the corresponding bit of the input integer.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_complement"]
def USize.complement (a : USize) : USize := ~~~a.toBitVec
/--
Negation of word-sized unsigned integers, computed modulo `USize.size`.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_neg"]
def USize.neg (a : USize) : USize := -a.toBitVec
instance : Complement USize := USize.complement
instance : Neg USize := USize.neg
instance : AndOp USize := USize.land
instance : OrOp USize := USize.lor
instance : Xor USize := USize.xor

View File

@@ -7,6 +7,8 @@ prelude
import Init.Data.Fin.Basic
import Init.Data.BitVec.BasicAux
set_option linter.missingDocs true
/-!
This module exists to provide the very basic `UInt8` etc. definitions required for
`Init.Data.Char.Basic` and `Init.Data.Array.Basic`. These are very important as they are used in
@@ -20,17 +22,53 @@ open Nat
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
/--
Converts a natural number to an 8-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt8.ofNat 5 = 5`
* `UInt8.ofNat 255 = 255`
* `UInt8.ofNat 256 = 0`
* `UInt8.ofNat 259 = 3`
* `UInt8.ofNat 32770 = 2`
-/
@[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`.
Converts a natural number to an 8-bit unsigned integer, returning the largest representable value if
the number is too large.
Returns `2^8 - 1` for natural numbers greater than or equal to `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)
/--
Converts a natural number to an 8-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `Nat.toUInt8 5 = 5`
* `Nat.toUInt8 255 = 255`
* `Nat.toUInt8 256 = 0`
* `Nat.toUInt8 259 = 3`
* `Nat.toUInt8 32770 = 2`
-/
abbrev Nat.toUInt8 := UInt8.ofNat
/--
Converts an 8-bit unsigned integer to an arbitrary-precision natural number.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_to_nat"]
def UInt8.toNat (n : UInt8) : Nat := n.toBitVec.toNat
@@ -40,21 +78,64 @@ instance UInt8.instOfNat : OfNat UInt8 n := ⟨UInt8.ofNat n⟩
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
/--
Converts a natural number to a 16-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt16.ofNat 5 = 5`
* `UInt16.ofNat 255 = 255`
* `UInt16.ofNat 32770 = 32770`
* `UInt16.ofNat 65537 = 1`
-/
@[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`.
Converts a natural number to a 16-bit unsigned integer, returning the largest representable value if
the number is too large.
Returns `2^16 - 1` for natural numbers greater than or equal to `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)
/--
Converts a natural number to a 16-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `Nat.toUInt16 5 = 5`
* `Nat.toUInt16 255 = 255`
* `Nat.toUInt16 32770 = 32770`
* `Nat.toUInt16 65537 = 1`
-/
abbrev Nat.toUInt16 := UInt16.ofNat
/--
Converts a 16-bit unsigned integer to an arbitrary-precision natural number.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_to_nat"]
def UInt16.toNat (n : UInt16) : Nat := n.toBitVec.toNat
/--
Converts 16-bit unsigned integers to 8-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_to_uint8"]
def UInt16.toUInt8 (a : UInt16) : UInt8 := a.toNat.toUInt8
/--
Converts 8-bit unsigned integers to 16-bit unsigned integers.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_to_uint16"]
def UInt8.toUInt16 (a : UInt8) : UInt16 := a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)
@@ -64,25 +145,70 @@ instance UInt16.instOfNat : OfNat UInt16 n := ⟨UInt16.ofNat n⟩
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
/--
Converts a natural number to a 32-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt32.ofNat 5 = 5`
* `UInt32.ofNat 65539 = 65539`
* `UInt32.ofNat 4_294_967_299 = 3`
-/
@[extern "lean_uint32_of_nat"]
def UInt32.ofNat (n : @& Nat) : UInt32 := BitVec.ofNat 32 n
@[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`.
Converts a natural number to a 32-bit unsigned integer, returning the largest representable value if
the number is too large.
Returns `2^32 - 1` for natural numbers greater than or equal to `2^32`.
-/
def UInt32.ofNatTruncate (n : Nat) : UInt32 :=
if h : n < UInt32.size then
UInt32.ofNatLT n h
else
UInt32.ofNatLT (UInt32.size - 1) (by decide)
/--
Converts a natural number to a 32-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `Nat.toUInt32 5 = 5`
* `Nat.toUInt32 65_539 = 65_539`
* `Nat.toUInt32 4_294_967_299 = 3`
-/
abbrev Nat.toUInt32 := UInt32.ofNat
/--
Converts a 32-bit unsigned integer to an 8-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_to_uint8"]
def UInt32.toUInt8 (a : UInt32) : UInt8 := a.toNat.toUInt8
/--
Converts 32-bit unsigned integers to 16-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_to_uint16"]
def UInt32.toUInt16 (a : UInt32) : UInt16 := a.toNat.toUInt16
/--
Converts 8-bit unsigned integers to 32-bit unsigned integers.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_to_uint32"]
def UInt8.toUInt32 (a : UInt8) : UInt32 := a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)
/--
Converts 16-bit unsigned integers to 32-bit unsigned integers.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_to_uint32"]
def UInt16.toUInt32 (a : UInt16) : UInt32 := a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)
@@ -110,29 +236,89 @@ theorem UInt32.lt_ofNat'_of_lt {n m : Nat} (h1 : n < UInt32.size) (h2 : m < UInt
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
/--
Converts a natural number to a 64-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `UInt64.ofNat 5 = 5`
* `UInt64.ofNat 65539 = 65539`
* `UInt64.ofNat 4_294_967_299 = 4_294_967_299`
* `UInt64.ofNat 18_446_744_073_709_551_620 = 4`
-/
@[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`.
Converts a natural number to a 64-bit unsigned integer, returning the largest representable value if
the number is too large.
Returns `2^64 - 1` for natural numbers greater than or equal to `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)
/--
Converts a natural number to a 64-bit unsigned integer, wrapping on overflow.
This function is overridden at runtime with an efficient implementation.
Examples:
* `Nat.toUInt64 5 = 5`
* `Nat.toUInt64 65539 = 65539`
* `Nat.toUInt64 4_294_967_299 = 4_294_967_299`
* `Nat.toUInt64 18_446_744_073_709_551_620 = 4`
-/
abbrev Nat.toUInt64 := UInt64.ofNat
/--
Converts a 64-bit unsigned integer to an arbitrary-precision natural number.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_to_nat"]
def UInt64.toNat (n : UInt64) : Nat := n.toBitVec.toNat
/--
Converts 64-bit unsigned integers to 8-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_to_uint8"]
def UInt64.toUInt8 (a : UInt64) : UInt8 := a.toNat.toUInt8
/--
Converts 64-bit unsigned integers to 16-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_to_uint16"]
def UInt64.toUInt16 (a : UInt64) : UInt16 := a.toNat.toUInt16
/--
Converts 64-bit unsigned integers to 32-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint64_to_uint32"]
def UInt64.toUInt32 (a : UInt64) : UInt32 := a.toNat.toUInt32
/--
Converts 8-bit unsigned integers to 64-bit unsigned integers.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint8_to_uint64"]
def UInt8.toUInt64 (a : UInt8) : UInt64 := a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)
/--
Converts 16-bit unsigned integers to 64-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint16_to_uint64"]
def UInt16.toUInt64 (a : UInt16) : UInt64 := a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)
/--
Converts 32-bit unsigned integers to 64-bit unsigned integers. Wraps around on overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_uint32_to_uint64"]
def UInt32.toUInt64 (a : UInt32) : UInt64 := a.toNat, Nat.lt_trans a.toBitVec.isLt (by decide)
@@ -153,26 +339,60 @@ theorem usize_size_pos : 0 < 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
/--
Converts an arbitrary-precision natural number to an unsigned word-sized integer, wrapping around on
overflow.
This function is overridden at runtime with an efficient implementation.
-/
@[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`.
Converts a natural number to `USize`, returning the largest representable value if the number is too
large.
Returns `USize.size - 1`, which is `2^64 - 1` or `2^32 - 1` depending on the platform, for natural
numbers greater than or equal to `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
@[inherit_doc USize.ofNat] abbrev Nat.toUSize := USize.ofNat
/--
Converts a word-sized unsigned integer to an arbitrary-precision natural number.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_to_nat"]
def USize.toNat (n : USize) : Nat := n.toBitVec.toNat
/--
Adds two word-sized unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_add"]
def USize.add (a b : USize) : USize := a.toBitVec + b.toBitVec
/--
Subtracts one word-sized-bit unsigned integer from another, wrapping around on underflow. Usually
accessed via the `-` operator.
This function is overridden at runtime with an efficient implementation.
-/
@[extern "lean_usize_sub"]
def USize.sub (a b : USize) : USize := a.toBitVec - b.toBitVec
/--
Strict inequality of word-sized unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `<` operator.
-/
def USize.lt (a b : USize) : Prop := a.toBitVec < b.toBitVec
/--
Non-strict inequality of word-sized unsigned integers, defined as inequality of the corresponding
natural numbers. Usually accessed via the `≤` operator.
-/
def USize.le (a b : USize) : Prop := a.toBitVec b.toBitVec
instance USize.instOfNat : OfNat USize n := USize.ofNat n
@@ -182,10 +402,33 @@ instance : Sub USize := ⟨USize.sub⟩
instance : LT USize := USize.lt
instance : LE USize := USize.le
/--
Decides whether one word-sized unsigned integer is strictly less than another. Usually accessed via
the `DecidableLT USize` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (6 : USize) < 7 then "yes" else "no") = "yes"`
* `(if (5 : USize) < 5 then "yes" else "no") = "no"`
* `show ¬((7 : USize) < 7) by decide`
-/
@[extern "lean_usize_dec_lt"]
def USize.decLt (a b : USize) : Decidable (a < b) :=
inferInstanceAs (Decidable (a.toBitVec < b.toBitVec))
/--
Decides whether one word-sized unsigned integer is less than or equal to another. Usually accessed
via the `DecidableLE USize` instance.
This function is overridden at runtime with an efficient implementation.
Examples:
* `(if (15 : USize) ≤ 15 then "yes" else "no") = "yes"`
* `(if (15 : USize) ≤ 5 then "yes" else "no") = "no"`
* `(if (5 : USize) ≤ 15 then "yes" else "no") = "yes"`
* `show (7 : USize) ≤ 7 by decide`
-/
@[extern "lean_usize_dec_le"]
def USize.decLe (a b : USize) : Decidable (a b) :=
inferInstanceAs (Decidable (a.toBitVec b.toBitVec))

View File

@@ -6,18 +6,12 @@ Authors: Markus Himmel, Mac Malone
prelude
import Init.Data.UInt.Lemmas
import Init.Data.Fin.Bitwise
import Init.Data.BitVec.Lemmas
set_option hygiene false in
macro "declare_bitwise_uint_theorems" typeName:ident bits:term:arg : command =>
`(
namespace $typeName
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec / b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec % b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_not {a : $typeName} : (~~~a).toBitVec = ~~~a.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_and (a b : $typeName) : (a &&& b).toBitVec = a.toBitVec &&& b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_or (a b : $typeName) : (a ||| b).toBitVec = a.toBitVec ||| b.toBitVec := rfl
@@ -70,3 +64,362 @@ theorem Bool.toBitVec_toUSize {b : Bool} :
· simp [toUSize]
· apply BitVec.eq_of_toNat_eq
simp [toUSize]
@[simp] theorem UInt8.toFin_and (a b : UInt8) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt16.toFin_and (a b : UInt16) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt32.toFin_and (a b : UInt32) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt64.toFin_and (a b : UInt64) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem USize.toFin_and (a b : USize) : (a &&& b).toFin = a.toFin &&& b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt8.toUInt16_and (a b : UInt8) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := rfl
@[simp] theorem UInt8.toUInt32_and (a b : UInt8) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := rfl
@[simp] theorem UInt8.toUInt64_and (a b : UInt8) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
@[simp] theorem UInt8.toUSize_and (a b : UInt8) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := rfl
@[simp] theorem UInt16.toUInt8_and (a b : UInt16) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt16.toUInt32_and (a b : UInt16) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := rfl
@[simp] theorem UInt16.toUInt64_and (a b : UInt16) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
@[simp] theorem UInt16.toUSize_and (a b : UInt16) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := rfl
@[simp] theorem UInt32.toUInt8_and (a b : UInt32) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt16_and (a b : UInt32) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt64_and (a b : UInt32) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
@[simp] theorem UInt32.toUSize_and (a b : UInt32) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := rfl
@[simp] theorem USize.toUInt8_and (a b : USize) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt16_and (a b : USize) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt32_and (a b : USize) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt64_and (a b : USize) : (a &&& b).toUInt64 = a.toUInt64 &&& b.toUInt64 := rfl
@[simp] theorem UInt64.toUInt8_and (a b : UInt64) : (a &&& b).toUInt8 = a.toUInt8 &&& b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt16_and (a b : UInt64) : (a &&& b).toUInt16 = a.toUInt16 &&& b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt32_and (a b : UInt64) : (a &&& b).toUInt32 = a.toUInt32 &&& b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUSize_and (a b : UInt64) : (a &&& b).toUSize = a.toUSize &&& b.toUSize := USize.toBitVec_inj.1 (by simp)
@[simp] theorem UInt8.toFin_or (a b : UInt8) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt16.toFin_or (a b : UInt16) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt32.toFin_or (a b : UInt32) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt64.toFin_or (a b : UInt64) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem USize.toFin_or (a b : USize) : (a ||| b).toFin = a.toFin ||| b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt8.toUInt16_or (a b : UInt8) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := rfl
@[simp] theorem UInt8.toUInt32_or (a b : UInt8) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := rfl
@[simp] theorem UInt8.toUInt64_or (a b : UInt8) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
@[simp] theorem UInt8.toUSize_or (a b : UInt8) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := rfl
@[simp] theorem UInt16.toUInt8_or (a b : UInt16) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt16.toUInt32_or (a b : UInt16) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := rfl
@[simp] theorem UInt16.toUInt64_or (a b : UInt16) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
@[simp] theorem UInt16.toUSize_or (a b : UInt16) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := rfl
@[simp] theorem UInt32.toUInt8_or (a b : UInt32) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt16_or (a b : UInt32) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt64_or (a b : UInt32) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
@[simp] theorem UInt32.toUSize_or (a b : UInt32) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := rfl
@[simp] theorem USize.toUInt8_or (a b : USize) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt16_or (a b : USize) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt32_or (a b : USize) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt64_or (a b : USize) : (a ||| b).toUInt64 = a.toUInt64 ||| b.toUInt64 := rfl
@[simp] theorem UInt64.toUInt8_or (a b : UInt64) : (a ||| b).toUInt8 = a.toUInt8 ||| b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt16_or (a b : UInt64) : (a ||| b).toUInt16 = a.toUInt16 ||| b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt32_or (a b : UInt64) : (a ||| b).toUInt32 = a.toUInt32 ||| b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUSize_or (a b : UInt64) : (a ||| b).toUSize = a.toUSize ||| b.toUSize := USize.toBitVec_inj.1 (by simp)
@[simp] theorem UInt8.toFin_xor (a b : UInt8) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt16.toFin_xor (a b : UInt16) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt32.toFin_xor (a b : UInt32) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt64.toFin_xor (a b : UInt64) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem USize.toFin_xor (a b : USize) : (a ^^^ b).toFin = a.toFin ^^^ b.toFin := Fin.val_inj.1 (by simp)
@[simp] theorem UInt8.toUInt16_xor (a b : UInt8) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := rfl
@[simp] theorem UInt8.toUInt32_xor (a b : UInt8) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := rfl
@[simp] theorem UInt8.toUInt64_xor (a b : UInt8) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
@[simp] theorem UInt8.toUSize_xor (a b : UInt8) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := rfl
@[simp] theorem UInt16.toUInt8_xor (a b : UInt16) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt16.toUInt32_xor (a b : UInt16) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := rfl
@[simp] theorem UInt16.toUInt64_xor (a b : UInt16) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
@[simp] theorem UInt16.toUSize_xor (a b : UInt16) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := rfl
@[simp] theorem UInt32.toUInt8_xor (a b : UInt32) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt16_xor (a b : UInt32) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt64_xor (a b : UInt32) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
@[simp] theorem UInt32.toUSize_xor (a b : UInt32) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := rfl
@[simp] theorem USize.toUInt8_xor (a b : USize) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt16_xor (a b : USize) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt32_xor (a b : USize) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt64_xor (a b : USize) : (a ^^^ b).toUInt64 = a.toUInt64 ^^^ b.toUInt64 := rfl
@[simp] theorem UInt64.toUInt8_xor (a b : UInt64) : (a ^^^ b).toUInt8 = a.toUInt8 ^^^ b.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt16_xor (a b : UInt64) : (a ^^^ b).toUInt16 = a.toUInt16 ^^^ b.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt32_xor (a b : UInt64) : (a ^^^ b).toUInt32 = a.toUInt32 ^^^ b.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUSize_xor (a b : UInt64) : (a ^^^ b).toUSize = a.toUSize ^^^ b.toUSize := USize.toBitVec_inj.1 (by simp)
@[simp] theorem UInt8.toNat_not (a : UInt8) : (~~~a).toNat = UInt8.size - 1 - a.toNat := by
rw [ toNat_toBitVec, UInt8.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
@[simp] theorem UInt16.toNat_not (a : UInt16) : (~~~a).toNat = UInt16.size - 1 - a.toNat := by
rw [ toNat_toBitVec, UInt16.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
@[simp] theorem UInt32.toNat_not (a : UInt32) : (~~~a).toNat = UInt32.size - 1 - a.toNat := by
rw [ toNat_toBitVec, UInt32.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
@[simp] theorem UInt64.toNat_not (a : UInt64) : (~~~a).toNat = UInt64.size - 1 - a.toNat := by
rw [ toNat_toBitVec, UInt64.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
@[simp] theorem USize.toNat_not (a : USize) : (~~~a).toNat = USize.size - 1 - a.toNat := by
rw [ toNat_toBitVec, USize.toBitVec_not, BitVec.toNat_not, toNat_toBitVec]
@[simp] theorem UInt8.toFin_not (a : UInt8) : (~~~a).toFin = a.toFin.rev := by
rw [ toFin_toBitVec, UInt8.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
@[simp] theorem UInt16.toFin_not (a : UInt16) : (~~~a).toFin = a.toFin.rev := by
rw [ toFin_toBitVec, UInt16.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
@[simp] theorem UInt32.toFin_not (a : UInt32) : (~~~a).toFin = a.toFin.rev := by
rw [ toFin_toBitVec, UInt32.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
@[simp] theorem UInt64.toFin_not (a : UInt64) : (~~~a).toFin = a.toFin.rev := by
rw [ toFin_toBitVec, UInt64.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
@[simp] theorem USize.toFin_not (a : USize) : (~~~a).toFin = a.toFin.rev := by
rw [ toFin_toBitVec, USize.toBitVec_not, BitVec.toFin_not, toFin_toBitVec]
@[simp] theorem UInt16.toUInt8_not (a : UInt16) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt8_not (a : UInt32) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt8_not (a : UInt64) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt8_not (a : USize) : (~~~a).toUInt8 = ~~~a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt16_not (a : UInt32) : (~~~a).toUInt16 = ~~~a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt16_not (a : UInt64) : (~~~a).toUInt16 = ~~~a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt16_not (a : USize) : (~~~a).toUInt16 = ~~~a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt32_not (a : UInt64) : (~~~a).toUInt32 = ~~~a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt32_not (a : USize) : (~~~a).toUInt32 = ~~~a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUSize_not (a : UInt64) : (~~~a).toUSize = ~~~a.toUSize := USize.toBitVec_inj.1 (by simp)
@[simp] theorem UInt8.toUInt16_not (a : UInt8) : (~~~a).toUInt16 = ~~~a.toUInt16 % 256 := by
simp [UInt8.toUInt16_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt32_not (a : UInt8) : (~~~a).toUInt32 = ~~~a.toUInt32 % 256 := by
simp [UInt8.toUInt32_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt64_not (a : UInt8) : (~~~a).toUInt64 = ~~~a.toUInt64 % 256 := by
simp [UInt8.toUInt64_eq_mod_256_iff]
@[simp] theorem UInt8.toUSize_not (a : UInt8) : (~~~a).toUSize = ~~~a.toUSize % 256 := by
simp [UInt8.toUSize_eq_mod_256_iff]
@[simp] theorem UInt16.toUInt32_not (a : UInt16) : (~~~a).toUInt32 = ~~~a.toUInt32 % 65536 := by
simp [UInt16.toUInt32_eq_mod_65536_iff]
@[simp] theorem UInt16.toUInt64_not (a : UInt16) : (~~~a).toUInt64 = ~~~a.toUInt64 % 65536 := by
simp [UInt16.toUInt64_eq_mod_65536_iff]
@[simp] theorem UInt16.toUSize_not (a : UInt16) : (~~~a).toUSize = ~~~a.toUSize % 65536 := by
simp [UInt16.toUSize_eq_mod_65536_iff]
@[simp] theorem UInt32.toUInt64_not (a : UInt32) : (~~~a).toUInt64 = ~~~a.toUInt64 % 4294967296 := by
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
@[simp] theorem UInt32.toUSize_not (a : UInt32) : (~~~a).toUSize = ~~~a.toUSize % 4294967296 := by
simp [UInt32.toUSize_eq_mod_4294967296_iff]
@[simp] theorem USize.toUInt64_not (a : USize) : (~~~a).toUInt64 = ~~~a.toUInt64 % UInt64.ofNat USize.size := by
simp [USize.toUInt64_eq_mod_usizeSize_iff]
@[simp] theorem UInt8.toFin_shiftLeft (a b : UInt8) (hb : b < 8) : (a <<< b).toFin = a.toFin <<< b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 8) hb])
@[simp] theorem UInt16.toFin_shiftLeft (a b : UInt16) (hb : b < 16) : (a <<< b).toFin = a.toFin <<< b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 16) hb])
@[simp] theorem UInt32.toFin_shiftLeft (a b : UInt32) (hb : b < 32) : (a <<< b).toFin = a.toFin <<< b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 32) hb])
@[simp] theorem UInt64.toFin_shiftLeft (a b : UInt64) (hb : b < 64) : (a <<< b).toFin = a.toFin <<< b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 64) hb])
@[simp] theorem USize.toFin_shiftLeft (a b : USize) (hb : b.toNat < System.Platform.numBits) : (a <<< b).toFin = a.toFin <<< b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := System.Platform.numBits) hb])
theorem UInt8.shiftLeft_eq_shiftLeft_mod (a b : UInt8) : a <<< b = a <<< (b % 8) := UInt8.toBitVec_inj.1 (by simp)
theorem UInt16.shiftLeft_eq_shiftLeft_mod (a b : UInt16) : a <<< b = a <<< (b % 16) := UInt16.toBitVec_inj.1 (by simp)
theorem UInt32.shiftLeft_eq_shiftLeft_mod (a b : UInt32) : a <<< b = a <<< (b % 32) := UInt32.toBitVec_inj.1 (by simp)
theorem UInt64.shiftLeft_eq_shiftLeft_mod (a b : UInt64) : a <<< b = a <<< (b % 64) := UInt64.toBitVec_inj.1 (by simp)
theorem USize.shiftLeft_eq_shiftLeft_mod (a b : USize) : a <<< b = a <<< (b % USize.ofNat System.Platform.numBits) :=
USize.toBitVec_inj.1 (by simp)
theorem UInt8.shiftRight_eq_shiftRight_mod (a b : UInt8) : a >>> b = a >>> (b % 8) := UInt8.toBitVec_inj.1 (by simp)
theorem UInt16.shiftRight_eq_shiftRight_mod (a b : UInt16) : a >>> b = a >>> (b % 16) := UInt16.toBitVec_inj.1 (by simp)
theorem UInt32.shiftRight_eq_shiftRight_mod (a b : UInt32) : a >>> b = a >>> (b % 32) := UInt32.toBitVec_inj.1 (by simp)
theorem UInt64.shiftRight_eq_shiftRight_mod (a b : UInt64) : a >>> b = a >>> (b % 64) := UInt64.toBitVec_inj.1 (by simp)
theorem USize.shiftRight_eq_shiftRight_mod (a b : USize) : a >>> b = a >>> (b % USize.ofNat System.Platform.numBits) :=
USize.toBitVec_inj.1 (by simp)
@[simp] theorem UInt16.toUInt8_shiftLeft (a b : UInt16) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
apply UInt8.toBitVec_inj.1
simp only [lt_iff_toNat_lt, UInt16.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_trans hb (by decide : 8 < 16))]
@[simp] theorem UInt32.toUInt8_shiftLeft (a b : UInt32) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
apply UInt8.toBitVec_inj.1
simp only [lt_iff_toNat_lt, UInt32.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_trans hb (by decide : 8 < 32))]
@[simp] theorem UInt32.toUInt16_shiftLeft (a b : UInt32) (hb : b < 16) : (a <<< b).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
apply UInt16.toBitVec_inj.1
simp only [lt_iff_toNat_lt, UInt32.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_trans hb (by decide : 16 < 32))]
@[simp] theorem USize.toUInt8_shiftLeft (a b : USize) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
apply UInt8.toBitVec_inj.1
simp only [lt_iff_toNat_lt, USize.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.eight_le_numBits)]
@[simp] theorem USize.toUInt16_shiftLeft (a b : USize) (hb : b < 16) : (a <<< b).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
apply UInt16.toBitVec_inj.1
simp only [lt_iff_toNat_lt, USize.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.sixteen_le_numBits)]
@[simp] theorem USize.toUInt32_shiftLeft (a b : USize) (hb : b < 32) : (a <<< b).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
apply UInt32.toBitVec_inj.1
simp only [lt_iff_toNat_lt, USize.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.le_numBits)]
@[simp] theorem UInt64.toUInt8_shiftLeft (a b : UInt64) (hb : b < 8) : (a <<< b).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
apply UInt8.toBitVec_inj.1
simp only [lt_iff_toNat_lt, UInt64.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb (by decide : 8 64))]
@[simp] theorem UInt64.toUInt16_shiftLeft (a b : UInt64) (hb : b < 16) : (a <<< b).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
apply UInt16.toBitVec_inj.1
simp only [lt_iff_toNat_lt, UInt64.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb (by decide : 16 64))]
@[simp] theorem UInt64.toUInt32_shiftLeft (a b : UInt64) (hb : b < 32) : (a <<< b).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
apply UInt32.toBitVec_inj.1
simp only [lt_iff_toNat_lt, UInt64.reduceToNat] at hb
simp [Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb (by decide : 32 64))]
@[simp] theorem UInt64.toUSize_shiftLeft (a b : UInt64) (hb : b.toNat < System.Platform.numBits) :
(a <<< b).toUSize = a.toUSize <<< b.toUSize := by
apply USize.toBitVec_inj.1
have h₁ : b.toNat % 64 = b.toNat := Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le hb System.Platform.numBits_le)
have h₂ : b.toNat % (2 ^ System.Platform.numBits) % System.Platform.numBits = b.toNat := by
rw [Nat.mod_eq_of_lt (a := b.toNat), Nat.mod_eq_of_lt hb]
exact Nat.lt_trans hb (Nat.lt_pow_self Nat.one_lt_two)
simp [h₁, h₂]
@[simp] theorem UInt16.toUInt8_shiftLeft_mod (a b : UInt16) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
rw [UInt16.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt16.toUInt8_mod_of_dvd _ _ (by simp)]
simp [ UInt8.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem UInt32.toUInt8_shiftLeft_mod (a b : UInt32) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
rw [UInt32.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt32.toUInt8_mod_of_dvd _ _ (by simp)]
simp [ UInt8.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem UInt32.toUInt16_shiftLeft_mod (a b : UInt32) : (a <<< (b % 16)).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
rw [UInt32.toUInt16_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt32.toUInt16_mod_of_dvd _ _ (by simp)]
simp [ UInt16.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem USize.toUInt8_shiftLeft_mod (a b : USize) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
rw [USize.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by simp [-toBitVec_ofNat])), USize.toUInt8_mod_of_dvd _ _ (by simp)]
simp [ UInt8.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem USize.toUInt16_shiftLeft_mod (a b : USize) : (a <<< (b % 16)).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
rw [USize.toUInt16_shiftLeft _ _ (Nat.mod_lt _ (by simp [-toBitVec_ofNat])), USize.toUInt16_mod_of_dvd _ _ (by simp)]
simp [ UInt16.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem USize.toUInt32_shiftLeft_mod (a b : USize) : (a <<< (b % 32)).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
rw [USize.toUInt32_shiftLeft _ _ (Nat.mod_lt _ (by simp [-toBitVec_ofNat])), USize.toUInt32_mod_of_dvd _ _ (by simp)]
simp [ UInt32.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem UInt64.toUInt8_shiftLeft_mod (a b : UInt64) : (a <<< (b % 8)).toUInt8 = a.toUInt8 <<< b.toUInt8 := by
rw [UInt64.toUInt8_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt64.toUInt8_mod_of_dvd _ _ (by simp)]
simp [ UInt8.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem UInt64.toUInt16_shiftLeft_mod (a b : UInt64) : (a <<< (b % 16)).toUInt16 = a.toUInt16 <<< b.toUInt16 := by
rw [UInt64.toUInt16_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt64.toUInt16_mod_of_dvd _ _ (by simp)]
simp [ UInt16.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem UInt64.toUInt32_shiftLeft_mod (a b : UInt64) : (a <<< (b % 32)).toUInt32 = a.toUInt32 <<< b.toUInt32 := by
rw [UInt64.toUInt32_shiftLeft _ _ (Nat.mod_lt _ (by decide)), UInt64.toUInt32_mod_of_dvd _ _ (by simp)]
simp [ UInt32.shiftLeft_eq_shiftLeft_mod]
@[simp] theorem UInt64.toUSize_shiftLeft_mod (a b : UInt64) : (a <<< (b % UInt64.ofNat System.Platform.numBits)).toUSize = a.toUSize <<< b.toUSize := by
rw [UInt64.toUSize_shiftLeft, UInt64.toUSize_mod_of_dvd]
· simp [ USize.shiftLeft_eq_shiftLeft_mod]
· cases System.Platform.numBits_eq <;> simp_all
· cases System.Platform.numBits_eq <;> simp_all [Nat.mod_lt]
theorem UInt8.toUInt16_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUInt16 = (a.toUInt16 <<< b.toUInt16) % 256 := by
rwa [UInt8.toUInt16_eq_mod_256_iff, UInt16.toUInt8_shiftLeft, toUInt8_toUInt16, toUInt8_toUInt16]
theorem UInt8.toUInt32_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUInt32 = (a.toUInt32 <<< b.toUInt32) % 256 := by
rwa [UInt8.toUInt32_eq_mod_256_iff, UInt32.toUInt8_shiftLeft, toUInt8_toUInt32, toUInt8_toUInt32]
theorem UInt8.toUInt64_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % 256 := by
rwa [UInt8.toUInt64_eq_mod_256_iff, UInt64.toUInt8_shiftLeft, toUInt8_toUInt64, toUInt8_toUInt64]
theorem UInt8.toUSize_shiftLeft_of_lt (a b : UInt8) (hb : b < 8) : (a <<< b).toUSize = (a.toUSize <<< b.toUSize) % 256 := by
rw [UInt8.toUSize_eq_mod_256_iff, USize.toUInt8_shiftLeft, toUInt8_toUSize, toUInt8_toUSize]
simpa [USize.lt_iff_toNat_lt]
theorem UInt16.toUInt32_shiftLeft_of_lt (a b : UInt16) (hb : b < 16) : (a <<< b).toUInt32 = (a.toUInt32 <<< b.toUInt32) % 65536 := by
rwa [UInt16.toUInt32_eq_mod_65536_iff, UInt32.toUInt16_shiftLeft, toUInt16_toUInt32, toUInt16_toUInt32]
theorem UInt16.toUInt64_shiftLeft_of_lt (a b : UInt16) (hb : b < 16) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % 65536 := by
rwa [UInt16.toUInt64_eq_mod_65536_iff, UInt64.toUInt16_shiftLeft, toUInt16_toUInt64, toUInt16_toUInt64]
theorem UInt16.toUSize_shiftLeft_of_lt (a b : UInt16) (hb : b < 16) : (a <<< b).toUSize = (a.toUSize <<< b.toUSize) % 65536 := by
rw [UInt16.toUSize_eq_mod_65536_iff, USize.toUInt16_shiftLeft, toUInt16_toUSize, toUInt16_toUSize]
simpa [USize.lt_iff_toNat_lt]
theorem UInt32.toUInt64_shiftLeft_of_lt (a b : UInt32) (hb : b < 32) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % 4294967296 := by
rwa [UInt32.toUInt64_eq_mod_4294967296_iff, UInt64.toUInt32_shiftLeft, toUInt32_toUInt64, toUInt32_toUInt64]
theorem UInt32.toUSize_shiftLeft_of_lt (a b : UInt32) (hb : b < 32) : (a <<< b).toUSize = (a.toUSize <<< b.toUSize) % 4294967296 := by
rw [UInt32.toUSize_eq_mod_4294967296_iff, USize.toUInt32_shiftLeft, toUInt32_toUSize, toUInt32_toUSize]
simpa [USize.lt_iff_toNat_lt]
theorem USize.toUInt64_shiftLeft_of_lt (a b : USize) (hb : b.toNat < System.Platform.numBits) : (a <<< b).toUInt64 = (a.toUInt64 <<< b.toUInt64) % UInt64.ofNat USize.size := by
rwa [USize.toUInt64_eq_mod_usizeSize_iff, UInt64.toUSize_shiftLeft, toUSize_toUInt64, toUSize_toUInt64]
@[simp] theorem UInt8.toUInt16_shiftLeft (a b : UInt8) : (a <<< b).toUInt16 = (a.toUInt16 <<< (b % 8).toUInt16) % 256 := by
simp [UInt8.toUInt16_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt32_shiftLeft (a b : UInt8) : (a <<< b).toUInt32 = (a.toUInt32 <<< (b % 8).toUInt32) % 256 := by
simp [UInt8.toUInt32_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt64_shiftLeft (a b : UInt8) : (a <<< b).toUInt64 = (a.toUInt64 <<< (b % 8).toUInt64) % 256 := by
simp [UInt8.toUInt64_eq_mod_256_iff]
@[simp] theorem UInt8.toUSize_shiftLeft (a b : UInt8) : (a <<< b).toUSize = (a.toUSize <<< (b % 8).toUSize) % 256 := by
simp [UInt8.toUSize_eq_mod_256_iff]
@[simp] theorem UInt16.toUInt32_shiftLeft (a b : UInt16) : (a <<< b).toUInt32 = (a.toUInt32 <<< (b % 16).toUInt32) % 65536 := by
simp [UInt16.toUInt32_eq_mod_65536_iff]
@[simp] theorem UInt16.toUInt64_shiftLeft (a b : UInt16) : (a <<< b).toUInt64 = (a.toUInt64 <<< (b % 16).toUInt64) % 65536 := by
simp [UInt16.toUInt64_eq_mod_65536_iff]
@[simp] theorem UInt16.toUSize_shiftLeft (a b : UInt16) : (a <<< b).toUSize = (a.toUSize <<< (b % 16).toUSize) % 65536 := by
simp [UInt16.toUSize_eq_mod_65536_iff]
@[simp] theorem UInt32.toUInt64_shiftLeft (a b : UInt32) : (a <<< b).toUInt64 = (a.toUInt64 <<< (b % 32).toUInt64) % 4294967296 := by
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
@[simp] theorem UInt32.toUSize_shiftLeft (a b : UInt32) : (a <<< b).toUSize = (a.toUSize <<< (b % 32).toUSize) % 4294967296 := by
simp [UInt32.toUSize_eq_mod_4294967296_iff]
@[simp] theorem USize.toUInt64_shiftLeft (a b : USize) :
(a <<< b).toUInt64 = (a.toUInt64 <<< (b % USize.ofNat System.Platform.numBits).toUInt64) % UInt64.ofNat USize.size := by
have : System.Platform.numBits < USize.size := Nat.lt_of_le_of_lt System.Platform.numBits_le (Nat.lt_of_lt_of_le (by decide) USize.le_size)
simp [USize.toUInt64_eq_mod_usizeSize_iff, toUInt64_ofNat' this]
@[simp] theorem UInt8.toFin_shiftRight (a b : UInt8) (hb : b < 8) : (a >>> b).toFin = a.toFin >>> b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 8) hb])
@[simp] theorem UInt16.toFin_shiftRight (a b : UInt16) (hb : b < 16) : (a >>> b).toFin = a.toFin >>> b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 16) hb])
@[simp] theorem UInt32.toFin_shiftRight (a b : UInt32) (hb : b < 32) : (a >>> b).toFin = a.toFin >>> b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 32) hb])
@[simp] theorem UInt64.toFin_shiftRight (a b : UInt64) (hb : b < 64) : (a >>> b).toFin = a.toFin >>> b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := 64) hb])
@[simp] theorem USize.toFin_shiftRight (a b : USize) (hb : b.toNat < System.Platform.numBits) : (a >>> b).toFin = a.toFin >>> b.toFin :=
Fin.val_inj.1 (by simp [Nat.mod_eq_of_lt (a := b.toNat) (b := System.Platform.numBits) hb])
@[simp] theorem UInt8.toUInt16_shiftRight (a b : UInt8) : (a >>> b).toUInt16 = a.toUInt16 >>> (b.toUInt16 % 8) :=
UInt16.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 8 16)])
@[simp] theorem UInt8.toUInt32_shiftRight (a b : UInt8) : (a >>> b).toUInt32 = a.toUInt32 >>> (b.toUInt32 % 8) :=
UInt32.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 8 32)])
@[simp] theorem UInt8.toUInt64_shiftRight (a b : UInt8) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % 8) :=
UInt64.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 8 64)])
@[simp] theorem UInt8.toUSize_shiftRight (a b : UInt8) : (a >>> b).toUSize = a.toUSize >>> (b.toUSize % 8) :=
USize.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;>
simp_all [Nat.mod_mod_of_dvd' (by decide : 8 32), Nat.mod_mod_of_dvd' (by decide : 8 64)])
@[simp] theorem UInt16.toUInt32_shiftRight (a b : UInt16) : (a >>> b).toUInt32 = a.toUInt32 >>> (b.toUInt32 % 16) :=
UInt32.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 16 32)])
@[simp] theorem UInt16.toUInt64_shiftRight (a b : UInt16) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % 16) :=
UInt64.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 16 64)])
@[simp] theorem UInt16.toUSize_shiftRight (a b : UInt16) : (a >>> b).toUSize = a.toUSize >>> (b.toUSize % 16) :=
USize.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;>
simp_all [Nat.mod_mod_of_dvd' (by decide : 16 32), Nat.mod_mod_of_dvd' (by decide : 16 64)])
@[simp] theorem UInt32.toUInt64_shiftRight (a b : UInt32) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % 32) :=
UInt64.toBitVec_inj.1 (by simp [Nat.mod_mod_of_dvd' (by decide : 32 64)])
@[simp] theorem UInt32.toUSize_shiftRight (a b : UInt32) : (a >>> b).toUSize = a.toUSize >>> (b.toUSize % 32) :=
USize.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;>
simp_all [Nat.mod_mod_of_dvd' (by decide : 32 32), Nat.mod_mod_of_dvd' (by decide : 32 64)])
@[simp] theorem USize.toUInt64_shiftRight (a b : USize) : (a >>> b).toUInt64 = a.toUInt64 >>> (b.toUInt64 % UInt64.ofNat System.Platform.numBits) :=
UInt64.toBitVec_inj.1 (by cases System.Platform.numBits_eq <;> simp_all [Nat.mod_mod_of_dvd' (by decide : 32 64)])
/-!
There is no reasonable statement for`UInt16.toUInt8_shiftRight`; in fact for `a b : UInt16` the
expression `(a >>> b).toUInt8` is not a function of `a.toUInt8` and `b.toUInt8`.
-/

View File

@@ -6,8 +6,11 @@ Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone, Mar
prelude
import Init.Data.UInt.Basic
import Init.Data.Fin.Lemmas
import Init.Data.Fin.Bitwise
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Bitblast
import Init.Data.Nat.Div.Lemmas
import Init.System.Platform
open Lean in
set_option hygiene false in
@@ -220,6 +223,13 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
@[deprecated ofBitVec_ofNat (since := "2025-02-12")]
theorem mk_ofNat (n : Nat) : ofBitVec (BitVec.ofNat _ n) = OfNat.ofNat n := rfl
@[simp, int_toBitVec] protected theorem toBitVec_add {a b : $typeName} : (a + b).toBitVec = a.toBitVec + b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_sub {a b : $typeName} : (a - b).toBitVec = a.toBitVec - b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_mul {a b : $typeName} : (a * b).toBitVec = a.toBitVec * b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_div {a b : $typeName} : (a / b).toBitVec = a.toBitVec / b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec % b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_neg {a : $typeName} : (-a).toBitVec = -a.toBitVec := rfl
)
if let some nbits := bits.raw.isNatLit? then
if nbits > 8 then
@@ -265,21 +275,72 @@ declare_uint_theorems USize System.Platform.numBits
theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat n) = n :=
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h USize.le_size)
theorem UInt32.toNat_lt_of_lt {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt8.lt_ofNat_iff {n : UInt8} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt8.ofNat_lt_iff {n : UInt8} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt8.le_ofNat_iff {n : UInt8} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt8.ofNat_le_iff {n : UInt8} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt32.lt_toNat_of_lt {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt16.lt_ofNat_iff {n : UInt16} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt16.ofNat_lt_iff {n : UInt16} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt16.le_ofNat_iff {n : UInt16} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt16.ofNat_le_iff {n : UInt16} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt32.lt_ofNat_iff {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt32.ofNat_lt_iff {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt32.le_ofNat_iff {n : UInt32} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt32.ofNat_le_iff {n : UInt32} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
@[deprecated UInt32.lt_ofNat_iff (since := "2025-03-18")]
theorem UInt32.toNat_lt_of_lt {n : UInt32} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m :=
(UInt32.lt_ofNat_iff h).1
@[deprecated UInt32.ofNat_lt_iff (since := "2025-03-18")]
theorem UInt32.lt_toNat_of_lt {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat :=
(UInt32.ofNat_lt_iff h).1
@[deprecated UInt32.le_ofNat_iff (since := "2025-03-18")]
theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ofNat m n.toNat m :=
(UInt32.le_ofNat_iff h).1
@[deprecated UInt32.ofNat_le_iff (since := "2025-03-18")]
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m n m n.toNat :=
(UInt32.ofNat_le_iff h).1
theorem UInt64.lt_ofNat_iff {n : UInt64} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt64.ofNat_lt_iff {n : UInt64} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt64.le_ofNat_iff {n : UInt64} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt64.ofNat_le_iff {n : UInt64} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem USize.lt_ofNat_iff {n : USize} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem USize.ofNat_lt_iff {n : USize} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem USize.le_ofNat_iff {n : USize} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem USize.ofNat_le_iff {n : USize} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
rw [le_iff_toNat_le, toNat_ofNat_of_lt' h]
theorem UInt8.mod_eq_of_lt {a b : UInt8} (h : a < b) : a % b = a := UInt8.toNat_inj.1 (Nat.mod_eq_of_lt h)
theorem UInt16.mod_eq_of_lt {a b : UInt16} (h : a < b) : a % b = a := UInt16.toNat_inj.1 (Nat.mod_eq_of_lt h)
theorem UInt32.mod_eq_of_lt {a b : UInt32} (h : a < b) : a % b = a := UInt32.toNat_inj.1 (Nat.mod_eq_of_lt h)
theorem UInt64.mod_eq_of_lt {a b : UInt64} (h : a < b) : a % b = a := UInt64.toNat_inj.1 (Nat.mod_eq_of_lt h)
theorem USize.mod_eq_of_lt {a b : USize} (h : a < b) : a % b = a := USize.toNat_inj.1 (Nat.mod_eq_of_lt h)
@[simp] theorem UInt8.toNat_lt (n : UInt8) : n.toNat < 2 ^ 8 := n.toFin.isLt
@[simp] theorem UInt16.toNat_lt (n : UInt16) : n.toNat < 2 ^ 16 := n.toFin.isLt
@@ -800,12 +861,19 @@ theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
@[simp] theorem USize.toUSize_toUInt64 (n : USize) : n.toUInt64.toUSize = n :=
USize.toNat.inj (by simp)
@[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toUInt32.toUSize = n % 4294967296 := by
apply USize.toNat.inj
simp only [UInt32.toNat_toUSize, toNat_toUInt32, Nat.reducePow, USize.toNat_mod]
cases USize.size_eq
· next h => rw [Nat.mod_eq_of_lt (h n.toNat_lt_size), USize.toNat_ofNat,
USize.size_eq_two_pow, h, Nat.mod_self, Nat.mod_zero]
· next h => rw [USize.toNat_ofNat_of_lt]; simp_all
-- Note: we are currently missing the following four results for which there does not seem to
-- be a good candidate for the RHS:
-- @[simp] theorem UInt64.toUInt64_toUSize (n : UInt64) : n.toUSize.toUInt64 = ? :=
-- @[simp] theorem UInt64.toUSize_toUInt32 (n : UInt64) : n.toUInt32.toUSize = ? :=
-- @[simp] theorem USize.toUInt64_toUInt32 (n : USize) : n.toUInt32.toUInt64 = ? :=
-- @[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toInt32.toUSize = ? :=
@[simp] theorem UInt8.toNat_ofFin (x : Fin UInt8.size) : (UInt8.ofFin x).toNat = x.val := rfl
@[simp] theorem UInt16.toNat_ofFin (x : Fin UInt16.size) : (UInt16.ofFin x).toNat = x.val := rfl
@@ -1344,3 +1412,531 @@ theorem USize.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem BitVec.ofNat_uSizeToNat (n : USize) : BitVec.ofNat System.Platform.numBits n.toNat = n.toBitVec :=
BitVec.eq_of_toNat_eq (by simp)
@[simp] protected theorem UInt8.toFin_div (a b : UInt8) : (a / b).toFin = a.toFin / b.toFin := rfl
@[simp] protected theorem UInt16.toFin_div (a b : UInt16) : (a / b).toFin = a.toFin / b.toFin := rfl
@[simp] protected theorem UInt32.toFin_div (a b : UInt32) : (a / b).toFin = a.toFin / b.toFin := rfl
@[simp] protected theorem UInt64.toFin_div (a b : UInt64) : (a / b).toFin = a.toFin / b.toFin := rfl
@[simp] protected theorem USize.toFin_div (a b : USize) : (a / b).toFin = a.toFin / b.toFin := rfl
@[simp] theorem UInt8.toUInt16_div (a b : UInt8) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 := rfl
@[simp] theorem UInt8.toUInt32_div (a b : UInt8) : (a / b).toUInt32 = a.toUInt32 / b.toUInt32 := rfl
@[simp] theorem UInt8.toUInt64_div (a b : UInt8) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
@[simp] theorem UInt8.toUSize_div (a b : UInt8) : (a / b).toUSize = a.toUSize / b.toUSize := rfl
@[simp] theorem UInt16.toUInt32_div (a b : UInt16) : (a / b).toUInt32 = a.toUInt32 / b.toUInt32 := rfl
@[simp] theorem UInt16.toUInt64_div (a b : UInt16) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
@[simp] theorem UInt16.toUSize_div (a b : UInt16) : (a / b).toUSize = a.toUSize / b.toUSize := rfl
@[simp] theorem UInt32.toUInt64_div (a b : UInt32) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
@[simp] theorem UInt32.toUSize_div (a b : UInt32) : (a / b).toUSize = a.toUSize / b.toUSize := rfl
@[simp] theorem USize.toUInt64_div (a b : USize) : (a / b).toUInt64 = a.toUInt64 / b.toUInt64 := rfl
theorem UInt16.toUInt8_div (a b : UInt16) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
theorem UInt32.toUInt8_div (a b : UInt32) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
theorem UInt32.toUInt16_div (a b : UInt32) (ha : a < 65536) (hb : b < 65536) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 :=
UInt16.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
theorem USize.toUInt8_div (a b : USize) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
UInt8.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt8.size_lt_usizeSize] using Nat.div_mod_eq_mod_div_mod ha hb)
theorem USize.toUInt16_div (a b : USize) (ha : a < 65536) (hb : b < 65536) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 :=
UInt16.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt16.size_lt_usizeSize] using Nat.div_mod_eq_mod_div_mod ha hb)
theorem UInt64.toUInt8_div (a b : UInt64) (ha : a < 256) (hb : b < 256) : (a / b).toUInt8 = a.toUInt8 / b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
theorem UInt64.toUInt16_div (a b : UInt64) (ha : a < 65536) (hb : b < 65536) : (a / b).toUInt16 = a.toUInt16 / b.toUInt16 :=
UInt16.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
theorem UInt64.toUInt32_div (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a / b).toUInt32 = a.toUInt32 / b.toUInt32 :=
UInt32.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
theorem UInt64.toUSize_div (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a / b).toUSize = a.toUSize / b.toUSize :=
USize.toNat.inj (Nat.div_mod_eq_mod_div_mod (Nat.lt_of_lt_of_le ha UInt32.size_le_usizeSize) (Nat.lt_of_lt_of_le hb UInt32.size_le_usizeSize))
theorem UInt64.toUSize_div_of_toNat_lt (a b : UInt64) (ha : a.toNat < USize.size) (hb : b.toNat < USize.size) :
(a / b).toUSize = a.toUSize / b.toUSize :=
USize.toNat.inj (by simpa using Nat.div_mod_eq_mod_div_mod ha hb)
@[simp] protected theorem UInt8.toFin_mod (a b : UInt8) : (a % b).toFin = a.toFin % b.toFin := rfl
@[simp] protected theorem UInt16.toFin_mod (a b : UInt8) : (a % b).toFin = a.toFin % b.toFin := rfl
@[simp] protected theorem UInt32.toFin_mod (a b : UInt32) : (a % b).toFin = a.toFin % b.toFin := rfl
@[simp] protected theorem UInt64.toFin_mod (a b : UInt64) : (a % b).toFin = a.toFin % b.toFin := rfl
@[simp] protected theorem USize.toFin_mod (a b : USize) : (a % b).toFin = a.toFin % b.toFin := rfl
@[simp] theorem UInt8.toUInt16_mod (a b : UInt8) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 := rfl
@[simp] theorem UInt8.toUInt32_mod (a b : UInt8) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := rfl
@[simp] theorem UInt8.toUInt64_mod (a b : UInt8) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
@[simp] theorem UInt8.toUSize_mod (a b : UInt8) : (a % b).toUSize = a.toUSize % b.toUSize := rfl
@[simp] theorem UInt16.toUInt32_mod (a b : UInt16) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := rfl
@[simp] theorem UInt16.toUInt64_mod (a b : UInt16) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
@[simp] theorem UInt16.toUSize_mod (a b : UInt16) : (a % b).toUSize = a.toUSize % b.toUSize := rfl
@[simp] theorem UInt32.toUInt64_mod (a b : UInt32) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
@[simp] theorem UInt32.toUSize_mod (a b : UInt32) : (a % b).toUSize = a.toUSize % b.toUSize := rfl
@[simp] theorem USize.toUInt64_mod (a b : USize) : (a % b).toUInt64 = a.toUInt64 % b.toUInt64 := rfl
theorem UInt16.toUInt8_mod (a b : UInt16) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem UInt16.toUInt8_mod_of_dvd (a b : UInt16) (hb : b.toNat 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem UInt32.toUInt8_mod (a b : UInt32) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem UInt32.toUInt16_mod (a b : UInt32) (ha : a < 65536) (hb : b < 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem UInt32.toUInt8_mod_of_dvd (a b : UInt32) (hb : b.toNat 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem UInt32.toUInt16_mod_of_dvd (a b : UInt32) (hb : b.toNat 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem USize.toUInt8_mod (a b : USize) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt8.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem USize.toUInt16_mod (a b : USize) (ha : a < 65536) (hb : b < 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
UInt16.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt16.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem USize.toUInt32_mod (a b : USize) (ha : a < 4294967296) (hb : b < 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := by
apply UInt32.toNat.inj
simp only [toNat_toUInt32, USize.toNat_mod, Nat.reducePow, UInt32.toNat_mod]
have := Nat.mod_mod_eq_mod_mod_mod ha hb
obtain (h|h) := USize.size_eq
· have ha' := h a.toNat_lt_size
have hb' := h b.toNat_lt_size
rw [Nat.mod_eq_of_lt ha', Nat.mod_eq_of_lt hb', Nat.mod_eq_of_lt]
exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) ha'
· simp_all
theorem USize.toUInt8_mod_of_dvd (a b : USize) (hb : b.toNat 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt8.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem USize.toUInt16_mod_of_dvd (a b : USize) (hb : b.toNat 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
UInt16.toNat.inj (by simpa [Nat.mod_eq_of_lt UInt16.size_lt_usizeSize] using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem USize.toUInt32_mod_of_dvd (a b : USize) (hb : b.toNat 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 := by
apply UInt32.toNat.inj
simp only [toNat_toUInt32, USize.toNat_mod, Nat.reducePow, UInt32.toNat_mod]
have := Nat.mod_mod_eq_mod_mod_mod_of_dvd (a := a.toNat) hb
obtain (h|h) := USize.size_eq
· have ha' := h a.toNat_lt_size
have hb' := h b.toNat_lt_size
rw [Nat.mod_eq_of_lt ha', Nat.mod_eq_of_lt hb', Nat.mod_eq_of_lt]
exact Nat.lt_of_le_of_lt (Nat.mod_le _ _) ha'
· simp_all
theorem UInt64.toUInt8_mod (a b : UInt64) (ha : a < 256) (hb : b < 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem UInt64.toUInt16_mod (a b : UInt64) (ha : a < 65536) (hb : b < 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem UInt64.toUInt32_mod (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 :=
UInt32.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem UInt64.toUSize_mod (a b : UInt64) (ha : a < 4294967296) (hb : b < 4294967296) : (a % b).toUSize = a.toUSize % b.toUSize :=
USize.toNat.inj (Nat.mod_mod_eq_mod_mod_mod (Nat.lt_of_lt_of_le ha UInt32.size_le_usizeSize) (Nat.lt_of_lt_of_le hb UInt32.size_le_usizeSize))
theorem UInt64.toUSize_mod_of_toNat_lt (a b : UInt64) (ha : a.toNat < USize.size) (hb : b.toNat < USize.size) : (a % b).toUSize = a.toUSize % b.toUSize :=
USize.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod ha hb)
theorem UInt64.toUInt8_mod_of_dvd (a b : UInt64) (hb : b.toNat 256) : (a % b).toUInt8 = a.toUInt8 % b.toUInt8 :=
UInt8.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem UInt64.toUInt16_mod_of_dvd (a b : UInt64)(hb : b.toNat 65536) : (a % b).toUInt16 = a.toUInt16 % b.toUInt16 :=
UInt16.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem UInt64.toUInt32_mod_of_dvd (a b : UInt64) (hb : b.toNat 4294967296) : (a % b).toUInt32 = a.toUInt32 % b.toUInt32 :=
UInt32.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
theorem UInt64.toUSize_mod_of_dvd (a b : UInt64) (hb : b.toNat 4294967296) : (a % b).toUSize = a.toUSize % b.toUSize :=
USize.toNat.inj (Nat.mod_mod_eq_mod_mod_mod_of_dvd (Nat.dvd_trans hb UInt32.size_dvd_usizeSize))
theorem UInt64.toUSize_mod_of_dvd_usizeSize (a b : UInt64) (hb : b.toNat USize.size) : (a % b).toUSize = a.toUSize % b.toUSize :=
USize.toNat.inj (by simpa using Nat.mod_mod_eq_mod_mod_mod_of_dvd hb)
@[simp] protected theorem UInt8.toFin_add (a b : UInt8) : (a + b).toFin = a.toFin + b.toFin := rfl
@[simp] protected theorem UInt16.toFin_add (a b : UInt16) : (a + b).toFin = a.toFin + b.toFin := rfl
@[simp] protected theorem UInt32.toFin_add (a b : UInt32) : (a + b).toFin = a.toFin + b.toFin := rfl
@[simp] protected theorem UInt64.toFin_add (a b : UInt64) : (a + b).toFin = a.toFin + b.toFin := rfl
@[simp] protected theorem USize.toFin_add (a b : USize) : (a + b).toFin = a.toFin + b.toFin := rfl
@[simp] theorem UInt16.toUInt8_add (a b : UInt16) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt8_add (a b : UInt32) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt8_add (a b : UInt64) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem USize.toUInt8_add (a b : USize) : (a + b).toUInt8 = a.toUInt8 + b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt16_add (a b : UInt32) : (a + b).toUInt16 = a.toUInt16 + b.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt16_add (a b : UInt64) : (a + b).toUInt16 = a.toUInt16 + b.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem USize.toUInt16_add (a b : USize) : (a + b).toUInt16 = a.toUInt16 + b.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_add (a b : UInt64) : (a + b).toUInt32 = a.toUInt32 + b.toUInt32 := UInt32.toNat.inj (by simp)
@[simp] theorem USize.toUInt32_add (a b : USize) : (a + b).toUInt32 = a.toUInt32 + b.toUInt32 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUSize_add (a b : UInt64) : (a + b).toUSize = a.toUSize + b.toUSize := USize.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt16_add (a b : UInt8) : (a + b).toUInt16 = (a.toUInt16 + b.toUInt16) % 256 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt32_add (a b : UInt8) : (a + b).toUInt32 = (a.toUInt32 + b.toUInt32) % 256 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt64_add (a b : UInt8) : (a + b).toUInt64 = (a.toUInt64 + b.toUInt64) % 256 := UInt64.toNat.inj (by simp)
@[simp] theorem UInt8.toUSize_add (a b : UInt8) : (a + b).toUSize = (a.toUSize + b.toUSize) % 256 := USize.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt32_add (a b : UInt16) : (a + b).toUInt32 = (a.toUInt32 + b.toUInt32) % 65536 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt64_add (a b : UInt16) : (a + b).toUInt64 = (a.toUInt64 + b.toUInt64) % 65536 := UInt64.toNat.inj (by simp)
@[simp] theorem UInt16.toUSize_add (a b : UInt16) : (a + b).toUSize = (a.toUSize + b.toUSize) % 65536 := USize.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt64_add (a b : UInt32) : (a + b).toUInt64 = (a.toUInt64 + b.toUInt64) % 4294967296 := UInt64.toNat.inj (by simp)
/-- Note that on 32-bit machines we are doing a modulo by zero. -/
@[simp] theorem UInt32.toUSize_add (a b : UInt32) : (a + b).toUSize = (a.toUSize + b.toUSize) % 4294967296 :=
USize.toNat.inj (by cases System.Platform.numBits_eq <;> simp_all [USize.toNat_ofNat])
@[simp] protected theorem UInt8.toFin_sub (a b : UInt8) : (a - b).toFin = a.toFin - b.toFin := rfl
@[simp] protected theorem UInt16.toFin_sub (a b : UInt16) : (a - b).toFin = a.toFin - b.toFin := rfl
@[simp] protected theorem UInt32.toFin_sub (a b : UInt32) : (a - b).toFin = a.toFin - b.toFin := rfl
@[simp] protected theorem UInt64.toFin_sub (a b : UInt64) : (a - b).toFin = a.toFin - b.toFin := rfl
@[simp] protected theorem USize.toFin_sub (a b : USize) : (a - b).toFin = a.toFin - b.toFin := rfl
@[simp] protected theorem UInt8.toFin_mul (a b : UInt8) : (a * b).toFin = a.toFin * b.toFin := rfl
@[simp] protected theorem UInt16.toFin_mul (a b : UInt16) : (a * b).toFin = a.toFin * b.toFin := rfl
@[simp] protected theorem UInt32.toFin_mul (a b : UInt32) : (a * b).toFin = a.toFin * b.toFin := rfl
@[simp] protected theorem UInt64.toFin_mul (a b : UInt64) : (a * b).toFin = a.toFin * b.toFin := rfl
@[simp] protected theorem USize.toFin_mul (a b : USize) : (a * b).toFin = a.toFin * b.toFin := rfl
@[simp] theorem UInt16.toUInt8_mul (a b : UInt16) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt8_mul (a b : UInt32) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt8_mul (a b : UInt64) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem USize.toUInt8_mul (a b : USize) : (a * b).toUInt8 = a.toUInt8 * b.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt16_mul (a b : UInt32) : (a * b).toUInt16 = a.toUInt16 * b.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt16_mul (a b : UInt64) : (a * b).toUInt16 = a.toUInt16 * b.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem USize.toUInt16_mul (a b : USize) : (a * b).toUInt16 = a.toUInt16 * b.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_mul (a b : UInt64) : (a * b).toUInt32 = a.toUInt32 * b.toUInt32 := UInt32.toNat.inj (by simp)
@[simp] theorem USize.toUInt32_mul (a b : USize) : (a * b).toUInt32 = a.toUInt32 * b.toUInt32 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUSize_mul (a b : UInt64) : (a * b).toUSize = a.toUSize * b.toUSize := USize.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt16_mul (a b : UInt8) : (a * b).toUInt16 = (a.toUInt16 * b.toUInt16) % 256 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt32_mul (a b : UInt8) : (a * b).toUInt32 = (a.toUInt32 * b.toUInt32) % 256 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt64_mul (a b : UInt8) : (a * b).toUInt64 = (a.toUInt64 * b.toUInt64) % 256 := UInt64.toNat.inj (by simp)
@[simp] theorem UInt8.toUSize_mul (a b : UInt8) : (a * b).toUSize = (a.toUSize * b.toUSize) % 256 := USize.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt32_mul (a b : UInt16) : (a * b).toUInt32 = (a.toUInt32 * b.toUInt32) % 65536 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt64_mul (a b : UInt16) : (a * b).toUInt64 = (a.toUInt64 * b.toUInt64) % 65536 := UInt64.toNat.inj (by simp)
@[simp] theorem UInt16.toUSize_mul (a b : UInt16) : (a * b).toUSize = (a.toUSize * b.toUSize) % 65536 := USize.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt64_mul (a b : UInt32) : (a * b).toUInt64 = (a.toUInt64 * b.toUInt64) % 4294967296 := UInt64.toNat.inj (by simp)
/-- Note that on 32-bit machines we are doing a modulo by zero. -/
@[simp] theorem UInt32.toUSize_mul (a b : UInt32) : (a * b).toUSize = (a.toUSize * b.toUSize) % 4294967296 :=
USize.toNat.inj (by cases System.Platform.numBits_eq <;> simp_all [USize.toNat_ofNat])
theorem UInt16.toUInt8_eq (a b : UInt16) : a.toUInt8 = b.toUInt8 a % 256 = b % 256 := by
simp [ UInt8.toNat_inj, UInt16.toNat_inj]
theorem UInt32.toUInt8_eq (a b : UInt32) : a.toUInt8 = b.toUInt8 a % 256 = b % 256 := by
simp [ UInt8.toNat_inj, UInt32.toNat_inj]
theorem UInt64.toUInt8_eq (a b : UInt64) : a.toUInt8 = b.toUInt8 a % 256 = b % 256 := by
simp [ UInt8.toNat_inj, UInt64.toNat_inj]
theorem USize.toUInt8_eq (a b : USize) : a.toUInt8 = b.toUInt8 a % 256 = b % 256 := by
simp [ UInt8.toNat_inj, USize.toNat_inj]
theorem UInt32.toUInt16_eq (a b : UInt32) : a.toUInt16 = b.toUInt16 a % 65536 = b % 65536 := by
simp [ UInt16.toNat_inj, UInt32.toNat_inj]
theorem UInt64.toUInt16_eq (a b : UInt64) : a.toUInt16 = b.toUInt16 a % 65536 = b % 65536 := by
simp [ UInt16.toNat_inj, UInt64.toNat_inj]
theorem USize.toUInt16_eq (a b : USize) : a.toUInt16 = b.toUInt16 a % 65536 = b % 65536 := by
simp [ UInt16.toNat_inj, USize.toNat_inj]
theorem UInt64.toUInt32_eq (a b : UInt64) : a.toUInt32 = b.toUInt32 a % 4294967296 = b % 4294967296 := by
simp [ UInt32.toNat_inj, UInt64.toNat_inj]
theorem USize.toUInt32_eq (a b : USize) : a.toUInt32 = b.toUInt32 a % 4294967296 = b % 4294967296 := by
simp [ UInt32.toNat_inj, USize.toNat_inj, USize.toNat_ofNat]
have := Nat.mod_eq_of_lt a.toNat_lt_two_pow_numBits
have := Nat.mod_eq_of_lt b.toNat_lt_two_pow_numBits
cases System.Platform.numBits_eq <;> simp_all
theorem UInt8.toUInt16_eq_mod_256_iff (a : UInt8) (b : UInt16) : a.toUInt16 = b % 256 a = b.toUInt8 := by
simp [ UInt8.toNat_inj, UInt16.toNat_inj]
theorem UInt8.toUInt32_eq_mod_256_iff (a : UInt8) (b : UInt32) : a.toUInt32 = b % 256 a = b.toUInt8 := by
simp [ UInt8.toNat_inj, UInt32.toNat_inj]
theorem UInt8.toUInt64_eq_mod_256_iff (a : UInt8) (b : UInt64) : a.toUInt64 = b % 256 a = b.toUInt8 := by
simp [ UInt8.toNat_inj, UInt64.toNat_inj]
theorem UInt8.toUSize_eq_mod_256_iff (a : UInt8) (b : USize) : a.toUSize = b % 256 a = b.toUInt8 := by
simp [ UInt8.toNat_inj, USize.toNat_inj]
theorem UInt16.toUInt32_eq_mod_65536_iff (a : UInt16) (b : UInt32) : a.toUInt32 = b % 65536 a = b.toUInt16 := by
simp [ UInt16.toNat_inj, UInt32.toNat_inj]
theorem UInt16.toUInt64_eq_mod_65536_iff (a : UInt16) (b : UInt64) : a.toUInt64 = b % 65536 a = b.toUInt16 := by
simp [ UInt16.toNat_inj, UInt64.toNat_inj]
theorem UInt16.toUSize_eq_mod_65536_iff (a : UInt16) (b : USize) : a.toUSize = b % 65536 a = b.toUInt16 := by
simp [ UInt16.toNat_inj, USize.toNat_inj]
theorem UInt32.toUInt64_eq_mod_4294967296_iff (a : UInt32) (b : UInt64) : a.toUInt64 = b % 4294967296 a = b.toUInt32 := by
simp [ UInt32.toNat_inj, UInt64.toNat_inj]
theorem UInt32.toUSize_eq_mod_4294967296_iff (a : UInt32) (b : USize) : a.toUSize = b % 4294967296 a = b.toUInt32 := by
simp [ UInt32.toNat_inj, USize.toNat_inj, USize.toNat_ofNat]
have := Nat.mod_eq_of_lt b.toNat_lt_two_pow_numBits
cases System.Platform.numBits_eq <;> simp_all
theorem USize.toUInt64_eq_mod_usizeSize_iff (a : USize) (b : UInt64) : a.toUInt64 = b % UInt64.ofNat USize.size a = b.toUSize := by
simp [ USize.toNat_inj, UInt64.toNat_inj, USize.size_eq_two_pow]
cases System.Platform.numBits_eq <;> simp_all
theorem UInt8.toUInt16_inj {a b : UInt8} : a.toUInt16 = b.toUInt16 a = b :=
fun h => by rw [ toUInt8_toUInt16 a, h, toUInt8_toUInt16], by rintro rfl; rfl
theorem UInt8.toUInt32_inj {a b : UInt8} : a.toUInt32 = b.toUInt32 a = b :=
fun h => by rw [ toUInt8_toUInt32 a, h, toUInt8_toUInt32], by rintro rfl; rfl
theorem UInt8.toUInt64_inj {a b : UInt8} : a.toUInt64 = b.toUInt64 a = b :=
fun h => by rw [ toUInt8_toUInt64 a, h, toUInt8_toUInt64], by rintro rfl; rfl
theorem UInt8.toUSize_inj {a b : UInt8} : a.toUSize = b.toUSize a = b :=
fun h => by rw [ toUInt8_toUSize a, h, toUInt8_toUSize], by rintro rfl; rfl
theorem UInt16.toUInt32_inj {a b : UInt16} : a.toUInt32 = b.toUInt32 a = b :=
fun h => by rw [ toUInt16_toUInt32 a, h, toUInt16_toUInt32], by rintro rfl; rfl
theorem UInt16.toUInt64_inj {a b : UInt16} : a.toUInt64 = b.toUInt64 a = b :=
fun h => by rw [ toUInt16_toUInt64 a, h, toUInt16_toUInt64], by rintro rfl; rfl
theorem UInt16.toUSize_inj {a b : UInt16} : a.toUSize = b.toUSize a = b :=
fun h => by rw [ toUInt16_toUSize a, h, toUInt16_toUSize], by rintro rfl; rfl
theorem UInt32.toUInt64_inj {a b : UInt32} : a.toUInt64 = b.toUInt64 a = b :=
fun h => by rw [ toUInt32_toUInt64 a, h, toUInt32_toUInt64], by rintro rfl; rfl
theorem UInt32.toUSize_inj {a b : UInt32} : a.toUSize = b.toUSize a = b :=
fun h => by rw [ toUInt32_toUSize a, h, toUInt32_toUSize], by rintro rfl; rfl
theorem USize.toUInt64_inj {a b : USize} : a.toUInt64 = b.toUInt64 a = b :=
fun h => by rw [ toUSize_toUInt64 a, h, toUSize_toUInt64], by rintro rfl; rfl
@[simp] theorem UInt8.toUInt16_lt {a b : UInt8} : a.toUInt16 < b.toUInt16 a < b := by
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
@[simp] theorem UInt8.toUInt32_lt {a b : UInt8} : a.toUInt32 < b.toUInt32 a < b := by
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
@[simp] theorem UInt8.toUInt64_lt {a b : UInt8} : a.toUInt64 < b.toUInt64 a < b := by
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
@[simp] theorem UInt8.toUSize_lt {a b : UInt8} : a.toUSize < b.toUSize a < b := by
simp [lt_iff_toNat_lt, USize.lt_iff_toNat_lt]
@[simp] theorem UInt16.toUInt32_lt {a b : UInt16} : a.toUInt32 < b.toUInt32 a < b := by
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
@[simp] theorem UInt16.toUInt64_lt {a b : UInt16} : a.toUInt64 < b.toUInt64 a < b := by
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
@[simp] theorem UInt16.toUSize_lt {a b : UInt16} : a.toUSize < b.toUSize a < b := by
simp [lt_iff_toNat_lt, USize.lt_iff_toNat_lt]
@[simp] theorem UInt32.toUInt64_lt {a b : UInt32} : a.toUInt64 < b.toUInt64 a < b := by
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
@[simp] theorem UInt32.toUSize_lt {a b : UInt32} : a.toUSize < b.toUSize a < b := by
simp [lt_iff_toNat_lt, USize.lt_iff_toNat_lt]
@[simp] theorem USize.toUInt64_lt {a b : USize} : a.toUInt64 < b.toUInt64 a < b := by
simp [lt_iff_toNat_lt, UInt64.lt_iff_toNat_lt]
@[simp] theorem UInt16.toUInt8_lt {a b : UInt16} : a.toUInt8 < b.toUInt8 a % 256 < b % 256 := by
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
@[simp] theorem UInt32.toUInt8_lt {a b : UInt32} : a.toUInt8 < b.toUInt8 a % 256 < b % 256 := by
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
@[simp] theorem UInt64.toUInt8_lt {a b : UInt64} : a.toUInt8 < b.toUInt8 a % 256 < b % 256 := by
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
@[simp] theorem USize.toUInt8_lt {a b : USize} : a.toUInt8 < b.toUInt8 a % 256 < b % 256 := by
simp [lt_iff_toNat_lt, UInt8.lt_iff_toNat_lt]
@[simp] theorem UInt32.toUInt16_lt {a b : UInt32} : a.toUInt16 < b.toUInt16 a % 65536 < b % 65536 := by
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
@[simp] theorem UInt64.toUInt16_lt {a b : UInt64} : a.toUInt16 < b.toUInt16 a % 65536 < b % 65536 := by
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
@[simp] theorem USize.toUInt16_lt {a b : USize} : a.toUInt16 < b.toUInt16 a % 65536 < b % 65536 := by
simp [lt_iff_toNat_lt, UInt16.lt_iff_toNat_lt]
@[simp] theorem UInt64.toUInt32_lt {a b : UInt64} : a.toUInt32 < b.toUInt32 a % 4294967296 < b % 4294967296 := by
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
@[simp] theorem USize.toUInt32_lt {a b : USize} : a.toUInt32 < b.toUInt32 a % 4294967296 < b % 4294967296 := by
rw [ UInt32.toUSize_lt, toUSize_toUInt32]
simp [lt_iff_toNat_lt, UInt32.lt_iff_toNat_lt]
@[simp] theorem UInt64.toUSize_lt {a b : UInt64} : a.toUSize < b.toUSize a % UInt64.ofNat USize.size < b % UInt64.ofNat USize.size := by
simp only [USize.lt_iff_toNat_lt, toNat_toUSize, lt_iff_toNat_lt, UInt64.toNat_mod, toNat_ofNat', Nat.reducePow]
cases System.Platform.numBits_eq <;> simp_all [USize.size]
@[simp] theorem UInt8.toUInt16_le {a b : UInt8} : a.toUInt16 b.toUInt16 a b := by
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
@[simp] theorem UInt8.toUInt32_le {a b : UInt8} : a.toUInt32 b.toUInt32 a b := by
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
@[simp] theorem UInt8.toUInt64_le {a b : UInt8} : a.toUInt64 b.toUInt64 a b := by
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
@[simp] theorem UInt8.toUSize_le {a b : UInt8} : a.toUSize b.toUSize a b := by
simp [le_iff_toNat_le, USize.le_iff_toNat_le]
@[simp] theorem UInt16.toUInt32_le {a b : UInt16} : a.toUInt32 b.toUInt32 a b := by
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
@[simp] theorem UInt16.toUInt64_le {a b : UInt16} : a.toUInt64 b.toUInt64 a b := by
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
@[simp] theorem UInt16.toUSize_le {a b : UInt16} : a.toUSize b.toUSize a b := by
simp [le_iff_toNat_le, USize.le_iff_toNat_le]
@[simp] theorem UInt32.toUInt64_le {a b : UInt32} : a.toUInt64 b.toUInt64 a b := by
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
@[simp] theorem UInt32.toUSize_le {a b : UInt32} : a.toUSize b.toUSize a b := by
simp [le_iff_toNat_le, USize.le_iff_toNat_le]
@[simp] theorem USize.toUInt64_le {a b : USize} : a.toUInt64 b.toUInt64 a b := by
simp [le_iff_toNat_le, UInt64.le_iff_toNat_le]
@[simp] theorem UInt16.toUInt8_le {a b : UInt16} : a.toUInt8 b.toUInt8 a % 256 b % 256 := by
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
@[simp] theorem UInt32.toUInt8_le {a b : UInt32} : a.toUInt8 b.toUInt8 a % 256 b % 256 := by
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
@[simp] theorem UInt64.toUInt8_le {a b : UInt64} : a.toUInt8 b.toUInt8 a % 256 b % 256 := by
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
@[simp] theorem USize.toUInt8_le {a b : USize} : a.toUInt8 b.toUInt8 a % 256 b % 256 := by
simp [le_iff_toNat_le, UInt8.le_iff_toNat_le]
@[simp] theorem UInt32.toUInt16_le {a b : UInt32} : a.toUInt16 b.toUInt16 a % 65536 b % 65536 := by
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
@[simp] theorem UInt64.toUInt16_le {a b : UInt64} : a.toUInt16 b.toUInt16 a % 65536 b % 65536 := by
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
@[simp] theorem USize.toUInt16_le {a b : USize} : a.toUInt16 b.toUInt16 a % 65536 b % 65536 := by
simp [le_iff_toNat_le, UInt16.le_iff_toNat_le]
@[simp] theorem UInt64.toUInt32_le {a b : UInt64} : a.toUInt32 b.toUInt32 a % 4294967296 b % 4294967296 := by
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
@[simp] theorem USize.toUInt32_le {a b : USize} : a.toUInt32 b.toUInt32 a % 4294967296 b % 4294967296 := by
rw [ UInt32.toUSize_le, toUSize_toUInt32]
simp [le_iff_toNat_le, UInt32.le_iff_toNat_le]
@[simp] theorem UInt64.toUSize_le {a b : UInt64} : a.toUSize b.toUSize a % UInt64.ofNat USize.size b % UInt64.ofNat USize.size := by
simp only [USize.le_iff_toNat_le, toNat_toUSize, le_iff_toNat_le, UInt64.toNat_mod, UInt64.reduceToNat]
cases System.Platform.numBits_eq <;> simp_all [USize.size]
@[simp] theorem UInt16.toUInt8_neg (a : UInt16) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt8_neg (a : UInt32) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt32.toUInt16_neg (a : UInt32) : (-a).toUInt16 = -a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt8_neg (a : UInt64) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt16_neg (a : UInt64) : (-a).toUInt16 = -a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUInt32_neg (a : UInt64) : (-a).toUInt32 = -a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem UInt64.toUSize_neg (a : UInt64) : (-a).toUSize = -a.toUSize := USize.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt8_neg (a : USize) : (-a).toUInt8 = -a.toUInt8 := UInt8.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt16_neg (a : USize) : (-a).toUInt16 = -a.toUInt16 := UInt16.toBitVec_inj.1 (by simp)
@[simp] theorem USize.toUInt32_neg (a : USize) : (-a).toUInt32 = -a.toUInt32 := UInt32.toBitVec_inj.1 (by simp)
@[simp] theorem UInt8.toUInt16_neg (a : UInt8) : (-a).toUInt16 = -a.toUInt16 % 256 := by
simp [UInt8.toUInt16_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt32_neg (a : UInt8) : (-a).toUInt32 = -a.toUInt32 % 256 := by
simp [UInt8.toUInt32_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt64_neg (a : UInt8) : (-a).toUInt64 = -a.toUInt64 % 256 := by
simp [UInt8.toUInt64_eq_mod_256_iff]
@[simp] theorem UInt8.toUSize_neg (a : UInt8) : (-a).toUSize = -a.toUSize % 256 := by
simp [UInt8.toUSize_eq_mod_256_iff]
@[simp] theorem UInt16.toUInt32_neg (a : UInt16) : (-a).toUInt32 = -a.toUInt32 % 65536 := by
simp [UInt16.toUInt32_eq_mod_65536_iff]
@[simp] theorem UInt16.toUInt64_neg (a : UInt16) : (-a).toUInt64 = -a.toUInt64 % 65536 := by
simp [UInt16.toUInt64_eq_mod_65536_iff]
@[simp] theorem UInt16.toUSize_neg (a : UInt16) : (-a).toUSize = -a.toUSize % 65536 := by
simp [UInt16.toUSize_eq_mod_65536_iff]
@[simp] theorem UInt32.toUInt64_neg (a : UInt32) : (-a).toUInt64 = -a.toUInt64 % 4294967296 := by
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
@[simp] theorem UInt32.toUSize_neg (a : UInt32) : (-a).toUSize = -a.toUSize % 4294967296 := by
simp [UInt32.toUSize_eq_mod_4294967296_iff]
@[simp] theorem USize.toUInt64_neg (a : USize) : (-a).toUInt64 = -a.toUInt64 % UInt64.ofNat USize.size := by
simp [USize.toUInt64_eq_mod_usizeSize_iff]
@[simp] theorem UInt8.toNat_neg (a : UInt8) : (-a).toNat = (UInt8.size - a.toNat) % UInt8.size := rfl
@[simp] theorem UInt16.toNat_neg (a : UInt16) : (-a).toNat = (UInt16.size - a.toNat) % UInt16.size := rfl
@[simp] theorem UInt32.toNat_neg (a : UInt32) : (-a).toNat = (UInt32.size - a.toNat) % UInt32.size := rfl
@[simp] theorem UInt64.toNat_neg (a : UInt64) : (-a).toNat = (UInt64.size - a.toNat) % UInt64.size := rfl
@[simp] theorem USize.toNat_neg (a : USize) : (-a).toNat = (USize.size - a.toNat) % USize.size := rfl
theorem UInt8.sub_eq_add_neg (a b : UInt8) : a - b = a + (-b) := UInt8.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
theorem UInt16.sub_eq_add_neg (a b : UInt16) : a - b = a + (-b) := UInt16.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
theorem UInt32.sub_eq_add_neg (a b : UInt32) : a - b = a + (-b) := UInt32.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
theorem UInt64.sub_eq_add_neg (a b : UInt64) : a - b = a + (-b) := UInt64.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
theorem USize.sub_eq_add_neg (a b : USize) : a - b = a + (-b) := USize.toBitVec_inj.1 (BitVec.sub_toAdd _ _)
theorem UInt8.neg_one_eq : (-1 : UInt8) = 255 := rfl
theorem UInt16.neg_one_eq : (-1 : UInt16) = 65535 := rfl
theorem UInt32.neg_one_eq : (-1 : UInt32) = 4294967295 := rfl
theorem UInt64.neg_one_eq : (-1 : UInt64) = 18446744073709551615 := rfl
theorem USize.neg_one_eq : (-1 : USize) = USize.ofNatLT (USize.size - 1) (Nat.sub_one_lt (Nat.pos_iff_ne_zero.1 size_pos)) :=
USize.toNat.inj (by simp)
theorem UInt8.toBitVec_zero : toBitVec 0 = 0#8 := rfl
theorem UInt16.toBitVec_zero : toBitVec 0 = 0#16 := rfl
theorem UInt32.toBitVec_zero : toBitVec 0 = 0#32 := rfl
theorem UInt64.toBitVec_zero : toBitVec 0 = 0#64 := rfl
theorem USize.toBitVec_zero : toBitVec 0 = 0#System.Platform.numBits := rfl
theorem UInt8.toBitVec_one : toBitVec 1 = 1#8 := rfl
theorem UInt16.toBitVec_one : toBitVec 1 = 1#16 := rfl
theorem UInt32.toBitVec_one : toBitVec 1 = 1#32 := rfl
theorem UInt64.toBitVec_one : toBitVec 1 = 1#64 := rfl
theorem USize.toBitVec_one : toBitVec 1 = 1#System.Platform.numBits := rfl
theorem UInt8.neg_eq_neg_one_mul (a : UInt8) : -a = -1 * a := by
apply UInt8.toBitVec_inj.1
rw [UInt8.toBitVec_neg, UInt8.toBitVec_mul, UInt8.toBitVec_neg, UInt8.toBitVec_one, BitVec.neg_eq_neg_one_mul]
theorem UInt16.neg_eq_neg_one_mul (a : UInt16) : -a = -1 * a := by
apply UInt16.toBitVec_inj.1
rw [UInt16.toBitVec_neg, UInt16.toBitVec_mul, UInt16.toBitVec_neg, UInt16.toBitVec_one, BitVec.neg_eq_neg_one_mul]
theorem UInt32.neg_eq_neg_one_mul (a : UInt32) : -a = -1 * a := by
apply UInt32.toBitVec_inj.1
rw [UInt32.toBitVec_neg, UInt32.toBitVec_mul, UInt32.toBitVec_neg, UInt32.toBitVec_one, BitVec.neg_eq_neg_one_mul]
theorem UInt64.neg_eq_neg_one_mul (a : UInt64) : -a = -1 * a := by
apply UInt64.toBitVec_inj.1
rw [UInt64.toBitVec_neg, UInt64.toBitVec_mul, UInt64.toBitVec_neg, UInt64.toBitVec_one, BitVec.neg_eq_neg_one_mul]
theorem USize.neg_eq_neg_one_mul (a : USize) : -a = -1 * a := by
apply USize.toBitVec_inj.1
rw [USize.toBitVec_neg, USize.toBitVec_mul, USize.toBitVec_neg, USize.toBitVec_one, BitVec.neg_eq_neg_one_mul]
theorem UInt8.sub_eq_add_mul (a b : UInt8) : a - b = a + 255 * b := by
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
theorem UInt16.sub_eq_add_mul (a b : UInt16) : a - b = a + 65535 * b := by
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
theorem UInt32.sub_eq_add_mul (a b : UInt32) : a - b = a + 4294967295 * b := by
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
theorem UInt64.sub_eq_add_mul (a b : UInt64) : a - b = a + 18446744073709551615 * b := by
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
theorem USize.sub_eq_add_mul (a b : USize) : a - b = a + USize.ofNatLT (USize.size - 1) (Nat.sub_one_lt (Nat.pos_iff_ne_zero.1 size_pos)) * b := by
rw [sub_eq_add_neg, neg_eq_neg_one_mul, neg_one_eq]
@[simp] theorem UInt8.ofNat_usizeSize_sub_one : UInt8.ofNat (USize.size - 1) = 255 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt16.ofNat_usizeSize_sub_one : UInt16.ofNat (USize.size - 1) = 65535 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt32.ofNat_usizeSize_sub_one : UInt32.ofNat (USize.size - 1) = 4294967295 := UInt32.toNat.inj (by simp)
@[simp] theorem USize.ofNat_uInt64Size_sub_one : USize.ofNat (UInt64.size - 1) = USize.ofNatLT (USize.size - 1) (Nat.sub_one_lt (Nat.pos_iff_ne_zero.1 size_pos)) :=
USize.toNat.inj (by simp [USize.toNat_ofNat])
@[simp] theorem UInt16.toUInt8_sub (a b : UInt16) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
@[simp] theorem UInt32.toUInt8_sub (a b : UInt32) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
@[simp] theorem UInt32.toUInt16_sub (a b : UInt32) : (a - b).toUInt16 = a.toUInt16 - b.toUInt16 := by
simp [sub_eq_add_neg, UInt16.sub_eq_add_neg]
@[simp] theorem UInt64.toUInt8_sub (a b : UInt64) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
@[simp] theorem UInt64.toUInt16_sub (a b : UInt64) : (a - b).toUInt16 = a.toUInt16 - b.toUInt16 := by
simp [sub_eq_add_neg, UInt16.sub_eq_add_neg]
@[simp] theorem UInt64.toUInt32_sub (a b : UInt64) : (a - b).toUInt32 = a.toUInt32 - b.toUInt32 := by
simp [sub_eq_add_neg, UInt32.sub_eq_add_neg]
@[simp] theorem UInt64.toUSize_sub (a b : UInt64) : (a - b).toUSize = a.toUSize - b.toUSize := by
simp [sub_eq_add_neg, USize.sub_eq_add_neg]
@[simp] theorem USize.toUInt8_sub (a b : USize) : (a - b).toUInt8 = a.toUInt8 - b.toUInt8 := by
simp [sub_eq_add_neg, UInt8.sub_eq_add_neg]
@[simp] theorem USize.toUInt16_sub (a b : USize) : (a - b).toUInt16 = a.toUInt16 - b.toUInt16 := by
simp [sub_eq_add_neg, UInt16.sub_eq_add_neg]
@[simp] theorem USize.toUInt32_sub (a b : USize) : (a - b).toUInt32 = a.toUInt32 - b.toUInt32 := by
simp [sub_eq_add_neg, UInt32.sub_eq_add_neg]
@[simp] theorem UInt8.toUInt16_sub (a b : UInt8) : (a - b).toUInt16 = (a.toUInt16 - b.toUInt16) % 256 := by
simp [UInt8.toUInt16_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt32_sub (a b : UInt8) : (a - b).toUInt32 = (a.toUInt32 - b.toUInt32) % 256 := by
simp [UInt8.toUInt32_eq_mod_256_iff]
@[simp] theorem UInt8.toUInt64_sub (a b : UInt8) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % 256 := by
simp [UInt8.toUInt64_eq_mod_256_iff]
@[simp] theorem UInt8.toUSize_sub (a b : UInt8) : (a - b).toUSize = (a.toUSize - b.toUSize) % 256 := by
simp [UInt8.toUSize_eq_mod_256_iff]
@[simp] theorem UInt16.toUInt32_sub (a b : UInt16) : (a - b).toUInt32 = (a.toUInt32 - b.toUInt32) % 65536 := by
simp [UInt16.toUInt32_eq_mod_65536_iff]
@[simp] theorem UInt16.toUInt64_sub (a b : UInt16) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % 65536 := by
simp [UInt16.toUInt64_eq_mod_65536_iff]
@[simp] theorem UInt16.toUSize_sub (a b : UInt16) : (a - b).toUSize = (a.toUSize - b.toUSize) % 65536 := by
simp [UInt16.toUSize_eq_mod_65536_iff]
@[simp] theorem UInt32.toUInt64_sub (a b : UInt32) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % 4294967296 := by
simp [UInt32.toUInt64_eq_mod_4294967296_iff]
@[simp] theorem UInt32.toUSize_sub (a b : UInt32) : (a - b).toUSize = (a.toUSize - b.toUSize) % 4294967296 := by
simp [UInt32.toUSize_eq_mod_4294967296_iff]
@[simp] theorem USize.toUInt64_sub (a b : USize) : (a - b).toUInt64 = (a.toUInt64 - b.toUInt64) % UInt64.ofNat USize.size := by
simp [USize.toUInt64_eq_mod_usizeSize_iff]

View File

@@ -6,17 +6,87 @@ Authors: Henrik Böving
prelude
import Init.Data.Fin.Log2
/--
Base-two logarithm of 8-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
This function is overridden at runtime with an efficient implementation. This definition is
the logical model.
Examples:
* `UInt8.log2 0 = 0`
* `UInt8.log2 1 = 0`
* `UInt8.log2 2 = 1`
* `UInt8.log2 4 = 2`
* `UInt8.log2 7 = 2`
* `UInt8.log2 8 = 3`
-/
@[extern "lean_uint8_log2"]
def UInt8.log2 (a : UInt8) : UInt8 := Fin.log2 a.toFin
/--
Base-two logarithm of 16-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
This function is overridden at runtime with an efficient implementation. This definition is
the logical model.
Examples:
* `UInt16.log2 0 = 0`
* `UInt16.log2 1 = 0`
* `UInt16.log2 2 = 1`
* `UInt16.log2 4 = 2`
* `UInt16.log2 7 = 2`
* `UInt16.log2 8 = 3`
-/
@[extern "lean_uint16_log2"]
def UInt16.log2 (a : UInt16) : UInt16 := Fin.log2 a.toFin
/--
Base-two logarithm of 32-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
This function is overridden at runtime with an efficient implementation. This definition is
the logical model.
Examples:
* `UInt32.log2 0 = 0`
* `UInt32.log2 1 = 0`
* `UInt32.log2 2 = 1`
* `UInt32.log2 4 = 2`
* `UInt32.log2 7 = 2`
* `UInt32.log2 8 = 3`
-/
@[extern "lean_uint32_log2"]
def UInt32.log2 (a : UInt32) : UInt32 := Fin.log2 a.toFin
/--
Base-two logarithm of 64-bit unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
This function is overridden at runtime with an efficient implementation. This definition is
the logical model.
Examples:
* `UInt64.log2 0 = 0`
* `UInt64.log2 1 = 0`
* `UInt64.log2 2 = 1`
* `UInt64.log2 4 = 2`
* `UInt64.log2 7 = 2`
* `UInt64.log2 8 = 3`
-/
@[extern "lean_uint64_log2"]
def UInt64.log2 (a : UInt64) : UInt64 := Fin.log2 a.toFin
/--
Base-two logarithm of word-sized unsigned integers. Returns `⌊max 0 (log₂ a)⌋`.
This function is overridden at runtime with an efficient implementation. This definition is
the logical model.
Examples:
* `USize.log2 0 = 0`
* `USize.log2 1 = 0`
* `USize.log2 2 = 1`
* `USize.log2 4 = 2`
* `USize.log2 7 = 2`
* `USize.log2 8 = 3`
-/
@[extern "lean_usize_log2"]
def USize.log2 (a : USize) : USize := Fin.log2 a.toFin

View File

@@ -468,7 +468,8 @@ If not, usually the right approach is `simp [Vector.unattach, -Vector.map_subtyp
-/
def unattach {α : Type _} {p : α Prop} (xs : Vector { x // p x } n) : Vector α n := xs.map (·.val)
@[simp] theorem unattach_nil {p : α Prop} : (#v[] : Vector { x // p x } 0).unattach = #v[] := rfl
@[simp] theorem unattach_nil {p : α Prop} : (#v[] : Vector { x // p x } 0).unattach = #v[] := by simp
@[simp] theorem unattach_push {p : α Prop} {a : { x // p x }} {xs : Vector { x // p x } n} :
(xs.push a).unattach = xs.unattach.push a.1 := by
simp only [unattach, Vector.map_push]
@@ -592,8 +593,11 @@ and simplifies these to the function directly taking the value.
unfold Array.unattach
rfl
@[simp] theorem unattach_mkVector {p : α Prop} {n : Nat} {x : { x // p x }} :
(mkVector n x).unattach = mkVector n x.1 := by
@[simp] theorem unattach_replicate {p : α Prop} {n : Nat} {x : { x // p x }} :
(replicate n x).unattach = replicate n x.1 := by
simp [unattach]
@[deprecated unattach_replicate (since := "2025-03-18")]
abbrev unattach_mkVector := @unattach_replicate
end Vector

View File

@@ -29,7 +29,9 @@ deriving Repr, DecidableEq
attribute [simp] Vector.size_toArray
/-- Convert `xs : Array α` to `Vector α xs.size`. -/
/--
Converts an array to a vector. The resulting vector's size is the array's size.
-/
abbrev Array.toVector (xs : Array α) : Vector α xs.size := .mk xs rfl
namespace Vector
@@ -65,16 +67,19 @@ def elimAsList {motive : Vector α n → Sort u}
abbrev mkEmpty := @emptyWithCapacity
/-- Makes a vector of size `n` with all cells containing `v`. -/
@[inline] def mkVector (n) (v : α) : Vector α n := mkArray n v, by simp
@[inline] def replicate (n) (v : α) : Vector α n := Array.replicate n v, by simp
@[deprecated replicate (since := "2025-03-18")]
abbrev mkVector := @replicate
instance : Nonempty (Vector α 0) := #v[]
instance [Nonempty α] : Nonempty (Vector α n) := mkVector _ Classical.ofNonempty
instance [Nonempty α] : Nonempty (Vector α n) := replicate _ Classical.ofNonempty
/-- Returns a vector of size `1` with element `v`. -/
@[inline] def singleton (v : α) : Vector α 1 := #[v], rfl
instance [Inhabited α] : Inhabited (Vector α n) where
default := mkVector n default
default := replicate n default
/-- Get an element of a vector using a `Fin` index. -/
@[inline] def get (xs : Vector α n) (i : Fin n) : α :=
@@ -469,7 +474,7 @@ Note that we immediately simplify this to an `++` operation,
and do not provide separate verification theorems.
-/
@[inline, simp] def leftpad (n : Nat) (a : α) (xs : Vector α m) : Vector α (max n m) :=
(mkVector (n - m) a ++ xs).cast (by omega)
(replicate (n - m) a ++ xs).cast (by omega)
/--
Pad a vector on the right with a given element.
@@ -478,7 +483,7 @@ Note that we immediately simplify this to an `++` operation,
and do not provide separate verification theorems.
-/
@[inline, simp] def rightpad (n : Nat) (a : α) (xs : Vector α m) : Vector α (max n m) :=
(xs ++ mkVector (n - m) a).cast (by omega)
(xs ++ replicate (n - m) a).cast (by omega)
/-! ### ForIn instance -/

View File

@@ -72,10 +72,13 @@ theorem countP_le_size {xs : Vector α n} : countP p xs ≤ n := by
rcases xs with xs, rfl
simp
theorem countP_mkVector (p : α Bool) (a : α) (n : Nat) :
countP p (mkVector n a) = if p a then n else 0 := by
simp only [mkVector_eq_mk_mkArray, countP_cast, countP_mk]
simp [Array.countP_mkArray]
theorem countP_replicate (p : α Bool) (a : α) (n : Nat) :
countP p (replicate n a) = if p a then n else 0 := by
simp only [replicate_eq_mk_replicate, countP_cast, countP_mk]
simp [Array.countP_replicate]
@[deprecated countP_replicate (since := "2025-03-18")]
abbrev countP_mkVector := @countP_replicate
theorem boole_getElem_le_countP (p : α Bool) (xs : Vector α n) (i : Nat) (h : i < n) :
(if p xs[i] then 1 else 0) xs.countP p := by
@@ -217,13 +220,19 @@ theorem count_eq_size {xs : Vector α n} : count a xs = xs.size ↔ ∀ b ∈ xs
rcases xs with xs, rfl
simp [Array.count_eq_size]
@[simp] theorem count_mkVector_self (a : α) (n : Nat) : count a (mkVector n a) = n := by
simp only [mkVector_eq_mk_mkArray, count_cast, count_mk]
@[simp] theorem count_replicate_self (a : α) (n : Nat) : count a (replicate n a) = n := by
simp only [replicate_eq_mk_replicate, count_cast, count_mk]
simp
theorem count_mkVector (a b : α) (n : Nat) : count a (mkVector n b) = if b == a then n else 0 := by
simp only [mkVector_eq_mk_mkArray, count_cast, count_mk]
simp [Array.count_mkArray]
@[deprecated count_replicate_self (since := "2025-03-18")]
abbrev count_mkVector_self := @count_replicate_self
theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b == a then n else 0 := by
simp only [replicate_eq_mk_replicate, count_cast, count_mk]
simp [Array.count_replicate]
@[deprecated count_replicate (since := "2025-03-18")]
abbrev count_mkVector := @count_replicate
theorem count_le_count_map [DecidableEq β] (xs : Vector α n) (f : α β) (x : α) :
count x xs count (f x) (map f xs) := by

View File

@@ -69,10 +69,13 @@ theorem eraseIdx_cast {xs : Vector α n} {k : Nat} (h : k < m) :
rcases xs with xs
simp
theorem eraseIdx_mkVector {n : Nat} {a : α} {k : Nat} {h} :
(mkVector n a).eraseIdx k = mkVector (n - 1) a := by
rw [mkVector_eq_mk_mkArray, eraseIdx_mk]
simp [Array.eraseIdx_mkArray, *]
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by
rw [replicate_eq_mk_replicate, eraseIdx_mk]
simp [Array.eraseIdx_replicate, *]
@[deprecated eraseIdx_replicate (since := "2025-03-18")]
abbrev eraseIdx_mkVector := @eraseIdx_replicate
theorem mem_eraseIdx_iff_getElem {x : α} {xs : Vector α n} {k} {h} : x xs.eraseIdx k h i w, i k xs[i]'w = x := by
rcases xs with xs

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