Compare commits

...

207 Commits

Author SHA1 Message Date
Kim Morrison
4b7f5cbef6 disable linter 2025-03-17 12:41:32 +11:00
Kim Morrison
65ab894ed4 fix: make List/Array modify argument order consistent 2025-03-17 12:41:30 +11:00
Leonardo de Moura
d5f01f2db1 feat: Nat divisibility constraints in cutsat (#7495)
This PR implements support for `Nat` divisibility constraints in the
cutsat procedure.
2025-03-15 03:46:47 +00:00
Leonardo de Moura
c8aae00847 feat: Nat inequalities in cutsat (#7494)
This PR implements support for `Nat` inequalities in the cutsat
procedure.
2025-03-15 00:43:18 +00:00
Siddharth
1bbd2c183b feat: BitVec.extract_Lsb'_append_[ite|of_lt|of_le] (#7482)
This PR implements the
[BV_EXTRACT_CONCAT](6a1a768987/src/rewrite/rewrites_bv.cpp (L1264))
rule from Bitwuzla, which explains how to extract bits from an append.
We first prove a 'master theorem' which has the full case analysis, from
which we rapidly derive the necessary `BV_EXTRACT_CONCAT` theorems:

```lean
theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start len : Nat} :
    extractLsb' start len (xhi ++ xlo) =
    if hstart : start < w
    then
      if hlen : start + len < w
      then extractLsb' start len xlo
      else
        (((extractLsb' (start - w) (len - (w - start)) xhi) ++
            extractLsb' start (w - start) xlo)).cast (by omega)
    else
      extractLsb' (start - w) len xhi

theorem extractLsb'_append_eq_of_lt {v w} {xhi : BitVec v} {xlo : BitVec w}
    {start len : Nat} (h : start + len < w) :
    extractLsb' start len (xhi ++ xlo) = extractLsb' start len xlo

theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
    {start len : Nat} (h : w ≤ start) :
    extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi
```

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2025-03-14 18:25:50 +00:00
Henrik Böving
b55a5b0826 feat: add BitVec.add_neg_mul to bv_decide (#7486)
This PR adds the BitVec.add_neg_mul rule introduced in #7481 to
bv_decide's preprocessor.
2025-03-14 15:28:20 +00:00
Sebastian Ullrich
eeca0ce96b perf: Environment blocker removals from async-proofs branch (#7483) 2025-03-14 13:37:01 +00:00
Siddharth
2cb89823f3 feat: BitVec.BV_ADD_NEG_MUL (#7481)
This PR implements the Bitwuzla rewrites [BV_ADD_NEG_MUL](), and
associated lemmas to make the proof streamlined. ```bvneg (bvadd a
(bvmul a b)) = (bvmul a (bvnot b))```, or spelled as lean:

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

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2025-03-14 13:21:17 +00:00
Henrik Böving
297be24c0d feat: bv_decide rewrites around ult, signExtend and extractLsb (#7480)
This PR adds the necessary rewrites for the Bitwuzla rules
BV_ULT_SPECIAL_CONST, BV_SIGN_EXTEND_ELIM, TODO.
2025-03-14 09:55:44 +00:00
Lean stage0 autoupdater
e59f487bf0 chore: update stage0 2025-03-14 08:29:06 +00:00
Sebastian Ullrich
e1d15946f7 feat: elaborate theorem bodies in parallel (#7084)
This PR enables the elaboration of theorem bodies, i.e. proofs, to
happen in parallel to each other as well as to other elaboration tasks.

Specifically, to be eligible for parallel proof elaboration,
* the theorem must not be in a `mutual` block
* `deprecated.oldSectionVars` must not be set
* `Elab.async` must be set (currently defaults to `true` in the language
server, `false` on the cmdline)

To be activated for downstream projects (i.e. in stage 1) pending
further Mathlib validation.
2025-03-14 07:50:42 +00:00
Eric Wieser
5c333d88c0 feat: mark forIn_pure_yield lemmas simp (#7433)
This PR makes `simp` able to simplify basic `for` loops in monads other
than `Id`.

This is some prework for #7352, where the `Id` lemmas will be
deprecated.
2025-03-14 00:28:23 +00:00
Sebastian Ullrich
07ee2eea21 fix: report replay kernel errors as standard diagnostics (#7471)
Avoids panics from follow-up cancellation errors

Fixes #7462
2025-03-13 18:45:46 +00:00
Henrik Böving
af82d75e86 fix: bv_decide don't analyze terms under binders by accident (#7477)
This PR ensures that bv_decide doesn't accidentally operate on terms
underneath binders. As there is currently no binder construct that is in
the supported fragment of bv_decide this changes nothing about the proof
power.

Closes #7475
2025-03-13 16:47:20 +00:00
David Thrane Christiansen
25179352b4 doc: review List docstrings for manual (#7452)
This PR makes the style of all `List` docstrings that appear in the
language reference consistent.

Relies on #7240 for links and example formatting.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2025-03-13 16:10:06 +00:00
David Thrane Christiansen
06c57826ae doc: manual docstring review for smaller namespaces (#7365)
This PR updates docstrings and adds some that are missing.
2025-03-13 16:09:37 +00:00
Sebastian Ullrich
044e3b1b56 fix: heartbeats from realizeConst should be ignored (#7473)
Avoids nondeterministic counting from racing threads
2025-03-13 15:10:29 +00:00
Sebastian Ullrich
96f9ee2a41 feat: allow async elab tasks to contribute to info trees reported to linters and request handlers (#7457)
This PR ensures info tree users such as linters and request handlers
have access to info subtrees created by async elab task by introducing
API to leave holes filled by such tasks.

**Breaking change**: other metaprogramming users of
`Command.State.infoState` may need to call `InfoState.substituteLazy` on
it manually to fill all holes.
2025-03-13 15:09:00 +00:00
Sebastian Ullrich
0f3d426591 chore: fix confusing Environment.replayConsts parameter order (#7472) 2025-03-13 12:35:45 +00:00
Sebastian Ullrich
a014ae1001 fix: make Term.mkAuxName async-compatible (#7468) 2025-03-13 12:24:24 +00:00
Lean stage0 autoupdater
137f559520 chore: update stage0 2025-03-13 11:55:08 +00:00
Siddharth
3d6d51d2c6 feat: BitVec.lt_allOnes (#7465)
This PR adds the theorem:  
```lean
theorem lt_allOnes_iff {x : BitVec w} : x < allOnes w ↔ x ≠ allOnes w
```
to simplify comparisons against `-1#w`. This is a corollary of the
existing lemma:
```lean
theorem allOnes_le_iff {x : BitVec w} : allOnes w ≤ x ↔ x = allOnes w
```
2025-03-13 09:43:17 +00:00
Sebastian Ullrich
3786ad6d0c chore: reset stdlib_flags (#7469)
Changed accidentally at
a2cb435aa1 (diff-83fe8b23d47dfed772cebbb2d0f7809b137482ab0d5c5aea66fb5b8ccefa1898)
2025-03-13 09:40:27 +00:00
Paul Reichert
b16769f5a0 feat: new tree map lemmas for getKey (#7412)
This PR provides lemmas about the tree map that have been introduced to
the hash map in #7289.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-13 08:13:18 +00:00
Markus Himmel
4262ea14d6 fix: Repr instance for Int32 (#7467)
This PR fixes the `Repr Int32` instance, which was previously repeating
the `Repr Int16` instance due to a copy-and-paste error.
2025-03-13 06:24:42 +00:00
Kim Morrison
816da7120e feat: cleanup of Int simp lemmas (#7466)
This PR further cleans up simp lemmas for `Int`.
2025-03-13 06:07:19 +00:00
Kim Morrison
38ed354cdb feat: Nat.add_div_of_dvd_add_add_one (#7432)
This PR adds a consequence of `Nat.add_div` using a divisibility
hypothesis.
2025-03-13 05:40:34 +00:00
Kim Morrison
56ac94b591 chore: rename Array.mkEmpty to emptyWithCapacity (#7445)
This PR renames `Array.mkEmpty` to `emptyWithCapacity`. (Similarly for
`ByteArray` and `FloatArray`.)
2025-03-12 23:19:17 +00:00
Lean stage0 autoupdater
b78352ec9d chore: update stage0 2025-03-12 23:48:59 +00:00
Kim Morrison
1feae7abe1 fix: indenting in release notes script (#7326)
This PR updates the release notes script to better indent PR
descriptions.
2025-03-12 23:02:02 +00:00
Kim Morrison
a2cb435aa1 chore: rename HashMap.empty to HashMap.emptyWithCapacity (#7447)
This PR renames `.empty` with `.emptyWithCapacity`. This is a companion
to #7445 for `Array`.
2025-03-12 23:01:18 +00:00
David Thrane Christiansen
c3f61ba3a2 chore: fix typo in test (#7460)
This PR fixes a minor typo in a test.
2025-03-12 16:43:35 +00:00
Siddharth
8850f9e9aa feat: BitVec.signExtend_eq_append_extractLsb' (#7454)
This PR implements the bitwuzla rule
[BV_SIGN_EXTEND_ELIM](https://github.com/bitwuzla/bitwuzla/blob/main/src/rewrite/rewrites_bv.cpp#L3638-L3663),
which rewrites a `signExtend x` as an `append` of the appropriate sign
bits, followed by the bits of `x`.

---------

Co-authored-by: Alex Keizer <alex@keizer.dev>
2025-03-12 15:40:23 +00:00
Pim Otte
2363d2fa87 chore: readd "(kernel)" to kernel error message (#7453)
This PR adds "(kernel)" to the message for the kernel-level application
type mismatch error.

It appears to have been accidentally removed in
b705142ae4.
2025-03-12 11:27:42 +00:00
Lean stage0 autoupdater
d1d2f215ad chore: update stage0 2025-03-12 10:19:24 +00:00
David Thrane Christiansen
eb58f46ce7 feat: language reference links and examples in docstrings (#7240)
This PR adds a canonical syntax for linking to sections in the language
reference along with formatting of examples in docstrings according to
the docstring style guide.


Docstrings are now pre-processed as follows:

* Output included as part of examples is shown with leading line comment
indicators in hovers

* URLs of the form `lean-manual://section/section-id` are rewritten to
links that point at the corresponding section in the Lean reference
manual. The reference manual's base URL is configured when Lean is built
and can be overridden with the `LEAN_MANUAL_ROOT` environment variable.
This way, releases can point documentation links to the correct
snapshot, and users can use their own, e.g. for offline reading.

Manual URLs in docstrings are validated when the docstring is added. The
presence of a URL starting with `lean-manual://` that is not a
syntactically valid section link causes the docstring to be rejected.
This allows for future extensibility to the set of allowed links. There
is no validation that the linked-to section actually exists. To provide
the best possible error messages in case of validation failures,
`Lean.addDocString` now takes a `TSyntax ``docComment` instead of a
string; clients should adapt by removing the step that extracts the
string, or by calling the lower-level `addDocStringCore` in cases where
the docstring in question is obtained from the environment and has thus
already had its links validated.

A stage0 update is required to make the documentation site configurable
at build time and for releases. A local commit on top of a stage0 update
that will be sent in a followup PR includes the configurable reference
manual root and updates to the release checklist.

---------

Co-authored-by: Marc Huisinga <mhuisi@protonmail.com>
2025-03-12 09:17:27 +00:00
Markus Himmel
1a2345b47f chore: rename insert_emptyc_eq to insert_empty_eq (#7451)
This PR renames the member `insert_emptyc_eq` of the `LawfulSingleton`
typeclass to `insert_empty_eq` to conform to the recommended spelling of
`∅` as `empty`.

See also #7447.
2025-03-12 09:14:05 +00:00
Kim Morrison
c1d145e9d7 feat: revision of Nat/Int lemmas (#7435)
This PR reviews the `Nat` and `Int` API, making the interfaces more
consistent.
2025-03-12 05:52:09 +00:00
Kim Morrison
3a308324f6 chore: use notation in favour of .empty functions (#7446)
This PR prefers using `∅` instead of `.empty` functions. We may later
rename `.empty` functions to avoid the naming clash with
`EmptyCollection`, and to better express semantics of functions which
take an optional capacity argument.
2025-03-12 04:22:40 +00:00
Kim Morrison
bc2561f538 chore: better hypothesis for Vector.getElem_take (#7449)
Fixes a problematic hypothesis as reported on zulip: 
[#lean4 > Vector refactor @
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Vector.20refactor/near/500330457).
2025-03-12 04:16:22 +00:00
Kim Morrison
ed89c2611e chore: fix duplicated namespaces (#7448) 2025-03-12 04:14:31 +00:00
Henrik Böving
2952cf81e6 feat: bv_decide rewrites for concatenation and extraction (#7441)
This PR adds the BV_CONCAT_CONST, BV_CONCAT_EXTRACT and ELIM_ZERO_EXTEND
rule from Bitwuzla to bv_decide.
2025-03-11 22:24:05 +00:00
Bhavik Mehta
589eff6187 doc: correct typo in PSigma projection docstrings (#7443)
These docstrings are for PSigma projections, so change them to refer to
PSigma rather than Sigma.
2025-03-11 18:36:24 +00:00
Sebastian Ullrich
7c5b423659 chore: unconditionally re-enable realizeConst (#7334)
To be merged when Mathlib adaption passes
2025-03-11 16:39:17 +00:00
jrr6
b1bd2c931c feat: allow turnstiles anywhere in location sequences (#7431)
This PR changes the syntax of location modifiers for tactics like `simp`
and `rw` (e.g., `simp at h ⊢`) to allow the turnstile `⊢` to appear
anywhere in the sequence of locations.

Closes #2278.
2025-03-11 15:34:40 +00:00
Henrik Böving
ce614bd830 chore: don't run MacOS aarch64 in merge queue (#7439)
This PR skips running MacOS aarch64 CI in merge queue but leaves it
enabled in PR and release CI.
2025-03-11 14:35:10 +00:00
Henrik Böving
1731f2f850 feat: add more constant related rewrites to bv_decide (#7438)
This PR adds the EQUAL_CONST_BV_ADD and BV_AND_CONST rules to
bv_decide's preprocessor.
2025-03-11 13:37:12 +00:00
Siddharth
bfe7b1fb34 feat: BitVec.extractLsb'_append_extractLsb'_eq_extractLsb' (#7427)
This PR implements the bitwuzla rule
[`BV_CONCAT_EXTRACT`](https://github.com/bitwuzla/bitwuzla/blob/main/src/rewrite/rewrites_bv.cpp#L1146-L1176).
This will be used by the bitblaster to simplify adjacent `extract`s
into a single `extract`.

We also implement the negated version of the rule,
which allows adjacent `not (extractLsb' _)` to be simplified into a
single `not (extractLsb' _)`.
2025-03-11 12:27:39 +00:00
Siddharth
0a14ec0978 feat: BitVec.setWidth_eq_append (#7424)
This PR proves Bitwuzla's rule
[`BV_ZERO_EXTEND_ELIM`](6a1a768987/src/rewrite/rewrites_bv.cpp (L4021-L4033)):

```lean
theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v ≤ w) :
    x.setWidth w = ((0#(w - v)) ++ x).cast (by omega) := by
```

We introduce a more general helper lemma for the above:

```lean
theorem setWidth_eq_append_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} :
    x.setWidth w = ((0#(w - v)) ++ x.extractLsb' 0 (min v w)).cast (by omega)
```

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2025-03-11 12:26:30 +00:00
Henrik Böving
bb47469d1a feat: add simprocs for turning shifts by constants into extracts to bv_decide (#7436)
This PR adds simprocs that turn left and right shifts by constants into
extracts to bv_decide.
2025-03-11 10:09:16 +00:00
Tobias Grosser
e7e57d40c4 feat: add BitVec.[toNat|toFin|toInt]_[sshiftRight|sshiftRight'] (#7104)
This PR adds `BitVec.[toNat|toFin|toInt]_[sshiftRight|sshiftRight']`
plus variants with `of_msb_*`. While at it, we also add
`toInt_zero_length` and `toInt_of_zero_length`. In support of our main
theorem we add `toInt_shiftRight_lt` and `le_toInt_shiftRight`, which
make the main theorem automatically derivable via omega.

We also add four shift lemmas for `Int`: `le_shiftRight_of_nonpos`,
`shiftRight_le_of_nonneg`, `le_shiftRight_of_nonneg`,
`shiftRight_le_of_nonpos`, as well as `emod_eq_add_self_emod`,
`ediv_nonpos_of_nonpos_of_neg `, and`bmod_eq_emod_of_lt `. For `Nat` we
add `shiftRight_le`.

Beyond the lemmas directly needed in the proof, we added a couple more
to ensure the API is complete.

We also fix the casing of `toFin_ushiftRight` and rename `lt_toInt` to
`two_mul_lt_toInt` to avoid `'`-ed lemmas.
2025-03-11 09:51:37 +00:00
Parth Shastri
7c0b72e2c5 fix: make the Subsingleton instance for Squash work for an arbitrary Sort (#7406)
This PR makes the instance for `Subsingleton (Squash α)` work for `α :
Sort u`.

Closes #7405

The fix removes some unused `section`/`variable` commands. They were
mistakenly kept when `EqvGen` was removed in 1d338c4.
2025-03-11 08:41:30 +00:00
Tobias Grosser
8fc8e8ed19 chore: generalize BitVec.toInt_[lt|le]' (#7420)
This PR generalizes `BitVec.toInt_[lt|le]'` to not require `0 < w`.
2025-03-11 06:20:27 +00:00
Kim Morrison
96947280df doc: reference mkEmpty in Array doc-string (#7430)
This PR explains how to use `Array.mkEmpty` to specify the capacity of a
new array, from the `Array` doc-string.
2025-03-10 22:28:22 +00:00
Henrik Böving
0af15f9b1d feat: bv_decide add BV_EXTRACT_FULL preprocessing rule (#7429)
This PR adds the BV_EXTRACT_FULL preprocessing rule from Bitwuzla to
bv_decide.
2025-03-10 22:08:59 +00:00
Lean stage0 autoupdater
dab4908317 chore: update stage0 2025-03-10 22:14:37 +00:00
jrr6
aca1d54514 refactor: add definitions to allow turnstiles anywhere in locations (#7425)
This PR adds definitions that will be required to allow to appear
turnstiles anywhere in tactic location specifiers.

This is the first (pre-stage0 update) half of #6992.
2025-03-10 21:18:00 +00:00
Lean stage0 autoupdater
817772e97b chore: update stage0 2025-03-10 20:18:34 +00:00
Siddharth
af8ec41014 feat: BitVec.extractLsb'_eq_self (#7426)
This PR adds the Bitwuzla rewrite rule
[`BV_EXTRACT_FULL`](6a1a768987/src/rewrite/rewrites_bv.cpp (L1236-L1253)),
which is useful for the bitblaster to simplify `extractLsb'` based
expressions.

```lean
theorem extractLsb'_eq_self (x : BitVec w) : x.extractLsb' 0 w = x
```
2025-03-10 19:16:25 +00:00
Marc Huisinga
51794c384a feat: parallel watchdog requests (#7223)
This PR implements parallel watchdog request processing so that requests
that are processed by the watchdog cannot block the main thread of the
watchdog anymore.

Since this shares the `References` data structure in the watchdog, we
adjust the `References` architecture to use `Std.TreeMap` instead of
`Std.HashMap`, so that updates to the data structure can still be
reasonably fast despite the sharing. This PR also optimizes the
`References` data structure a bit.
2025-03-10 18:46:25 +00:00
jrr6
acfc11ae42 fix: correctly collect let-rec fvars through delayed-assigned mvar (#7304)
This PR fixes an issue where nested `let rec` declarations within
`match` expressions or tactic blocks failed to compile if they were
nested within, and recursively called, a `let rec` that referenced a
variable bound by a containing declaration.

Closes #6927

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-03-10 18:13:48 +00:00
Sebastian Ullrich
9d39942189 fix: find realizations from other env branches (#7385) 2025-03-10 18:04:38 +00:00
Joachim Breitner
829522ba55 test: expand f91 test (#7421) 2025-03-10 17:15:54 +00:00
Lean stage0 autoupdater
d538e1cd90 chore: update stage0 2025-03-10 17:45:27 +00:00
Mac Malone
77609dcdc7 feat: lake: config field autocomplete in whitespace (#7393)
This PR adds autocompletion support for Lake configuration fields in the
Lean DSL at the indented whitespace after an existing field.
Autocompletion in the absence of any fields is currently still not
supported.

**Breaking change:** The nonstandard braced configuration syntax now
uses a semicolon `;` rather than a comma `,` as a separator. Indentation
can still be used as an alternative to the separator.
2025-03-10 15:37:39 +00:00
Lean stage0 autoupdater
22b6b49a43 chore: update stage0 2025-03-10 15:29:45 +00:00
Paul Reichert
f3c507ec57 feat: tree map lemmas for modify (#7419)
This PR provides lemmas about the tree map function `modify` and its
interactions with other functions for which lemmas already exist.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-10 14:35:24 +00:00
Henrik Böving
e0fa6a1792 feat: bv_decide support enum inductive matches with default branches (#7417)
This PR adds support for enum inductive matches with default branches to
bv_decide.
2025-03-10 14:05:04 +00:00
Eric Wieser
9a435b4f4a feat: lemmas about pure for {List,Array,Vector}.{mapM,foldlM,foldrM,anyM,allM,findM?,findSomeM?} (#7356)
This PR adds lemmas reducing monadic operations with `pure` to the
non-monadic counterparts.
2025-03-10 13:55:17 +00:00
Marc Huisinga
80b1ce8cad fix: language server dropping requests (#7178)
This PR fixes a race condition in the language server that would
sometimes cause it to drop requests and never respond to them when
editing the header of a file. This in turn could cause semantic
highlighting to stop functioning in VS Code, as VS Code would stop
emitting requests when a prior request was dropped, and also cause the
InfoView to become defective. It would also cause import auto-completion
to feel a bit wonky, since these requests were sometimes dropped. This
race condition has been present in the language server since its first
version in 2020.

This PR also reverts the futile fix attempt in #7130.

The specific race condition was that if the file worker crashed or had
to be restarted while a request was in flight in the file worker, then
we wouldn't correctly replay it in our watchdog crash-restart logic.
This PR adjusts this logic to fix this.
2025-03-10 13:45:17 +00:00
Paul Reichert
2ac0e4c061 fix: use getElem instead of get in the statements of hash map lemmas (#7418)
This PR renames several hash map lemmas (`get` -> `getElem`) and uses
`m[k]?` instead of `get? m k` (and also for `get!` and `get`).

BREAKING CHANGE: While many lemmas were renamed and the lemma with the
old signature was simply deprecated, some lemmas were changed without
renaming them. They now use the `getElem` variants instead of `get`.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-10 13:31:30 +00:00
Markus Himmel
cdfec6971f feat: remaining lemmas about iterated conversions of finite types (#7414)
This PR adds the remaining lemmas about iterated conversions of finite
type that go through signed or unsigned bounded integers.
2025-03-10 12:58:30 +00:00
Markus Himmel
7365600cf8 feat: BitVec conversion lemmas (#7415)
This PR adds a few lemmas about the interactions of `BitVec` with `Fin`
and `Nat`.
2025-03-10 12:58:13 +00:00
Joachim Breitner
754bab442a feat: omega to abstract its own proofs (#5998)
This PR lets `omega` always abstract its own proofs into an auxiliary
definition. The size of the olean of Vector.Extract goes down from 20MB
to 5MB with this, overall stdlib olean size and build instruction count
go down 5%.

Needs #7362.
2025-03-10 12:39:30 +00:00
Marc Huisinga
4593ff50f0 fix: only log goals accomplished in language server (#7416)
This PR addresses a performance regression noticed at
https://github.com/leanprover/lean4/pull/7366#issuecomment-2708162029.
It also ensures that we also consider the current message log when
logging the goals accomplished message.


`Language.Lean.internal.cmdlineSnapshots` in `Lean.Language.Lean` is
moved to `Lean.internal.cmdlineSnapshots` in `Lean.CoreM` to make the
option available in the elaborator.
2025-03-10 12:17:10 +00:00
Sebastian Ullrich
6ecce365e9 feat: make more constructions async-compatible (#7384) 2025-03-10 09:56:30 +00:00
Paul Reichert
1d17119710 refactor: make DHashMap.Raw.foldRev(M) internal (#7380)
This PR moves `DHashMap.Raw.foldRev(M)` into `DHashMap.Raw.Internal`.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-10 09:51:41 +00:00
Paul Reichert
9233d7a4d7 feat: tree map lemmas for alter (#7367)
This PR provides lemmas for the tree map functions `alter` and `modify`
and their interactions with other functions for which lemmas already
exist.

BREAKING CHANGE: The signature of `size_alter` was corrected for all
four hash map types. Instead of relying on the boolean operations
`contains` and `&&` in the if statements, we now use the `Prop`-based
operations `Membership` and `And`.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-10 09:42:25 +00:00
Sebastian Ullrich
060e137599 chore: enforce awaiting-mathlib label (#7342) 2025-03-10 09:27:43 +00:00
Markus Himmel
7bfa8f6296 feat: finite type conversions (Nat/Int/Fin/BitVec -> IntX -> *) (#7368)
This PR adds lemmas for iterated conversions between finite types,
starting with something of type `Nat`/`Int`/`Fin`/`BitVec` and going
through `IntX`.
2025-03-10 05:53:41 +00:00
Leonardo de Moura
84c7e5db1f test: cutsat (#7411) 2025-03-10 03:30:36 +00:00
Joachim Breitner
c797525d2a fix: WellFounded preprocessing: use dsimp (#7409)
This PR allows the use of `dsimp` during preprocessing of well-founded
definitions. This fixes regressions when using `if-then-else` without
giving a name to the condition, but where the condition is needed for
the termination proof, in cases where that subexpression is reachable
only by dsimp, but not by simp (e.g. inside a dependent let)

Also fixes some preprocessing lemmas to not be bad simp lemmas (with
lambdas on the LHS, due to dot notation and unfortunate argument order)

This fixes #7408.
2025-03-09 22:19:16 +00:00
Henrik Böving
0714a7150b feat: add more multiplication lemmas to bv_normalize (#7407)
This PR adds rules for `-1#w * a = -a` and `a * -1#w = -a` to
bv_normalize as seen in Bitwuzla's BV_MUL_SPECIAL_CONST.

This allows us to solve 
```lean
example {a : BitVec 32} : a + -1 * a = 0 := by bv_normalize
```
which would previously time out.
2025-03-09 18:14:30 +00:00
Leonardo de Moura
9c36901728 chore: cutsat minor improvements (#7404) 2025-03-09 14:50:55 +00:00
Leonardo de Moura
da2d877019 fix: cutsat conflict resolution bug (#7403) 2025-03-09 03:58:30 +00:00
Mac Malone
ffc7ba0829 chore: lake: revert builtin inits, elabs, & macros (#7399)
This PR reverts the new builtin initializers, elaborators, and macros in
Lake back to non-builtin.

That is, it reverts the significant change of #7171. This is done to
potential solve the intermittent test failures Lake has been
experiencing on `master`, which I suspect may be caused by this change.
2025-03-09 01:52:50 +00:00
Leonardo de Moura
09161f6fdd chore: remove workaround (#7402) 2025-03-09 01:46:17 +00:00
Leonardo de Moura
8dc3c53240 feat: tight inequalities using divisibility constraints in cutsat (#7401)
This PR improves the cutsat model search procedure by tightening
inequalities using divisibility constraints.
2025-03-09 00:23:32 +00:00
Joachim Breitner
dd91d7e2e2 fix: bv_omega to use -implicitDefEqProofs (#7387)
This PR uses `-implicitDefEqProofs` in `bv_omega` to ensure it is not
affected by the change in #7386.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2025-03-09 00:13:14 +00:00
David Thrane Christiansen
599444e27e doc: docstrings for Id (#7204)
This PR adds docstrings for the `Id` monad.
2025-03-08 22:17:32 +00:00
David Thrane Christiansen
1a0d2b6fc1 doc: Char docstring proofreading (#7198)
This PR makes the docstrings in the `Char` namespace follow the
documentation conventions.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-03-08 22:17:01 +00:00
Cameron Zwarich
8d0093b43f fix: properly handle scoping of join point candidates in cce (#7398)
This PR fixes a scoping error in the cce (Common Case Elimination) pass
of the old code generator. This pass would create a join point for
common minor premises even if some of those premises were in the bodies
of locally defined functions, which results in an improperly scoped
reference to a join point. The fix is to save/restore candidates when
visiting a lambda.
2025-03-08 18:10:41 +00:00
Leonardo de Moura
d07897fc36 fix: Poly.mul p 0 (#7397)
This PR ensures that `Poly.mul p 0` always returns `Poly.num 0`.
2025-03-08 16:57:13 +00:00
Leonardo de Moura
bfe8e5a958 fix: bug in cutsat model construction (#7396)
This PR fixes a bug in the cutsat model construction. It was searching
for a solution in the wrong direction.
2025-03-08 15:58:20 +00:00
Rob23oba
b9f8a859e7 feat: equivalence on hash maps (#7341)
This PR adds an equivalence relation to the hash map with several lemmas
for it.
2025-03-08 10:44:12 +00:00
Leonardo de Moura
0d3ae7fde5 feat: infrastructure for supporting Nat in cutsat (#7394)
This PR adds infrastructure necessary for supporting `Nat` in the cutsat
procedure. It also makes the `grind` more robust.
2025-03-08 08:36:58 +00:00
David Thrane Christiansen
1bfccf88da doc: add missing Bool docstrings and review existing ones (#7246)
This PR updates existing docstrings for Bool and adds the missing ones.
2025-03-08 08:16:13 +00:00
Leonardo de Moura
565c6f3eb2 fix: if-then-else split + normalization issue in grind (#7392)
This PR fixes an issue in the `grind` tactic when case splitting on
if-then-else expressions.

It adds a new marker gadget that prevents `grind` for re-normalizing the
condition `c` of an if-then-else
expression. Without this marker, the negated condition `¬c` might be
rewritten into
an alternative form `c'`, which `grind` may not recognize as equivalent
to `¬c`.
As a result, `grind` could fail to propagate that `if c then a else b`
simplifies to `b`
in the `¬c` branch.
2025-03-07 23:05:59 +00:00
Henrik Böving
77ae842496 feat: bv_decide remove casts (#7390)
This PR makes bv_decide's preprocessing handle casts, as we are in the
constant BitVec fragment we should be able to always remove them using
BitVec.cast_eq.
2025-03-07 22:40:53 +00:00
Sebastian Ullrich
250b977616 feat: support weak options coming from lake setup-file (#7376)
This PR ensures `weak` options do not have to be repeated in both Lake
`leanOptions` and `moreServerOptions`.
2025-03-07 20:55:53 +00:00
Markus Himmel
a8a5c6cff1 feat: integer prerequisites for finite type lemmas (#7378)
This PR adds lemmas about `Int` that will be required in #7368.

Most notably, we add
```lean
@[simp] theorem neg_nonpos_iff (i : Int) : -i ≤ 0 ↔ 0 ≤ i
```
which causes some breakage but gets us closer to mathlib which has a
more general version of this that applies to `Int`.

Note also that the mathlib adaptation branch deletes the (unused in
mathlib) mathib lemma `Int.zero_le_ofNat` as there is now a
syntactically different (but definitionally equal) `Int.zero_le_ofNat`
in core.
2025-03-07 16:09:03 +00:00
Lean stage0 autoupdater
555f3d86fb chore: update stage0 2025-03-07 15:15:36 +00:00
Marc Huisinga
dc5eb40ca3 feat: 'unsolved goals' & 'goals accomplished' diagnostics (#7366)
This PR adds server-side support for dedicated 'unsolved goals' and
'goals accomplished' diagnostics that will have special support in the
Lean 4 VS Code extension. The special 'unsolved goals' diagnostic is
adapted from the 'unsolved goals' error diagnostic, while the 'goals
accomplished' diagnostic is issued when a `theorem` or `Prop`-typed
`example` has no errors or `sorry`s. The Lean 4 VS Code extension
companion PR is at leanprover/vscode-lean4#585.

Specifically, this PR extends the diagnostics served by the language
server with the following fields:
- `leanTags`: Custom tags that denote the kind of diagnostic that is
being served. As opposed to the `code`, `leanTags` should never be
displayed in the UI. Examples introduced by this PR are a tag to
distinguish 'unsolved goals' errors from other diagnostics, as well as a
tag to distinguish the new 'goals accomplished' diagnostic from other
diagnostics.
- `isSilent`: Whether a diagnostic should not be displayed as a regular
diagnostic in the editor. In VS Code, this means that the diagnostic is
displayed in the InfoView under 'Messages', but that it will not be
displayed under 'All Messages' and that it will also not be displayed
with a squiggly line.

The `isSilent` field is also implemented for `Message` so that silent
diagnostics can be logged in the elaborator. All code paths except for
the language server that display diagnostics to users are adjusted to
filter `Message`s with `isSilent := true`.
2025-03-07 13:50:56 +00:00
Henrik Böving
20571a938b feat: bv_decide support for simple pattern matching on enum inductives (#7329)
This PR adds support to bv_decide for simple pattern matching on enum
inductives. By simple we mean non dependent match statements with all
arms written out.

This PR enables use cases such as:
```lean
namespace PingPong

inductive Direction where
  | goingDown
  | goingUp

structure State where
  val : BitVec 16
  low : BitVec 16
  high : BitVec 16
  direction : Direction

def State.step (s : State) : State :=
  match s.direction with
  | .goingDown =>
    if s.val = s.low then
      { s with direction := .goingUp }
    else
      { s with val := s.val - 1 }
  | .goingUp =>
    if s.val = s.high then
      { s with direction := .goingDown }
    else
      { s with val := s.val + 1 }

def State.steps (s : State) (n : Nat) : State :=
  match n with
  | 0 => s
  | n + 1 => (State.steps s n).step

def Inv (s : State) : Prop := s.low ≤ s.val ∧ s.val ≤ s.high ∧ s.low < s.high

example (s : State) (h : Inv s) (n : Nat) : Inv (State.steps s n) := by
  induction n with
  | zero => simp only [State.steps, Inv] at *; bv_decide
  | succ n ih =>
    simp only [State.steps, State.step, Inv] at *
    bv_decide
```

There is an important thing to consider in this implementation. As the
enums pass can now deal with control flow there is a tension between the
structures and enums pass at play:
1. Enums should run before structures as it could convert matches on
enums into `cond`
chains. This in turn can be used by the structures pass to float
projections into control
   flow which might be necessary.
2. Structures should run before enums as it could reveal new facts about
enums that we might
need to handle. For example a structure might contain a field that
contains a fact about
   some enum. This fact needs to be processed properly by the enums pass

To resolve this tension we do the following:
1. Run the structures pass (if enabled)
2. Run the enums pass (if enabled)
3. Within the enums pass we rerun the part of the structures pass (if
enabled) that could profit from the
enums pass as described above. This comes down to adding a few more
lemmas to a simp
invocation that is going to happen in the enums pass anyway and should
thus be cheap.
2025-03-07 09:23:48 +00:00
Leonardo de Moura
e9f2e1861e feat: cutsat missing case: disequality+inequality+divisibility conflict (#7373)
This PR implements the last missing case for the cutsat procedure and
fixes a bug. During model construction, we may encounter a bounded
interval containing integer solutions that satisfy the divisibility
constraint but fail to satisfy known disequalities.
2025-03-07 01:36:29 +00:00
Leonardo de Moura
905b2eedcd test: cutsat (#7372)
Additional tests for cutsat
2025-03-07 00:31:49 +00:00
Leonardo de Moura
00a4503c4f feat: combine two cutsat proof steps (#7371)
This PR combines two cutsat proof steps that often appear together.
2025-03-06 23:28:49 +00:00
JovanGerb
11aff52fb1 fix: abstractNestedProofs should see into the head of an application (#7353)
This PR changes `abstractNestedProofs` so that it also visits the
subterms in the head of an application.

This oversight caused some definitions in mathlib to have unabstracted
proofs, such as
[CategoryTheory.StructuredArrow.commaMapEquivalenceInverse](https://leanprover-community.github.io/mathlib4_docs/Mathlib/CategoryTheory/Comma/StructuredArrow/CommaMap.html#CategoryTheory.StructuredArrow.commaMapEquivalenceInverse)

Mathlib
[bench](https://github.com/leanprover-community/mathlib4/pull/22613#issuecomment-2704288815):
build instructions -0,166 %
lint instructions -0.72 %

This speedup comes from files containing `CategoryTheory.Functor`, which
contains beta unreduced expressions, where abstracting proofs used to
not happen.

Zulip:
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/dsimp.20simplifies.20proofs.2C.20which.20is.20slow/near/503630173
2025-03-06 20:08:38 +00:00
Leonardo de Moura
ec127a780e feat: simplify cooper case-split proof (#7370)
This PR simplifies the proof term due to the Cooper's conflict
resolution in cutsat.
2025-03-06 19:52:48 +00:00
Leonardo de Moura
b958109d06 feat: let-decls for polynomials in cutsat proof terms (#7369)
This PR uses `let`-declarations for each polynomial occurring in a proof
term generated by the cutsat procedure.
2025-03-06 18:34:26 +00:00
Paul Reichert
d0f4e7c590 feat: tree map lemmas for ofList (#7360)
This PR provides lemmas about the tree map function `ofList` and
interactions with other functions for which lemmas already exist.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-06 16:20:52 +00:00
Joachim Breitner
20d191bc8e fix: allow simp dischargers to add aux decls to the environment (#7362)
This PR allows simp dischargers to add aux decls to the environment.
This enables tactics like `native_decide` to be used here, and unblocks
improvements to omega in #5998.

Fixes #7318
2025-03-06 16:00:59 +00:00
Sebastian Ullrich
24db5b598b feat: use realizeConst for all equation, unfold, induction, and partial fixpoint theorems (#7261)
This PR ensures all equation, unfold, induction, and partial fixpoint
theorem generators in core are compatible with parallelism.

Stacked on #7247
2025-03-06 15:38:04 +00:00
Sebastian Ullrich
141e519009 feat: add async support to more extensions and constructions (#7363) 2025-03-06 14:27:45 +00:00
Kim Morrison
c5cec10788 feat: parity between Int.ediv/tdiv/fdiv theorems (#7358)
This PR fills further gaps in the integer division API, and mostly
achieves parity between the three variants of integer division. There
are still some inequality lemmas about `tdiv` and `fdiv` that are
missing, but as they would have quite awkward statements I'm hoping that
for now no one is going to miss them.
2025-03-06 12:04:14 +00:00
Sebastian Ullrich
950ab377c6 fix: remove incorrect Environment.findAsyncCore? shortcut (#7361)
Breaks with parallel elaboration
2025-03-06 11:07:21 +00:00
Paul Reichert
0c898742f6 feat: tree map lemmas for insertMany (#7331)
This PR provides lemmas about the tree map function `insertMany` and its
interaction with other functions for which lemmas already exist. Most
lemmas about `ofList`, which is related to `insertMany`, are not
included.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-06 08:54:42 +00:00
Kim Morrison
ca0d822619 chore: protect Int.sub_eq_iff_eq_add (#7359)
Minor problems introduced in #7274.
2025-03-06 05:42:12 +00:00
Kitamado
e2a80875c9 fix: doc in List.removeAll (#7288)
This PR fixes the doc of `List.removeAll`
2025-03-06 05:25:19 +00:00
Leonardo de Moura
061ebe1dca feat: mod and div in cutsat (#7357)
This PR adds support for `/` and `%` to the cutsat procedure.
2025-03-06 04:15:28 +00:00
Leonardo de Moura
7a8c8a4fb3 fix: markNestedProofs (#7355)
This PR fixes a bug in the `markNestedProofs` preprocessor used in the
`grind` tactic.
2025-03-06 00:51:13 +00:00
Leonardo de Moura
3ff10c6cdd test: cutsat cooper resolution (#7354) 2025-03-06 00:40:38 +00:00
Leonardo de Moura
9ae2ac39c9 feat: avoid cooper case analysis for univariate polynomials (#7351)
This PR ensures cutsat does not have to perform case analysis in the
univariate polynomial case. That it, it can close a goal whenever there
is no solution for a divisibility constraint in an interval. Example of
theorem that is now proved in a single step by cutsat:
```lean
example (x : Int) : 100 ≤ x → x ≤ 10000 → 20000 ∣ 3*x → False := by
  grind
```
2025-03-05 20:37:29 +00:00
Wojciech Rozowski
2c8fb9d3fc fix: strip optional parameters when elaborating the termination hints (#7335)
This PR modifies `elabTerminationByHints` in a way that the type of the
recursive function used for elaboration of the termination measure is
striped of from optional parameters. It prevents introducing
dependencies between the default values for arguments, that can cause
the termination checker to fail.

Closes https://github.com/leanprover/lean4/issues/6351.
2025-03-05 18:15:49 +00:00
Henrik Böving
dc7358b4df feat: upgrade cadical to 2.1.2 (#7347)
This PR upgrades the CaDiCal we ship and use for bv_decide to version
2.1.2. Additionally it enables binary LRAT proofs on windows by default
as https://github.com/arminbiere/cadical/issues/112 has been fixed.

Version 2.1.3 is already available but as the Bitwuzla authors [have
pointed out](https://github.com/bitwuzla/bitwuzla/pull/129) one needs to
be careful when upgrading CaDiCal so we just move to a version [they
confirmed](6e93389d86)
is fine for now.
2025-03-05 17:58:58 +00:00
Sebastian Ullrich
44a518b331 fix: never transfer constants from checked environment into elab branches (#7306)
Otherwise we may lose the environment extension state of the constant
2025-03-05 17:12:27 +00:00
Markus Himmel
68f3fc6d5d feat: finite type conversions (Nat/Int/Fin/BitVec -> UIntX -> *) (#7340)
This PR adds lemmas for iterated conversions between finite types which
start with `Nat`/`Int`/`Fin`/`BitVec` and then go through `UIntX`.
2025-03-05 15:35:36 +00:00
Sebastian Ullrich
72c4630aab feat: use realizeConst for all equation and unfold theorems (#7348)
This PR ensures all equation and unfold theorem generators in core are
compatible with parallelism.
2025-03-05 14:56:50 +00:00
Lean stage0 autoupdater
db0abe89cf chore: update stage0 2025-03-05 13:37:40 +00:00
Marc Huisinga
2b44a4f0d9 fix: inlay hint assertion violation when deleting open file (#7346)
This PR fixes an issue where the language server would run into an inlay
hint assertion violation when deleting a file that is still open in the
language server.
2025-03-05 12:40:21 +00:00
Marc Huisinga
72f4098156 feat: combined auto-implicit inlay hint tooltip (#7344)
This PR combines the auto-implicit inlay hint tooltips into a single
tooltip. This works around an issue in VS Code where VS Code fails to
update hovers for tooltips in adjacent inlay hint parts when moving the
mouse.
2025-03-05 12:23:58 +00:00
Marc Huisinga
f0f7c3ff01 fix: inlay hints inserted at wrong position after edit (#7343)
This PR mitigates an issue where inserting an inlay hint in VS Code by
double-clicking would insert the inlay hint at the wrong position right
after an edit.

This bug was originally reported by @plp127 at
https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/v4.2E18.2E0.20-.20inlay.20hints/near/503362330.

The cause of this bug is that when VS Code hasn't yet received a new set
of inlay hints for a new document state, it will happily move around the
displayed inlay hint, but it won't move around any of the other
position-dependent properties of the inlay hint, like the property
describing where to insert the inlay hint. Since we delay responses
after an edit by an edit delay of 3000ms to prevent inlay hint
flickering while typing, the window for this bug is relatively large.

To work around this bug, we now always immediately respond to the first
inlay hint request after an edit with the old state of the inlay hints,
which we already update correctly on edits on the server-side so that we
can serve old inlay hints for parts of the file that are still
in-progress. Essentially, we are just telling VS Code how it should have
moved all position-dependent properties of each inlay hint.

Even with this mitigation, there is still a small window for this bug to
occur, namely the window from an edit to when VS Code receives the old
inlay hints from the server. In practice, this window should be a couple
of milliseconds at most, so I'd hope it doesn't cause many problems.
There's nothing we can do about this in either vscode-lean4 or the
language server, unfortunately.
2025-03-05 12:23:53 +00:00
Kim Morrison
5536281238 feat: force-mathlib-ci label (#7337)
This PR adds support for a `force-mathlib-ci` label, which attempts full
Mathlib CI even if the PR branch is not based off the
`nightly-with-mathlib` branch, or if the relevant
`nightly-testing-YYYY-MM-DD` branch is not present at Batteries or
Mathlib.
2025-03-05 06:36:38 +00:00
Markus Himmel
8de6233326 feat: IntX conversion lemmas (#7274)
This PR adds lemmas about iterated conversions between finite types,
starting with something of type `IntX`.
2025-03-05 06:27:53 +00:00
Leonardo de Moura
f312170f21 feat: cooper resolution in cutsat (#7339)
This PR implements cooper conflict resolution in the cutsat procedure.
It also fixes several bugs in the proof term construction. We still need
to add more tests, but we can already solve the following example that
`omega` fails to solve:
```lean
example (x y : Int) :
    27 ≤ 11*x + 13*y →
    11*x + 13*y ≤ 45 →
    -10 ≤ 7*x - 9*y →
    7*x - 9*y ≤ 4 → False := by
  grind
```
2025-03-05 03:37:45 +00:00
Kim Morrison
6d1bda6ff2 feat: add @[simp] to Int.neg_inj (#7338)
This PR adds @[simp] to `Int.neg_inj`.
2025-03-05 02:53:41 +00:00
Joachim Breitner
f45c19b428 feat: identify more fixed parameters (#7166)
This PR extends the notion of “fixed parameter” of a recursive function
also to parameters that come after varying function. The main benefit is
that we get nicer induction principles.


Before the definition

```lean
def app (as : List α) (bs : List α) : List α :=
  match as with
  | [] => bs
  | a::as => a :: app as bs
```

produced

```lean
app.induct.{u_1} {α : Type u_1} (motive : List α → List α → Prop) (case1 : ∀ (bs : List α), motive [] bs)
  (case2 : ∀ (bs : List α) (a : α) (as : List α), motive as bs → motive (a :: as) bs) (as bs : List α) : motive as bs
```
and now you get
```lean
app.induct.{u_1} {α : Type u_1} (motive : List α → Prop) (case1 : motive [])
  (case2 : ∀ (a : α) (as : List α), motive as → motive (a :: as)) (as : List α) : motive as
```
because `bs` is fixed throughout the recursion (and can completely be
dropped from the principle).

This is a breaking change when such an induction principle is used
explicitly. Using `fun_induction` makes proof tactics robust against
this change.

The rules for when a parameter is fixed are now:

1. A parameter is fixed if it is reducibly defq to the the corresponding
argument in each recursive call, so we have to look at each such call.
2. With mutual recursion, it is not clear a-priori which arguments of
another function correspond to the parameter. This requires an analysis
with some graph algorithms to determine.
3. A parameter can only be fixed if all parameters occurring in its type
are fixed as well.
This dependency graph on parameters can be different for the different
functions in a recursive group, even leading to cycles.
4. For structural recursion, we kinda want to know the fixed parameters
before investigating which argument to actually recurs on. But once we
have that we may find that we fixed an index of the recursive
parameter’s type, and these cannot be fixed. So we have to un-fix them
5. … and all other fixed parameters that have dependencies on them.

Lean tries to identify the largest set of parameters that satisfies
these criteria.

Note that in a definition like
```lean
def app : List α → List α → List α
  | [], bs => bs
  | a::as, bs => a :: app as bs
```
the `bs` is not considered fixes, as it goes through the matcher
machinery.


Fixes #7027
Fixes #2113
2025-03-04 22:26:20 +00:00
Joachim Breitner
e2ee629022 fix: allow aux decls to be generated by decreasing_by tactics (#7333)
This PR allows aux decls (like generated by `match`) to be generated by
decreasing_by tactics.

Fixes #7332.
2025-03-04 18:42:36 +00:00
Sebastian Ullrich
64731b71aa fix: enable realizations for inductives as late as possible (#7336)
Realizations on them were missing access to e.g. `recOn`
2025-03-04 17:57:51 +00:00
Joachim Breitner
23b5baa5ec feat: WF/Fix.lean: only refine fix’s ih for atomic discriminant onlys (#7324)
This PR changes the internal construction of well-founded recursion, to
not change the type of `fix`’s induction hypothesis in non-defeq ways.

Fixes #7322 and hopefully unblocks #7166.
2025-03-04 13:49:01 +00:00
Sebastian Ullrich
f58e893e63 chore: Mathlib fixes (#7327)
* chore: revert changes to Environment.replay 
* chore: disable realizeConst for now when Elab.async is not set
2025-03-04 13:41:30 +00:00
Rob23oba
a856518265 perf: optimize elaboration of HashMap verification files (#7323)
This PR improves the elaboration time of
`Std.Data.DHashMap.Internal.RawLemmas` and
`Std.Data.DHashMap.RawLemmas`.
2025-03-04 13:30:15 +00:00
Joachim Breitner
45806017e5 feat: allow cond to be used in proofs (#7141)
This PR generalizes `cond` to allow the motive to be in `Sort u`, not
just `Type u`.
2025-03-04 12:10:29 +00:00
Paul Reichert
058e63a3d6 feat: tree map lemmas for foldlM, foldl, foldrM and foldr (#7270)
This PR provides lemmas about the tree map functions `foldlM`, `foldl`,
`foldrM` and `foldr` and their interactions with other functions for
which lemmas already exist. Additionally, it generalizes the
`fold*`/`keys` lemmas to arbitrary tree maps, which were previously
stated only for the `DTreeMap α Unit` case.

A later PR will make the hash map functions `fold` and `revFold`
internal and also update their signature to conform to the tree map and
list API. This is out of scope for this PR.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-03-04 11:44:41 +00:00
Kim Morrison
e8e6c4716f chore: copy v4.17.0 release notes from releases/v4.17.0 branch (#7325) 2025-03-04 11:24:51 +00:00
Lean stage0 autoupdater
3ce8c73315 chore: update stage0 2025-03-04 11:40:02 +00:00
Kim Morrison
88edd13642 feat: alignment of Int.ediv/fdiv/tdiv lemmas (#7319)
This PR continues alignment of lemmas about `Int.ediv/fdiv/tdiv`,
including adding notes about "missing" lemmas that do not apply in one
case. Also lemmas about `emod/fmod/tmod`. There's still more to do.
2025-03-04 10:41:01 +00:00
Sebastian Ullrich
c70e614a5b chore: harden use of panics in Lean.Environment (#7321)
* avoid `panic!`s that return `Unit` or some otherwise unused value lest
they get optimized away
* make some fallback values explicit to avoid follow-up errors
* avoid redundant declaration names in panic messages
2025-03-04 10:29:54 +00:00
Joachim Breitner
aa8faae576 feat: allow cond to be used in proofs (stage0 update prep) (#7320)
This PR prepares for #7141.
2025-03-04 10:26:12 +00:00
euprunin
2f8901d6d0 chore: add missing period to grind warning message (#7317)
Co-authored-by: euprunin <euprunin@users.noreply.github.com>
2025-03-04 09:42:17 +00:00
Leonardo de Moura
9ff8c5ac2d feat: cooper conflict resolution in cutsat (#7315)
This PR implements the Cooper conflict resolution in cutsat. We still
need to implement the backtracking and disequality case.
2025-03-04 03:23:14 +00:00
Kyle Miller
48491e5262 chore: re-enable synthesis checkpoint for structure parent elaboration (#7314)
This PR changes elaboration of `structure` parents so that each must be
fully elaborated before the next one is processed.

In particular, it re-adds synthesizing synthetic mvars between
`structure` parents, in the same manner as other fields. This synthesis
step was removed in #5842 because I had thought parents were like type
parameters and would participate in header elaboration, but in the end
it made more sense elaborating parents after the headers are done, since
they're like fields.

We want this enabled because it will help ensure that all the necessary
reductions are done to types of fields as they're added to the
structure.
2025-03-04 02:49:30 +00:00
Leonardo de Moura
9f5cc7262b feat: proof generation for cooper_dvd_left and variants in cutsat (#7312)
This PR implements proof term generation for `cooper_dvd_left` and its
variants in the cutsat procedure for linear integer arithmetic.
2025-03-04 00:40:31 +00:00
Kim Morrison
957beb02bc chore: deprecate Environment.replay; use lean4checker (#7311) 2025-03-04 00:23:36 +00:00
Henrik Böving
017a1f2b94 fix: bv_decide structures pass instantiate mvars (#7309)
This PR fixes a bug where bv_decide's new structure support would
sometimes not case split on all available structure fvars as their type
was an mvar.
2025-03-03 21:27:53 +00:00
Lean stage0 autoupdater
f8f1b2212a chore: update stage0 2025-03-03 20:17:14 +00:00
Sebastian Ullrich
dab6a161bd feat: realizeConst for match equations (#7247)
This PR makes generation of `match` equations and splitters compatible
with parallelism.
2025-03-03 17:18:29 +00:00
Sebastian Ullrich
8e47d29bf9 feat: debug_assert! (#7256)
This PR introduces the `assert!` variant `debug_assert!` that is
activated when compiled with `buildType` `debug`.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2025-03-03 16:34:44 +00:00
jrr6
e337129108 fix: move auxDeclToFullName to LocalContext to fix name (un)resolution (#7075)
This PR ensures that names suggested by tactics like `simp?` are not
shadowed by auxiliary declarations in the local context and that names
of `let rec` and `where` declarations are correctly resolved in tactic
blocks.

This PR contains the following potentially breaking changes:
* Moves the `auxDeclToFullName` map from `TermElab.Context` to
`LocalContext`.
* Refactors `Lean.Elab.Term.resolveLocalName : Name → TermElabM …` to
`Lean.resolveLocalName [MonadResolveName m] [MonadEnv m] [MonadLCtx m] :
Name → m …`.
* Refactors the `TermElabM` action `Lean.Elab.Term.withAuxDecl` to a
monad-polymorphic action `Lean.Meta.withAuxDecl`.
* Adds an optional `filter` argument to `Lean.unresolveNameGlobal`.

Closes #6706, closes #7073.
2025-03-03 16:10:54 +00:00
Rob23oba
d3eb2fe13c feat: HashMap getKey lemmas (#7289)
This PR adds `getKey_beq`, `getKey_congr` and variants to the hashmap
api.
2025-03-03 15:06:58 +00:00
Markus Himmel
d2239a5770 feat: IntX simprocs (#7228)
This PR adds simprocs to reduce expressions involving `IntX`.
2025-03-03 13:37:57 +00:00
Sebastian Ullrich
a244b06882 feat: use realizeConst for bv_decide helper constants (#7276)
This PR ensures helper constants generated by `bv_decide` are compatible
with parallelism.
2025-03-03 12:36:25 +00:00
Sebastian Ullrich
0a55f4bf36 fix: more realizeConst fixes (#7300)
Found and debugged while working on stage 2 of #7247
2025-03-03 12:10:40 +00:00
Kim Morrison
e7a411a66d chore: begin development cycle for v4.19.0 (#7299) 2025-03-03 11:01:21 +00:00
Henrik Böving
783671261d feat: bv_decide add rewrites around ite + operations (#7298)
This PR adds rewrites to bv_decide's preprocessing that concern
combinations of if-then-else and operation such as multiplication or
negation.
2025-03-03 10:51:19 +00:00
Sebastian Ullrich
01d951c3fc fix: cancel computations within command elaboration as soon as reuse is ruled out (#7241)
The other part of #7175
2025-03-03 10:37:10 +00:00
Eric Wieser
6cf3402f1c perf: use free_sized in mpz.cpp (#6825)
The performance win here is pretty negligible (and of course irrelevant
with the small allocator enabled), but this is consistent with it being
used elsewhere.

Follow-up to #6598
2025-03-03 08:47:15 +00:00
Kyle Miller
e3c6909ad5 chore: reimplement mk_projections in Lean (#7295)
This PR translates `lean::mk_projections` into Lean, adding
`Lean.Meta.mkProjections`. It also puts `hasLooseBVarInExplicitDomain`
back in sync with the kernel version. Deletes
`src/library/constructions/projection.{h,cpp}`.
2025-03-03 01:10:27 +00:00
Sean McLaughlin
255810db64 fix: Float32.ofInt (#7277)
This PR fixes a bug in Float32.ofInt, which previously returned a
Float(64).

Closes https://github.com/leanprover/lean4/issues/7264
2025-03-02 23:22:35 +00:00
Leonardo de Moura
f094652481 fix: Rat.floor and Rat.ceil (#7294)
This PR fixes bugs in `Std.Internal.Rat.floor` and
`Std.Internal.Rat.ceil`.
2025-03-02 22:50:36 +00:00
Leonardo de Moura
3eb07cac44 feat: cooper_right helper theorem for cutsat (#7293)
This PR adds support theorems for the Cooper-Right conflict resolution
rule used in the cutsat procedure. During model construction, when
attempting to extend the model to a variable x, cutsat may find a
conflict that involves two inequalities (the lower and upper bounds for
x). This is a special case of Cooper-Dvd-Right when there is no
divisibility constraint.
2025-03-02 19:21:08 +00:00
Mac Malone
58034bf237 feat: lake: display newest job in monitor (#7291)
This PR changes the Lake job monitor to display the last (i.e., newest)
running/unfinished job rather than the first. This avoids the monitor
focusing too long on any one job (e.g., "Running job computation").
2025-03-02 18:38:23 +00:00
Leonardo de Moura
7ba7ea4e16 feat: helper theorems for cooper_dvd_right (#7292)
This PR adds support theorems for the **Cooper-Dvd-Right** conflict
resolution rule used in the cutsat procedure. During model construction,
when attempting to extend the model to a variable `x`, cutsat may find a
conflict that involves two inequalities (the lower and upper bounds for
`x`) and a divisibility constraint.
2025-03-02 18:09:55 +00:00
Leonardo de Moura
4877e84031 feat: cooper_left helper theorem for cutsat (#7290)
This PR adds support theorems for the **Cooper-Left** conflict
resolution rule used in the cutsat procedure. During model
construction,when attempting to extend the model to a variable `x`,
cutsat may find a conflict that involves two inequalities (the lower and
upper bounds for `x`). This is a special case of Cooper-Dvd-Left when
there is no divisibility constraint.
2025-03-02 16:34:48 +00:00
Henrik Böving
9c47f395c8 refactor: change iff lowering rule in bv_decide (#7287)
This PR uses a better lowering rule for iff in bv_decide's
preprocessing.
2025-03-02 12:20:27 +00:00
Kim Morrison
3f98b4835c chore: add Fin.mk_eq_zero simp lemma (#7286) 2025-03-02 11:11:23 +00:00
Leonardo de Moura
a86145b6bb feat: non-chronological backtracking for cutsat (#7284)
This PR implements non-choronological backtracking for the cutsat
procedure. The procedure has two main kinds of case-splits:
disequalities and Cooper resolvents. This PR focus on the first kind.
2025-03-01 23:19:11 +00:00
dependabot[bot]
c4d3a74f32 chore: CI: bump dawidd6/action-download-artifact from 8 to 9 (#7285)
Bumps
[dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact)
from 8 to 9.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dawidd6/action-download-artifact/releases">dawidd6/action-download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v9</h2>
<h2>What's Changed</h2>
<ul>
<li>add merge_multiple option by <a
href="https://github.com/timostroehlein"><code>@​timostroehlein</code></a>
in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/327">dawidd6/action-download-artifact#327</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/timostroehlein"><code>@​timostroehlein</code></a>
made their first contribution in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/327">dawidd6/action-download-artifact#327</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dawidd6/action-download-artifact/compare/v8...v9">https://github.com/dawidd6/action-download-artifact/compare/v8...v9</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="07ab29fd4a"><code>07ab29f</code></a>
add merge_multiple option (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/327">#327</a>)</li>
<li>See full diff in <a
href="https://github.com/dawidd6/action-download-artifact/compare/v8...v9">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dawidd6/action-download-artifact&package-manager=github_actions&previous-version=8&new-version=9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-01 20:47:28 +00:00
Leonardo de Moura
c74865fbe2 feat: helper theorems for cooper_dvd_left (#7279)
This PR adds support theorems for the **Cooper-Dvd-Left** conflict
resolution rule used in the cutsat procedure. During model construction,
when attempting to extend the model to a variable `x`, cutsat may find a
conflict that involves two inequalities (the lower and upper bounds for
`x`) and a divisibility constraint:

```lean
a * x + p ≤ 0
b * x + q ≤ 0
d ∣ c * x + s
```

We apply Cooper's quantifier elimination to produce:

```lean
OrOver (Int.lcm a (a * d / Int.gcd(a * d) c)) fun k =>
     b * p + (-a) * q + b * k ≤ 0 ∧
     a ∣ p + k ∧
     a * d ∣ c * p + (-a) * s + c * k
```

Here, `OrOver` is a "big-or" operator. This PR introduces the following
theorem, which encapsulates the above approach via reflection:

```lean
theorem cooper_dvd_left (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
    : cooper_dvd_left_cert p₁ p₂ p₃ d n
      → p₁.denote' ctx ≤ 0
      → p₂.denote' ctx ≤ 0
      → d ∣ p₃.denote' ctx
      → OrOver n (cooper_dvd_left_split ctx p₁ p₂ p₃ d) :=
```

For each `0 <= k < n`, we generate the three implied facts using:

```lean
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
    : cooper_dvd_left_split ctx p₁ p₂ p₃ d k
      → cooper_dvd_left_split_ineq_cert p₁ p₂ k b p'
      → p'.denote ctx ≤ 0

theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
    : cooper_dvd_left_split ctx p₁ p₂ p₃ d k
      → cooper_dvd_left_split_dvd1_cert p₁ p' a k
      → a ∣ p'.denote ctx

theorem cooper_dvd_left_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
    : cooper_dvd_left_split ctx p₁ p₂ p₃ d k
      → cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p'
      → d' ∣ p'.denote ctx
```

Two helper `OrOver` theorems are used to process the `OrOver`:

```lean
theorem orOver_unsat {p} : ¬ OrOver 0 p

theorem orOver_resolve {n p} : OrOver (n+1) p → ¬ p n → OrOver n p
```

Where `p` is instantiated using `cooper_dvd_left_split ctx p₁ p₂ p₃ d`.
2025-03-01 02:18:12 +00:00
Leonardo de Moura
93a908469c feat: cutsat counterexamples (#7278)
This PR adds counterexamples for linear integer constraints in the
`grind` tactic. This feature is implemented in the cutsat procedure.
2025-02-28 19:05:27 +00:00
Joachim Breitner
903fe29863 chore: release_notes.py: report on all commit types (#7258)
I missed a few that we should not be shy of.
2025-02-28 17:39:18 +00:00
Henrik Böving
84da113355 feat: add all bitwuzla level 1 if rewrites to bv_decide (#7275)
This PR adds all level 1 rewrites from Bitwuzla to the preprocessor of
bv_decide.
2025-02-28 16:04:09 +00:00
Markus Himmel
75df4c0b52 fix: statement of a UIntX conversion lemma (#7273)
This PR fixes the statement of a `UIntX` conversion lemma.
2025-02-28 15:15:58 +00:00
Sebastian Ullrich
ad5a746cdd fix: realizeConst fixes (#7272)
Emerged and fixed while adding more `realizeConst` callers
2025-02-28 14:59:13 +00:00
Paul Reichert
2bd3ce5463 fix: harmonize foldr signature of the tree map with that of List (#7271)
This PR changes the order of arguments of the folding function expected
by the tree map's `foldr` and `foldrM` functions so that they are
consistent with the API of `List`.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-28 14:36:56 +00:00
Henrik Böving
2b752ec245 feat: add IntX and ISize support for bv_decide (#7269)
This PR implements support for `IntX` and `ISize` in `bv_decide`.
2025-02-28 10:33:11 +00:00
Paul Reichert
909ee719aa feat: tree map lemmas for keys and toList (#7260)
This PR provides lemmas about the tree map functions `keys` and `toList`
and their interactions with other functions for which lemmas already
exist. Moreover, a bug in `foldr` (calling `foldlM` instead of `foldrM`)
is fixed.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-28 10:14:13 +00:00
Markus Himmel
7dd5e957da feat: ToExpr IntX (#7268)
This PR implements `Lean.ToExpr` for finite signed integers.
2025-02-28 09:32:30 +00:00
Markus Himmel
d67e0eea47 feat: IntX theory for simprocs and bv_decide (#7259)
This PR contains theorems about `IntX` that are required for `bv_decide`
and the `IntX` simprocs.

A more comprehensive set of theorems about `IntX` will be part of future
PRs.
2025-02-28 07:04:52 +00:00
Kim Morrison
10bfeba2d9 chore: aligning Int.ediv/fdiv/tdiv theorems (#7266)
This PR begins the alignment of `Int.ediv/fdiv/tdiv` theorems.
2025-02-28 05:27:40 +00:00
Leonardo de Moura
4285f8ba05 feat: improve cutsat model search procedure (#7267)
This PR improves the cutsat search procedure. It adds support for find
an approximate rational solution, checks disequalities, and adds stubs
for all missing cases.
2025-02-28 04:26:53 +00:00
Leonardo de Moura
d8be3ef7a8 doc: cutsat procedure (#7262) 2025-02-27 21:15:34 +00:00
Paul Reichert
c924768879 fix: add @[specialize] annotations to helpers used in alter and modify of the hash map (#7245)
This PR adds missing `@[specialize]` annotations to the `alter` and
`modify` functions in `Std.Data.DHashMap.Internal.AssocList`, which are
used by the corresponding hash map functions.

Zulip thread:
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/.60Std.2EHashMap.2Emodify.60.20and.20.60alter.60.20do.20not.20inline.20the.20function

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-27 15:43:05 +00:00
Henrik Böving
c1e76e8976 perf: optimize LRAT trimming in bv_decide (#7257)
This PR improves performance of LRAT trimming in bv_decide.

The underlying idea is taken from LRAT trimming as implemented in
[`lrat-trim`](https://github.com/arminbiere/lrat-trim/t): As we only
filter about half to two thirds of the LRAT proof steps anyway, there is
no need to use tree or hash maps to store information about them and we
can instead use arrays indexed by the proof step directly. This does not
meaningfully increase the amount of memory required but makes the
trimming step basically disappear from profiles, e.g.
`smt/non-incremental/QF_BV/20210312-Bouvier/vlsat3_a72.smt2` [used
to](https://share.firefox.dev/41kJTle) have 8% of its time spent in
trimming [now](https://share.firefox.dev/3QAKI4w) 1.5%.
2025-02-27 13:47:21 +00:00
Paul Reichert
60a9f8e492 feat: well-formedness lemmas for raw tree map operations (#7237)
This PR provides proofs that the raw tree map operations are well-formed
and refactors the file structure of the tree map, introducing new
modules `Std.{DTreeMap,TreeMap,TreeSet}.Raw` and splittting
`AdditionalOperations` into separate files for bundled and raw types.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-27 13:08:41 +00:00
Kim Morrison
604133d189 chore: cleanup of remaining Array-specific material (#7253)
This PR takes Array-specific lemmas at the end of `Array/Lemmas.lean`
(i.e. material that does not have exact correspondences with
`List/Lemmas.lean`) and moves them to more appropriate homes. More to
come.
2025-02-27 10:51:30 +00:00
Kim Morrison
d3781bb787 fix: definition of Min (Option α), and basic lemmas (#7255)
This PR fixes the definition of `Min (Option α)`. This is a breaking
change. This treats `none` as the least element,
so `min none x = min x none = none` for all `x : Option α`. Prior to
nightly-2025-02-27, we instead had `min none (some x) = min (some x)
none = some x`. Also adds basic lemmas relating `min`, `max`, `≤` and
`<` on `Option`.
2025-02-27 10:44:44 +00:00
Sebastian Ullrich
87e8da5230 chore: temporarily disable Elab.async in the server (#7254)
...pending further testing of #7241 post-release
2025-02-27 08:31:54 +00:00
Kim Morrison
727c696d9f chore: add @[simp] to List.getElem_append_left|right (#7216)
Helps with confluence.
2025-02-27 03:01:33 +00:00
Mac Malone
cf2b7f4c1b feat: lake: builtin inits, elabs, & macros for DSL (#7171)
This PR changes the Lake DSL to use builtin elaborators, macros, and
initializers.

This works out of the box for the Lake executable and is supported in
interactive contexts through the Lake plugin.
2025-02-27 02:34:14 +00:00
Leonardo de Moura
cd4383b6f3 feat: refine inequalites using disequalities in cutsat (#7252)
This PR implements inequality refinement using disequalities. It
minimizes the number of case splits cutsat will have to perform.
2025-02-27 01:33:58 +00:00
Cameron Zwarich
0d9859370a fix: make extern decls evaluate as ⊤ instead of ⊥ in LCNF.elimDeadBranches (#6928)
This PR makes extern decls evaluate as ⊤ rather than the default value
of ⊥ in the LCNF elimDeadBranches analysis.
2025-02-27 01:24:47 +00:00
Cameron Zwarich
c292ae2e0e fix: don't create reduced arity LCNF decls with no params (#7086)
This PR makes the arity reduction pass in the new code generator match
the old one when it comes to the behavior of decls with no used
parameters. This is important, because otherwise we might create a
top-level decl with no params that contains unreachable code, which
would get evaluated unconditionally during initialization. This actually
happens when initializing Init.Core built with the new code generator.
2025-02-27 01:23:34 +00:00
Kim Morrison
3113847806 chore: reenable Vector variable name linters (#7251) 2025-02-26 23:59:28 +00:00
Kim Morrison
d275455674 chore: alignment of a List/Array/Vector.reverse lemma (#7250)
Minor lemma alignment missed earlier.
2025-02-26 23:59:06 +00:00
Kim Morrison
a4d10742d3 feat: align List/Array/Vector.any/all theorems (#7249)
This PR completes alignment of theorems about
`List/Array/Vector.any/all`.
2025-02-26 23:53:53 +00:00
Leonardo de Moura
777fba495a feat: cutsat implied equalities (#7248)
This PR implements simple equality propagation in cutsat `p <= 0 -> -p
<= 0 -> p = 0`
2025-02-26 22:52:37 +00:00
1192 changed files with 39432 additions and 10038 deletions

20
.github/workflows/awaiting-mathlib.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Check awaiting-mathlib label
on:
merge_group:
pull_request:
types: [opened, labeled]
jobs:
check-awaiting-mathlib:
runs-on: ubuntu-latest
steps:
- name: Check awaiting-mathlib label
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
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');
}

View File

@@ -204,7 +204,8 @@ jobs:
"os": "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"check-level": 0,
// 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*",
@@ -260,8 +261,21 @@ jobs:
// "CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_tempfile.lean\\.|leanruntest_libuv\\.lean\""
// }
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
return matrix.filter((job) => level >= job["check-level"])
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);
build:
needs: [configure]

View File

@@ -34,7 +34,7 @@ jobs:
- name: Download artifact from the previous workflow.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: download-artifact
uses: dawidd6/action-download-artifact@v8 # https://github.com/marketplace/actions/download-workflow-artifact
uses: dawidd6/action-download-artifact@v9 # https://github.com/marketplace/actions/download-workflow-artifact
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts
@@ -155,6 +155,20 @@ jobs:
fi
if [[ -n "$MESSAGE" ]]; then
# Check if force-mathlib-ci label is present
LABELS="$(curl --retry 3 --location --silent \
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
| jq -r '.[].name')"
if echo "$LABELS" | grep -q "^force-mathlib-ci$"; then
echo "force-mathlib-ci label detected, forcing CI despite issues"
MESSAGE="Forcing Mathlib CI because the \`force-mathlib-ci\` label is present, despite problem: $MESSAGE"
FORCE_CI=true
else
MESSAGE="$MESSAGE You can force Mathlib CI using the \`force-mathlib-ci\` label."
fi
echo "Checking existing messages"
@@ -201,7 +215,12 @@ jobs:
else
echo "The message already exists in the comment body."
fi
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
if [[ "$FORCE_CI" == "true" ]]; then
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
else
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
fi
else
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
fi
@@ -252,7 +271,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Batteries. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi
@@ -316,7 +335,7 @@ jobs:
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi

View File

@@ -47,10 +47,11 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif()
string(APPEND CADICAL_CXXFLAGS " -DNCLOSEFROM")
ExternalProject_add(cadical
PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-1.9.5
GIT_TAG rel-2.1.2
CONFIGURE_COMMAND ""
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}

8
flake.lock generated
View File

@@ -36,17 +36,17 @@
},
"nixpkgs-cadical": {
"locked": {
"lastModified": 1722221733,
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
"lastModified": 1740791350,
"narHash": "sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
}
},

View File

@@ -8,8 +8,8 @@
# old nixpkgs used for portable release with older glibc (2.26)
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
inputs.nixpkgs-older.flake = false;
# for cadical 1.9.5; sync with CMakeLists.txt
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
# for cadical 2.1.2; sync with CMakeLists.txt by taking commit from https://www.nixhub.io/packages/cadical
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:

1110
releases/v4.17.0.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -65,20 +65,21 @@ def format_markdown_description(pr_number, description):
link = f"[#{pr_number}](https://github.com/leanprover/lean4/pull/{pr_number})"
return f"{link} {description}"
def commit_types():
# see doc/dev/commit_convention.md
return ['feat', 'fix', 'doc', 'style', 'refactor', 'test', 'chore', 'perf']
def count_commit_types(commits):
counts = {
'total': len(commits),
'feat': 0,
'fix': 0,
'refactor': 0,
'doc': 0,
'chore': 0
}
for commit_type in commit_types():
counts[commit_type] = 0
for _, first_line, _ in commits:
for commit_type in ['feat:', 'fix:', 'refactor:', 'doc:', 'chore:']:
if first_line.startswith(commit_type):
counts[commit_type.rstrip(':')] += 1
for commit_type in commit_types():
if first_line.startswith(f'{commit_type}:'):
counts[commit_type] += 1
break
return counts
@@ -158,8 +159,9 @@ def main():
counts = count_commit_types(commits)
print(f"For this release, {counts['total']} changes landed. "
f"In addition to the {counts['feat']} feature additions and {counts['fix']} fixes listed below "
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements "
f"and {counts['chore']} chores.\n")
f"there were {counts['refactor']} refactoring changes, {counts['doc']} documentation improvements, "
f"{counts['perf']} performance improvements, {counts['test']} improvements to the test suite "
f"and {counts['style'] + counts['chore']} other changes.\n")
section_order = sort_sections_order()
sorted_changelog = sorted(changelog.items(), key=lambda item: section_order.index(format_section_title(item[0])) if format_section_title(item[0]) in section_order else len(section_order))
@@ -168,7 +170,12 @@ def main():
section_title = format_section_title(label) if label != "Uncategorised" else "Uncategorised"
print(f"## {section_title}\n")
for _, entry in sorted(entries, key=lambda x: x[0]):
print(f"* {entry}\n")
# Split entry into lines and indent all lines after the first
lines = entry.splitlines()
print(f"* {lines[0]}")
for line in lines[1:]:
print(f" {line}")
print() # Empty line after each entry
if __name__ == "__main__":
main()

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 18)
set(LEAN_VERSION_MINOR 19)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")

View File

@@ -40,3 +40,4 @@ import Init.Syntax
import Init.Internal
import Init.Try
import Init.BinderNameHint
import Init.Task

View File

@@ -51,14 +51,28 @@ def ForInStep.value (x : ForInStep α) : α :=
@[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl
@[simp] theorem ForInStep.value_yield (b : β) : (ForInStep.yield b).value = b := rfl
/--
Maps a function over a functor, with parameters swapped so that the function comes last.
This function is `Functor.map` with the parameters reversed, typically used via the `<&>` operator.
-/
@[reducible]
def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β :=
fun a f => f <$> a
@[inherit_doc Functor.mapRev]
infixr:100 " <&> " => Functor.mapRev
recommended_spelling "mapRev" for "<&>" in [Functor.mapRev, «term_<&>_»]
/--
Discards the value in a functor, retaining the functor's structure.
Discarding values is especially useful when using `Applicative` functors or `Monad`s to implement
effects, and some operation should be carried out only for its effects. In `do`-notation, statements
whose values are discarded must return `Unit`, and `discard` can be used to explicitly discard their
values.
-/
@[always_inline, inline]
def Functor.discard {f : Type u Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
Functor.mapConst PUnit.unit x

View File

@@ -13,10 +13,20 @@ import Init.Coe
namespace Except
variable {ε : Type u}
/--
A successful computation in the `Except ε` monad: `a` is returned, and no exception is thrown.
-/
@[always_inline, inline]
protected def pure (a : α) : Except ε α :=
Except.ok a
/--
Transforms a successful result with a function, doing nothing when an exception is thrown.
Examples:
* `(pure 2 : Except String Nat).map toString = pure 2`
* `(throw "Error" : Except String Nat).map toString = throw "Error"`
-/
@[always_inline, inline]
protected def map (f : α β) : Except ε α Except ε β
| Except.error err => Except.error err
@@ -27,36 +37,78 @@ protected def map (f : α → β) : Except ε α → Except ε β
intro e
simp [Except.map]; cases e <;> rfl
/--
Transforms exceptions with a function, doing nothing on successful results.
Examples:
* `(pure 2 : Except String Nat).mapError (·.length) = pure 2`
* `(throw "Error" : Except String Nat).mapError (·.length) = throw 5`
-/
@[always_inline, inline]
protected def mapError (f : ε ε') : Except ε α Except ε' α
| Except.error err => Except.error <| f err
| Except.ok v => Except.ok v
/--
Sequences two operations that may throw exceptions, allowing the second to depend on the value
returned by the first.
If the first operation throws an exception, then it is the result of the computation. If the first
succeeds but the second throws an exception, then that exception is the result. If both succeed,
then the result is the result of the second computation.
This is the implementation of the `>>=` operator for `Except ε`.
-/
@[always_inline, inline]
protected def bind (ma : Except ε α) (f : α Except ε β) : Except ε β :=
match ma with
| Except.error err => Except.error err
| Except.ok v => f v
/-- Returns true if the value is `Except.ok`, false otherwise. -/
/-- Returns `true` if the value is `Except.ok`, `false` otherwise. -/
@[always_inline, inline]
protected def toBool : Except ε α Bool
| Except.ok _ => true
| Except.error _ => false
@[inherit_doc Except.toBool]
abbrev isOk : Except ε α Bool := Except.toBool
/--
Returns `none` if an exception was thrown, or `some` around the value on success.
Examples:
* `(pure 10 : Except String Nat).toOption = some 10`
* `(throw "Failure" : Except String Nat).toOption = none`
-/
@[always_inline, inline]
protected def toOption : Except ε α Option α
| Except.ok a => some a
| Except.error _ => none
/--
Handles exceptions thrown in the `Except ε` monad.
If `ma` is successful, its result is returned. If it throws an exception, then `handle` is invoked
on the exception's value.
Examples:
* `(pure 2 : Except String Nat).tryCatch (pure ·.length) = pure 2`
* `(throw "Error" : Except String Nat).tryCatch (pure ·.length) = pure 5`
* `(throw "Error" : Except String Nat).tryCatch (fun x => throw ("E: " ++ x)) = throw "E: Error"`
-/
@[always_inline, inline]
protected def tryCatch (ma : Except ε α) (handle : ε Except ε α) : Except ε α :=
match ma with
| Except.ok a => Except.ok a
| Except.error e => handle e
/--
Recovers from exceptions thrown in the `Except ε` monad. Typically used via the `<|>` operator.
`Except.tryCatch` is a related operator that allows the recovery procedure to depend on _which_
exception was thrown.
-/
def orElseLazy (x : Except ε α) (y : Unit Except ε α) : Except ε α :=
match x with
| Except.ok a => Except.ok a
@@ -70,12 +122,26 @@ instance : Monad (Except ε) where
end Except
/--
Adds exceptions of type `ε` to a monad `m`.
-/
def ExceptT (ε : Type u) (m : Type u Type v) (α : Type u) : Type v :=
m (Except ε α)
/--
Use a monadic action that may return an exception's value as an action in the transformed monad that
may throw the corresponding exception.
This is the inverse of `ExceptT.run`.
-/
@[always_inline, inline]
def ExceptT.mk {ε : Type u} {m : Type u Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
/--
Use a monadic action that may throw an exception as an action that may return an exception's value.
This is the inverse of `ExceptT.mk`.
-/
@[always_inline, inline]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
@@ -83,25 +149,41 @@ namespace ExceptT
variable {ε : Type u} {m : Type u Type v} [Monad m]
/--
Returns the value `a` without throwing exceptions or having any other effect.
-/
@[always_inline, inline]
protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
ExceptT.mk <| pure (Except.ok a)
/--
Handles exceptions thrown by an action that can have no effects _other_ than throwing exceptions.
-/
@[always_inline, inline]
protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β)
| Except.ok a => f a
| Except.error e => pure (Except.error e)
/--
Sequences two actions that may throw exceptions. Typically used via `do`-notation or the `>>=`
operator.
-/
@[always_inline, inline]
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α ExceptT ε m β) : ExceptT ε m β :=
ExceptT.mk <| ma >>= ExceptT.bindCont f
/--
Transforms a successful computation's value using `f`. Typically used via the `<$>` operator.
-/
@[always_inline, inline]
protected def map {α β : Type u} (f : α β) (x : ExceptT ε m α) : ExceptT ε m β :=
ExceptT.mk <| x >>= fun a => match a with
| (Except.ok a) => pure <| Except.ok (f a)
| (Except.error e) => pure <| Except.error e
/--
Runs a computation from an underlying monad in the transformed monad with exceptions.
-/
@[always_inline, inline]
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
ExceptT.mk <| Except.ok <$> t
@@ -110,6 +192,9 @@ protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
instance : MonadLift (Except ε) (ExceptT ε m) := fun e => ExceptT.mk <| pure e
instance : MonadLift m (ExceptT ε m) := ExceptT.lift
/--
Handles exceptions produced in the `ExceptT ε` transformer.
-/
@[always_inline, inline]
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α :=
ExceptT.mk <| ma >>= fun res => match res with
@@ -124,6 +209,11 @@ instance : Monad (ExceptT ε m) where
bind := ExceptT.bind
map := ExceptT.map
/--
Transforms exceptions using the function `f`.
This is the `ExceptT` version of `Except.mapError`.
-/
@[always_inline, inline]
protected def adapt {ε' α : Type u} (f : ε ε') : ExceptT ε m α ExceptT ε' m α := fun x =>
ExceptT.mk <| Except.mapError f <$> x
@@ -150,8 +240,12 @@ instance (ε) : MonadExceptOf ε (Except ε) where
namespace MonadExcept
variable {ε : Type u} {m : Type v Type w}
/-- Alternative orelse operator that allows to select which exception should be used.
The default is to use the first exception since the standard `orelse` uses the second. -/
/--
An alternative unconditional error recovery operator that allows callers to specify which exception
to throw in cases where both operations throw exceptions.
By default, the first is thrown, because the `<|>` operator throws the second.
-/
@[always_inline, inline]
def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α :=
tryCatch t₁ fun e₁ => tryCatch t₂ fun e₂ => throw (if useFirstEx then e₁ else e₂)

View File

@@ -10,19 +10,37 @@ import Init.Control.Lawful.Basic
The Exception monad transformer using CPS style.
-/
/--
Adds exceptions of type `ε` to a monad `m`.
Instead of using `Except ε` to model exceptions, this implementation uses continuation passing
style. This has different performance characteristics from `ExceptT ε`.
-/
def ExceptCpsT (ε : Type u) (m : Type u Type v) (α : Type u) := (β : Type u) (α m β) (ε m β) m β
namespace ExceptCpsT
/--
Use a monadic action that may throw an exception as an action that may return an exception's value.
-/
@[always_inline, inline]
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
set_option linter.unusedVariables false in -- `s` unused
/--
Use a monadic action that may throw an exception by providing explicit success and failure
continuations.
-/
@[always_inline, inline]
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α m β) (error : ε m β) : m β :=
x _ ok error
/--
Returns the value of a computation, forgetting whether it was an exception or a success.
This corresponds to early return.
-/
@[always_inline, inline]
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
x α pure pure
@@ -40,6 +58,9 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
throw e := fun _ _ k => k e
tryCatch x handle := fun _ k₁ k₂ => x _ k₁ (fun e => handle e _ k₁ k₂)
/--
Run an action from the transformed monad in the exception monad.
-/
@[always_inline, inline]
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
fun _ k _ => x >>= k

View File

@@ -10,6 +10,28 @@ import Init.Core
universe u
/--
The identity function on types, used primarily for its `Monad` instance.
The identity monad is useful together with monad transformers to construct monads for particular
purposes. Additionally, it can be used with `do`-notation in order to use control structures such as
local mutability, `for`-loops, and early returns in code that does not otherwise use monads.
Examples:
```lean example
def containsFive (xs : List Nat) : Bool := Id.run do
for x in xs do
if x == 5 then return true
return false
```
```lean example
#eval containsFive [1, 3, 5, 7]
```
```output
true
```
-/
def Id (type : Type u) : Type u := type
namespace Id
@@ -20,9 +42,18 @@ instance : Monad Id where
bind x f := f x
map f x := f x
/--
The identity monad has a `bind` operator.
-/
def hasBind : Bind Id :=
inferInstance
/--
Runs a computation in the identity monad.
This function is the identity function. Because its parameter has type `Id α`, it causes
`do`-notation in its arguments to use the `Monad Id` instance.
-/
@[always_inline, inline]
protected def run (x : Id α) : α := x

View File

@@ -13,17 +13,26 @@ open Function
rfl
/--
The `Functor` typeclass only contains the operations of a functor.
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
including the preservation of the identity and composition laws:
```
id <$> x = x
(h ∘ g) <$> x = h <$> g <$> x
```
A functor satisfies the functor laws.
The `Functor` class contains the operations of a functor, but does not require that instances
prove they satisfy the laws of a functor. A `LawfulFunctor` instance includes proofs that the laws
are satisfied. Because `Functor` instances may provide optimized implementations of `mapConst`,
`LawfulFunctor` instances must also prove that the optimized implementation is equivalent to the
standard implementation.
-/
class LawfulFunctor (f : Type u Type v) [Functor f] : Prop where
/--
The `mapConst` implementation is equivalent to the default implementation.
-/
map_const : (Functor.mapConst : α f β f α) = Functor.map const β
/--
The `map` implementation preserves identity.
-/
id_map (x : f α) : id <$> x = x
/--
The `map` implementation preserves function composition.
-/
comp_map (g : α β) (h : β γ) (x : f α) : (h g) <$> x = h <$> g <$> x
export LawfulFunctor (map_const id_map comp_map)
@@ -38,21 +47,48 @@ attribute [simp] id_map
(comp_map _ _ _).symm
/--
The `Applicative` typeclass only contains the operations of an applicative functor.
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
```
pure id <*> v = v
pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (f x)
u <*> pure y = pure (· y) <*> u
```
An applicative functor satisfies the laws of an applicative functor.
The `Applicative` class contains the operations of an applicative functor, but does not require that
instances prove they satisfy the laws of an applicative functor. A `LawfulApplicative` instance
includes proofs that the laws are satisfied.
Because `Applicative` instances may provide optimized implementations of `seqLeft` and `seqRight`,
`LawfulApplicative` instances must also prove that the optimized implementation is equivalent to the
standard implementation.
-/
class LawfulApplicative (f : Type u Type v) [Applicative f] : Prop extends LawfulFunctor f where
/-- `seqLeft` is equivalent to the default implementation. -/
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
/-- `seqRight` is equivalent to the default implementation. -/
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
/--
`pure` before `seq` is equivalent to `Functor.map`.
This means that `pure` really is pure when occurring immediately prior to `seq`.
-/
pure_seq (g : α β) (x : f α) : pure g <*> x = g <$> x
/--
Mapping a function over the result of `pure` is equivalent to applying the function under `pure`.
This means that `pure` really is pure with respect to `Functor.map`.
-/
map_pure (g : α β) (x : α) : g <$> (pure x : f α) = pure (g x)
/--
`pure` after `seq` is equivalent to `Functor.map`.
This means that `pure` really is pure when occurring just after `seq`.
-/
seq_pure {α β : Type u} (g : f (α β)) (x : α) : g <*> pure x = (fun h => h x) <$> g
/--
`seq` is associative.
Changing the nesting of `seq` calls while maintaining the order of computations results in an
equivalent computation. This means that `seq` is not doing any more than sequencing.
-/
seq_assoc {α β γ : Type u} (x : f α) (g : f (α β)) (h : f (β γ)) : h <*> (g <*> x) = ((@comp α β γ) <$> h) <*> g <*> x
comp_map g h x := (by
repeat rw [ pure_seq]
@@ -66,21 +102,36 @@ attribute [simp] map_pure seq_pure
simp [pure_seq]
/--
The `Monad` typeclass only contains the operations of a monad.
`LawfulMonad` further asserts that these operations satisfy the laws of a monad,
including associativity and identity laws for `bind`:
```
pure x >>= f = f x
x >>= pure = x
x >>= f >>= g = x >>= (fun x => f x >>= g)
```
Lawful monads are those that satisfy a certain behavioral specification. While all instances of
`Monad` should satisfy these laws, not all implementations are required to prove this.
`LawfulMonad.mk'` is an alternative constructor containing useful defaults for many fields.
`LawfulMonad.mk'` is an alternative constructor that contains useful defaults for many fields.
-/
class LawfulMonad (m : Type u Type v) [Monad m] : Prop extends LawfulApplicative m where
/--
A `bind` followed by `pure` composed with a function is equivalent to a functorial map.
This means that `pure` really is pure after a `bind` and has no effects.
-/
bind_pure_comp (f : α β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
/--
A `bind` followed by a functorial map is equivalent to `Applicative` sequencing.
This means that the effect sequencing from `Monad` and `Applicative` are the same.
-/
bind_map {α β : Type u} (f : m (α β)) (x : m α) : f >>= (. <$> x) = f <*> x
/--
`pure` followed by `bind` is equivalent to function application.
This means that `pure` really is pure before a `bind` and has no effects.
-/
pure_bind (x : α) (f : α m β) : pure x >>= f = f x
/--
`bind` is associative.
Changing the nesting of `bind` calls while maintaining the order of computations results in an
equivalent computation. This means that `bind` is not doing more than data-dependent sequencing.
-/
bind_assoc (x : m α) (f : α m β) (g : β m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
map_pure g x := (by rw [ bind_pure_comp, pure_bind])
seq_pure g x := (by rw [ bind_map]; simp [map_pure, bind_pure_comp])

View File

@@ -52,8 +52,6 @@ attribute [simp] namedPattern
/--
`Empty.elim : Empty → C` says that a value of any type can be constructed from
`Empty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
This is a non-dependent variant of `Empty.rec`.
-/
@[macro_inline] def Empty.elim {C : Sort u} : Empty C := Empty.rec
@@ -63,8 +61,6 @@ instance : DecidableEq Empty := fun a => a.elim
/--
`PEmpty.elim : Empty → C` says that a value of any type can be constructed from
`PEmpty`. This can be thought of as a compiler-checked assertion that a code path is unreachable.
This is a non-dependent variant of `PEmpty.rec`.
-/
@[macro_inline] def PEmpty.elim {C : Sort _} : PEmpty C := fun a => nomatch a
@@ -138,46 +134,50 @@ recommended_spelling "iff" for "↔" in [Iff, «term_↔_»]
recommended_spelling "iff" for "<->" in [Iff, «term_<->_»]
/--
`Sum α β`, or `α ⊕ β`, is the disjoint union of types `α` and `β`.
An element of `α ⊕ β` is either of the form `.inl a` where `a : α`,
or `.inr b` where `b : β`.
The disjoint union of types `α` and `β`, ordinarily written `αβ`.
An element of `α ⊕ β` is either an `a : α` wrapped in `Sum.inl` or a `b : β` wrapped in `Sum.inr`.
`α ⊕ β` is not equivalent to the set-theoretic union of `α` and `β` because its values include an
indication of which of the two types was chosen. The union of a singleton set with itself contains
one element, while `Unit ⊕ Unit` contains distinct values `inl ()` and `inr ()`.
-/
inductive Sum (α : Type u) (β : Type v) where
/-- Left injection into the sum type `α ⊕ β`. If `a : α` then `.inl a : α ⊕ β`. -/
/-- Left injection into the sum type `α ⊕ β`. -/
| inl (val : α) : Sum α β
/-- Right injection into the sum type `α ⊕ β`. If `b : β` then `.inr b : α ⊕ β`. -/
/-- Right injection into the sum type `α ⊕ β`. -/
| inr (val : β) : Sum α β
@[inherit_doc] infixr:30 "" => Sum
/--
`PSum α β`, or `α ⊕' β`, is the disjoint union of types `α` and `β`.
It differs from `α ⊕ β` in that it allows `α` and `β` to have arbitrary sorts
`Sort u` and `Sort v`, instead of restricting to `Type u` and `Type v`. This means
that it can be used in situations where one side is a proposition, like `True ⊕' Nat`.
The disjoint union of arbitrary sorts `α` `β`, or `α ⊕' β`.
The reason this is not the default is that this type lives in the universe `Sort (max 1 u v)`,
which can cause problems for universe level unification,
because the equation `max 1 u v = ?u + 1` has no solution in level arithmetic.
`PSum` is usually only used in automation that constructs sums of arbitrary types.
It differs from `α ⊕ β` in that it allows `α` and `β` to have arbitrary sorts `Sort u` and `Sort v`,
instead of restricting them to `Type u` and `Type v`. This means that it can be used in situations
where one side is a proposition, like `True ⊕' Nat`. However, the resulting universe level
constraints are often more difficult to solve than those that result from `Sum`.
-/
inductive PSum (α : Sort u) (β : Sort v) where
/-- Left injection into the sum type `α ⊕' β`. If `a : α` then `.inl a : α ⊕' β`. -/
/-- Left injection into the sum type `α ⊕' β`.-/
| inl (val : α) : PSum α β
/-- Right injection into the sum type `α ⊕' β`. If `b : β` then `.inr b : α ⊕' β`. -/
/-- Right injection into the sum type `α ⊕' β`. -/
| inr (val : β) : PSum α β
@[inherit_doc] infixr:30 " ⊕' " => PSum
/--
`PSum α β` is inhabited if `α` is inhabited.
This is not an instance to avoid non-canonical instances.
If the left type in a sum is inhabited then the sum is inhabited.
This is not an instance to avoid non-canonical instances when both the left and right types are
inhabited.
-/
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := PSum.inl default
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := PSum.inl default
/--
`PSum α β` is inhabited if `β` is inhabited.
This is not an instance to avoid non-canonical instances.
If the right type in a sum is inhabited then the sum is inhabited.
This is not an instance to avoid non-canonical instances when both the left and right types are
inhabited.
-/
@[reducible] def PSum.inhabitedRight {α β} [Inhabited β] : Inhabited (PSum α β) := PSum.inr default
@@ -188,47 +188,58 @@ instance PSum.nonemptyRight [h : Nonempty β] : Nonempty (PSum α β) :=
Nonempty.elim h (fun b => PSum.inr b)
/--
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
whose first component is `a : α` and whose second component is `b : β a`
(so the type of the second component can depend on the value of the first component).
It is sometimes known as the dependent sum type, since it is the type level version
of an indexed summation.
Dependent pairs, in which the second element's type depends on the value of the first element. The
type `Sigma β` is typically written `Σ a : α, β a` or `(a : α) × β a`.
Although its values are pairs, `Sigma` is sometimes known as the *dependent sum type*, since it is
the type level version of an indexed summation.
-/
@[pp_using_anonymous_constructor]
structure Sigma {α : Type u} (β : α Type v) where
/-- Constructor for a dependent pair. If `a : α` and `b : β a` then `⟨a, b⟩ : Sigma β`.
(This will usually require a type ascription to determine `β`
since it is not determined from `a` and `b` alone.) -/
/--
Constructs a dependent pair.
Using this constructor in a context in which the type is not known usually requires a type
ascription to determine `β`. This is because the desired relationship between the two values can't
generally be determined automatically.
-/
mk ::
/-- The first component of a dependent pair. If `p : @Sigma α β` then `p.1 : α`. -/
/--
The first component of a dependent pair.
-/
fst : α
/-- The second component of a dependent pair. If `p : Sigma β` then `p.2 : β p.1`. -/
/--
The second component of a dependent pair. Its type depends on the first component.
-/
snd : β fst
attribute [unbox] Sigma
/--
`PSigma β`, also denoted `Σ' a : α, β a` or `(a : α) ×' β a`, is the type of dependent pairs
whose first component is `a : α` and whose second component is `b : β a`
(so the type of the second component can depend on the value of the first component).
It differs from `Σ a : α, β a` in that it allows `α` and `β` to have arbitrary sorts
`Sort u` and `Sort v`, instead of restricting to `Type u` and `Type v`. This means
that it can be used in situations where one side is a proposition, like `(p : Nat) ×' p = p`.
Fully universe-polymorphic dependent pairs, in which the second element's type depends on the value
of the first element and both types are allowed to be propositions. The type `PSigma β` is typically
written `Σ' a : α, β a` or `(a : α) ×' β a`.
The reason this is not the default is that this type lives in the universe `Sort (max 1 u v)`,
which can cause problems for universe level unification,
because the equation `max 1 u v = ?u + 1` has no solution in level arithmetic.
`PSigma` is usually only used in automation that constructs pairs of arbitrary types.
In practice, this generality leads to universe level constraints that are difficult to solve, so
`PSigma` is rarely used in manually-written code. It is usually only used in automation that
constructs pairs of arbitrary types.
To pair a value with a proof that a predicate holds for it, use `Subtype`. To demonstrate that a
value exists that satisfies a predicate, use `Exists`. A dependent pair with a proposition as its
first component is not typically useful due to proof irrelevance: there's no point in depending on a
specific proof because all proofs are equal anyway.
-/
@[pp_using_anonymous_constructor]
structure PSigma {α : Sort u} (β : α Sort v) where
/-- Constructor for a dependent pair. If `a : α` and `b : β a` then `⟨a, b⟩ : PSigma β`.
(This will usually require a type ascription to determine `β`
since it is not determined from `a` and `b` alone.) -/
/-- Constructs a fully universe-polymorphic dependent pair. -/
mk ::
/-- The first component of a dependent pair. If `p : @Sigma α β` then `p.1 : α`. -/
/--
The first component of a dependent pair.
-/
fst : α
/-- The second component of a dependent pair. If `p : Sigma β` then `p.2 : β p.1`. -/
/--
The second component of a dependent pair. Its type depends on the first component.
-/
snd : β fst
/--
@@ -514,10 +525,21 @@ export Singleton (singleton)
class LawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert α β] [Singleton α β] :
Prop where
/-- `insert x ∅ = {x}` -/
insert_emptyc_eq (x : α) : (insert x : β) = singleton x
export LawfulSingleton (insert_emptyc_eq)
insert_empty_eq (x : α) : (insert x : β) = singleton x
export LawfulSingleton (insert_empty_eq)
attribute [simp] insert_empty_eq
@[deprecated insert_empty_eq (since := "2025-03-12")]
theorem insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
[LawfulSingleton α β] (x : α) : (insert x : β) = singleton x :=
insert_empty_eq _
@[deprecated insert_empty_eq (since := "2025-03-12")]
theorem LawfulSingleton.insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
[LawfulSingleton α β] (x : α) : (insert x : β) = singleton x :=
insert_empty_eq _
attribute [simp] insert_emptyc_eq
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
class Sep (α : outParam <| Type u) (γ : Type v) where
@@ -676,9 +698,11 @@ Unlike `x ≠ y` (which is notation for `Ne x y`), this is `Bool` valued instead
recommended_spelling "bne" for "!=" in [bne, «term_!=_»]
/--
`LawfulBEq α` is a typeclass which asserts that the `BEq α` implementation
(which supplies the `a == b` notation) coincides with logical equality `a = b`.
In other words, `a == b` implies `a = b`, and `a == a` is true.
A Boolean equality test coincides with propositional equality.
In other words:
* `a == b` implies `a = b`.
* `a == a` is true.
-/
class LawfulBEq (α : Type u) [BEq α] : Prop where
/-- If `a == b` evaluates to `true`, then `a` and `b` are equal in the logic. -/
@@ -935,9 +959,10 @@ namespace Decidable
variable {p q : Prop}
/--
Synonym for `dite` (dependent if-then-else). We can construct an element `q`
(of any sort, not just a proposition) by cases on whether `p` is true or false,
provided `p` is decidable.
Construct a `q` if some proposition `p` is decidable, and both the truth and falsity of `p` are
sufficient to construct a `q`.
This is a synonym for `dite`, the dependent if-then-else operator.
-/
@[macro_inline] def byCases {q : Sort u} [dec : Decidable p] (h1 : p q) (h2 : ¬p q) : q :=
match dec with
@@ -1070,22 +1095,28 @@ theorem nonempty_of_exists {α : Sort u} {p : α → Prop} : Exists (fun x => p
/-! # Subsingleton -/
/--
A "subsingleton" is a type with at most one element.
In other words, it is either empty, or has a unique element.
All propositions are subsingletons because of proof irrelevance, but some other types
are subsingletons as well and they inherit many of the same properties as propositions.
`Subsingleton α` is a typeclass, so it is usually used as an implicit argument and
inferred by typeclass inference.
A _subsingleton_ is a type with at most one element. It is either empty or has a unique element.
All propositions are subsingletons because of proof irrelevance: false propositions are empty, and
all proofs of a true proposition are equal to one another. Some non-propositional types are also
subsingletons.
-/
class Subsingleton (α : Sort u) : Prop where
/-- Construct a proof that `α` is a subsingleton by showing that any two elements are equal. -/
/-- Prove that `α` is a subsingleton by showing that any two elements are equal. -/
intro ::
/-- Any two elements of a subsingleton are equal. -/
allEq : (a b : α) a = b
/--
If a type is a subsingleton, then all of its elements are equal.
-/
protected theorem Subsingleton.elim {α : Sort u} [h : Subsingleton α] : (a b : α) a = b :=
h.allEq
/--
If two types are equal and one of them is a subsingleton, then all of their elements are
[heterogeneously equal](lean-manual://section/HEq).
-/
protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (h₂ : α = β) (a : α) (b : β) : HEq a b := by
subst h₂
apply heq_of_eq
@@ -1123,22 +1154,21 @@ theorem recSubsingleton
| isFalse h => h₄ h
/--
An equivalence relation `~ : αα → Prop` is a relation that is:
An equivalence relation `r : αα → Prop` is a relation that is
* reflexive: `x ~ x`
* symmetric: `x ~ y` implies `y ~ x`
* transitive: `x ~ y` and `y ~ z` implies `x ~ z`
* reflexive: `r x x`,
* symmetric: `r x y` implies `r y x`, and
* transitive: `r x y` and `r y z` implies `r x z`.
Equality is an equivalence relation, and equivalence relations share many of
the properties of equality. In particular, `Quot α r` is most well behaved
when `r` is an equivalence relation, and in this case we use `Quotient` instead.
Equality is an equivalence relation, and equivalence relations share many of the properties of
equality.
-/
structure Equivalence {α : Sort u} (r : α α Prop) : Prop where
/-- An equivalence relation is reflexive: `x ~ x` -/
/-- An equivalence relation is reflexive: `r x x` -/
refl : x, r x x
/-- An equivalence relation is symmetric: `x ~ y` implies `y ~ x` -/
/-- An equivalence relation is symmetric: `r x y` implies `r y x` -/
symm : {x y}, r x y r y x
/-- An equivalence relation is transitive: `x ~ y` and `y ~ z` implies `x ~ z` -/
/-- An equivalence relation is transitive: `r x y` and `r y z` implies `r x z` -/
trans : {x y z}, r x y r y z r x z
/-- The empty relation is the relation on `α` which is always `False`. -/
@@ -1210,12 +1240,12 @@ end Subtype
section
variable {α : Type u} {β : Type v}
/-- This is not an instance to avoid non-canonical instances. -/
@[reducible] def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
@[reducible, inherit_doc PSum.inhabitedLeft]
def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
default := Sum.inl default
/-- This is not an instance to avoid non-canonical instances. -/
@[reducible] def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
@[reducible, inherit_doc PSum.inhabitedRight]
def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
default := Sum.inr default
instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
@@ -1275,7 +1305,12 @@ instance [DecidableEq α] [DecidableEq β] : DecidableEq (α × β) :=
instance [BEq α] [BEq β] : BEq (α × β) where
beq := fun (a₁, b₁) (a₂, b₂) => a₁ == a₂ && b₁ == b₂
/-- Lexicographical order for products -/
/--
Lexicographical order for products.
Two pairs are lexicographically ordered if their first elements are ordered or if their first
elements are equal and their second elements are ordered.
-/
def Prod.lexLt [LT α] [LT β] (s : α × β) (t : α × β) : Prop :=
s.1 < t.1 (s.1 = t.1 s.2 < t.2)
@@ -1291,8 +1326,11 @@ theorem Prod.lexLt_def [LT α] [LT β] (s t : α × β) : (Prod.lexLt s t) = (s.
theorem Prod.eta (p : α × β) : (p.1, p.2) = p := rfl
/--
`Prod.map f g : α₁ × β₁ → α₂ × β₂` maps across a pair
by applying `f` to the first component and `g` to the second.
Transforms a pair by applying functions to both elements.
Examples:
* `(1, 2).map (· + 1) (· * 3) = (2, 6)`
* `(1, 2).map toString (· * 3) = ("1", 6)`
-/
def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂ : Type v₂}
(f : α₁ α₂) (g : β₁ β₂) : α₁ × β₁ α₂ × β₂
@@ -1339,7 +1377,8 @@ instance : DecidableEq PUnit :=
/--
A setoid is a type with a distinguished equivalence relation, denoted `≈`.
This is mainly used as input to the `Quotient` type constructor.
The `Quotient` type constructor requires a `Setoid` instance.
-/
class Setoid (α : Sort u) where
/-- `x ≈ y` is the distinguished equivalence relation of a setoid. -/
@@ -1354,12 +1393,15 @@ namespace Setoid
variable {α : Sort u} [Setoid α]
/-- A setoid's equivalence relation is reflexive. -/
theorem refl (a : α) : a a :=
iseqv.refl a
/-- A setoid's equivalence relation is symmetric. -/
theorem symm {a b : α} (hab : a b) : b a :=
iseqv.symm hab
/-- A setoid's equivalence relation is transitive. -/
theorem trans {a b c : α} (hab : a b) (hbc : b c) : a c :=
iseqv.trans hab hbc
@@ -1576,34 +1618,21 @@ theorem imp_iff_not (hb : ¬b) : a → b ↔ ¬a := imp_congr_right fun _ => iff
namespace Quot
/--
The **quotient axiom**, or at least the nontrivial part of the quotient
axiomatization. Quotient types are introduced by the `init_quot` command
in `Init.Prelude` which introduces the axioms:
The **quotient axiom**, which asserts the equality of elements related by the quotient's relation.
```
opaque Quot {α : Sort u} (r : αα → Prop) : Sort u
The relation `r` does not need to be an equivalence relation to use this axiom. When `r` is not an
equivalence relation, the quotient is with respect to the equivalence relation generated by `r`.
opaque Quot.mk {α : Sort u} (r : αα → Prop) (a : α) : Quot r
`Quot.sound` is part of the built-in primitive quotient type:
* `Quot` is the built-in quotient type.
* `Quot.mk` places elements of the underlying type `α` into the quotient.
* `Quot.lift` allows the definition of functions from the quotient to some other type.
* `Quot.ind` is used to write proofs about quotients by assuming that all elements are constructed
with `Quot.mk`; it is analogous to the [recursor](lean-manual://section/recursors) for a
structure.
opaque Quot.lift {α : Sort u} {r : αα → Prop} {β : Sort v} (f : α → β) :
(∀ a b : α, r a b → f a = f b) → Quot r → β
opaque Quot.ind {α : Sort u} {r : αα → Prop} {β : Quot r → Prop} :
(∀ a : α, β (Quot.mk r a)) → ∀ q : Quot r, β q
```
All of these axioms are true if we assume `Quot α r = α` and `Quot.mk` and
`Quot.lift` are identity functions, so they do not add much. However this axiom
cannot be explained in that way (it is false for that interpretation), so the
real power of quotient types come from this axiom.
It says that the quotient by `r` maps elements which are related by `r` to equal
values in the quotient. Together with `Quot.lift` which says that functions
which respect `r` can be lifted to functions on the quotient, we can deduce that
`Quot α r` exactly consists of the equivalence classes with respect to `r`.
It is important to note that `r` need not be an equivalence relation in this axiom.
When `r` is not an equivalence relation, we are actually taking a quotient with
respect to the equivalence relation generated by `r`.
[Quotient types](lean-manual://section/quotients) are described in more detail in the Lean Language
Reference.
-/
axiom sound : {α : Sort u} {r : α α Prop} {a b : α}, r a b Quot.mk r a = Quot.mk r b
@@ -1621,8 +1650,17 @@ protected theorem indBeta {α : Sort u} {r : αα → Prop} {motive : Quot
rfl
/--
`Quot.liftOn q f h` is the same as `Quot.lift f h q`. It just reorders
the argument `q : Quot r` to be first.
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
quotient's relation.
Given a relation `r : αα → Prop` and a quotient's value `q : Quot r`, applying a `f : α → β`
requires a proof `c` that `f` respects `r`. In this case, `Quot.liftOn q f h : β` evaluates
to the result of applying `f` to the underlying value in `α` from `q`.
`Quot.liftOn` is a version of the built-in primitive `Quot.lift` with its parameters re-ordered.
[Quotient types](lean-manual://section/quotients) are described in more detail in the Lean Language
Reference.
-/
protected abbrev liftOn {α : Sort u} {β : Sort v} {r : α α Prop}
(q : Quot r) (f : α β) (c : (a b : α) r a b f a = f b) : β :=
@@ -1663,12 +1701,19 @@ protected theorem liftIndepPr1
exact rfl
/--
Dependent recursion principle for `Quot`. This constructor can be tricky to use,
so you should consider the simpler versions if they apply:
* `Quot.lift`, for nondependent functions
* `Quot.ind`, for theorems / proofs of propositions about quotients
* `Quot.recOnSubsingleton`, when the target type is a `Subsingleton`
* `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption
A dependent recursion principle for `Quot`. It is analogous to the
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
is not necessarily a proposition.
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
be easier to use:
* `Quot.lift` is useful for defining non-dependent functions.
* `Quot.ind` is useful for proving theorems about quotients.
* `Quot.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
* `Quot.hrecOn` uses [heterogeneous equality](lean-manual://section/HEq) instead of rewriting with
`Quot.sound`.
`Quot.recOn` is a version of this recursor that takes the quotient parameter first.
-/
@[elab_as_elim] protected abbrev rec
(f : (a : α) motive (Quot.mk r a))
@@ -1676,7 +1721,22 @@ so you should consider the simpler versions if they apply:
(q : Quot r) : motive q :=
Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2)
@[inherit_doc Quot.rec, elab_as_elim] protected abbrev recOn
/--
A dependent recursion principle for `Quot` that takes the quotient first. It is analogous to the
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
is not necessarily a proposition.
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
be easier to use:
* `Quot.lift` is useful for defining non-dependent functions.
* `Quot.ind` is useful for proving theorems about quotients.
* `Quot.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
* `Quot.hrecOn` uses [heterogeneous equality](lean-manual://section/HEq) instead of rewriting with
`Quot.sound`.
`Quot.rec` is a version of this recursor that takes the quotient parameter last.
-/
@[elab_as_elim] protected abbrev recOn
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
@@ -1684,8 +1744,13 @@ so you should consider the simpler versions if they apply:
q.rec f h
/--
Dependent induction principle for a quotient, when the target type is a `Subsingleton`.
In this case the quotient's side condition is trivial so any function can be lifted.
An alternative induction principle for quotients that can be used when the target type is a
subsingleton, in which all elements are equal.
In these cases, the proof that the function respects the quotient's relation is trivial, so any
function can be lifted.
`Quot.rec` does not assume that the type is a subsingleton.
-/
@[elab_as_elim] protected abbrev recOnSubsingleton
[h : (a : α) Subsingleton (motive (Quot.mk r a))]
@@ -1697,9 +1762,11 @@ In this case the quotient's side condition is trivial so any function can be lif
apply Subsingleton.elim
/--
Heterogeneous dependent recursion principle for a quotient.
This may be easier to work with since it uses `HEq` instead of
an `Eq.ndrec` in the hypothesis.
A dependent recursion principle for `Quot` that uses [heterogeneous
equality](lean-manual://section/HEq), analogous to a [recursor](lean-manual://section/recursors) for
a structure.
`Quot.recOn` is a version of this recursor that uses `Eq` instead of `HEq`.
-/
protected abbrev hrecOn
(q : Quot r)
@@ -1715,48 +1782,101 @@ end Quot
set_option linter.unusedVariables.funArgs false in
/--
`Quotient α s` is the same as `Quot α r`, but it is specialized to a setoid `s`
(that is, an equivalence relation) instead of an arbitrary relation.
Prefer `Quotient` over `Quot` if your relation is actually an equivalence relation.
Quotient types coarsen the propositional equality for a type so that terms related by some
equivalence relation are considered equal. The equivalence relation is given by an instance of
`Setoid`.
Set-theoretically, `Quotient s` can seen as the set of equivalence classes of `α` modulo the
`Setoid` instance's relation `s.r`. Functions from `Quotient s` must prove that they respect `s.r`:
to define a function `f : Quotient s → β`, it is necessary to provide `f' : α → β` and prove that
for all `x : α` and `y : α`, `s.r x y → f' x = f' y`. `Quotient.lift` implements this operation.
The key quotient operators are:
* `Quotient.mk` places elements of the underlying type `α` into the quotient.
* `Quotient.lift` allows the definition of functions from the quotient to some other type.
* `Quotient.sound` asserts the equality of elements related by `r`
* `Quotient.ind` is used to write proofs about quotients by assuming that all elements are
constructed with `Quotient.mk`.
`Quotient` is built on top of the primitive quotient type `Quot`, which does not require a proof
that the relation is an equivalence relation. `Quotient` should be used instead of `Quot` for
relations that actually are equivalence relations.
-/
def Quotient {α : Sort u} (s : Setoid α) :=
@Quot α Setoid.r
namespace Quotient
/-- The canonical quotient map into a `Quotient`. -/
/--
Places an element of a type into the quotient that equates terms according to an equivalence
relation.
The setoid instance is provided explicitly. `Quotient.mk'` uses instance synthesis instead.
Given `v : α`, `Quotient.mk s v : Quotient s` is like `v`, except all observations of `v`'s value
must respect `s.r`. `Quotient.lift` allows values in a quotient to be mapped to other types, so long
as the mapping respects `s.r`.
-/
@[inline]
protected def mk {α : Sort u} (s : Setoid α) (a : α) : Quotient s :=
Quot.mk Setoid.r a
/--
The canonical quotient map into a `Quotient`.
(This synthesizes the setoid by typeclass inference.)
Places an element of a type into the quotient that equates terms according to an equivalence
relation.
The equivalence relation is found by synthesizing a `Setoid` instance. `Quotient.mk` instead expects
the instance to be provided explicitly.
Given `v : α`, `Quotient.mk' v : Quotient s` is like `v`, except all observations of `v`'s value
must respect `s.r`. `Quotient.lift` allows values in a quotient to be mapped to other types, so long
as the mapping respects `s.r`.
-/
protected def mk' {α : Sort u} [s : Setoid α] (a : α) : Quotient s :=
Quotient.mk s a
/--
The analogue of `Quot.sound`: If `a` and `b` are related by the equivalence relation,
then they have equal equivalence classes.
The **quotient axiom**, which asserts the equality of elements related in the setoid.
Because `Quotient` is built on a lower-level type `Quot`, `Quotient.sound` is implemented as a
theorem. It is derived from `Quot.sound`, the soundness axiom for the lower-level quotient type
`Quot`.
-/
theorem sound {α : Sort u} {s : Setoid α} {a b : α} : a b Quotient.mk s a = Quotient.mk s b :=
Quot.sound
/--
The analogue of `Quot.lift`: if `f : α → β` respects the equivalence relation `≈`,
then it lifts to a function on `Quotient s` such that `lift f h (mk a) = f a`.
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
quotient's equivalence relation.
Given `s : Setoid α` and a quotient `Quotient s`, applying a function `f : α → β` requires a proof
`h` that `f` respects the equivalence relation `s.r`. In this case, the function
`Quotient.lift f h : Quotient s → β` computes the same values as `f`.
`Quotient.liftOn` is a version of this operation that takes the quotient value as its first explicit
parameter.
-/
protected abbrev lift {α : Sort u} {β : Sort v} {s : Setoid α} (f : α β) : ((a b : α) a b f a = f b) Quotient s β :=
Quot.lift f
/-- The analogue of `Quot.ind`: every element of `Quotient s` is of the form `Quotient.mk s a`. -/
/--
A reasoning principle for quotients that allows proofs about quotients to assume that all values are
constructed with `Quotient.mk`.
-/
protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s Prop} : ((a : α) motive (Quotient.mk s a)) (q : Quotient s) motive q :=
Quot.ind
/--
The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`,
then it lifts to a function on `Quotient s` such that `liftOn (mk a) f h = f a`.
Lifts a function from an underlying type to a function on a quotient, requiring that it respects the
quotient's equivalence relation.
Given `s : Setoid α` and a quotient value `q : Quotient s`, applying a function `f : α → β` requires
a proof `c` that `f` respects the equivalence relation `s.r`. In this case, the term
`Quotient.liftOn q f h : β` reduces to the result of applying `f` to the underlying `α` value.
`Quotient.lift` is a version of this operation that takes the quotient value last, rather than
first.
-/
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α β) (c : (a b : α) a b f a = f b) : β :=
Quot.liftOn q f c
@@ -1777,7 +1897,21 @@ variable {α : Sort u}
variable {s : Setoid α}
variable {motive : Quotient s Sort v}
/-- The analogue of `Quot.rec` for `Quotient`. See `Quot.rec`. -/
/--
A dependent recursion principle for `Quotient`. It is analogous to the
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
is not necessarily a proposition.
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
be easier to use:
* `Quotient.lift` is useful for defining non-dependent functions.
* `Quotient.ind` is useful for proving theorems about quotients.
* `Quotient.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
* `Quotient.hrecOn` uses heterogeneous equality instead of rewriting with `Quotient.sound`.
`Quotient.recOn` is a version of this recursor that takes the quotient parameter first.
-/
@[inline, elab_as_elim]
protected def rec
(f : (a : α) motive (Quotient.mk s a))
@@ -1786,7 +1920,21 @@ protected def rec
: motive q :=
Quot.rec f h q
/-- The analogue of `Quot.recOn` for `Quotient`. See `Quot.recOn`. -/
/--
A dependent recursion principle for `Quotient`. It is analogous to the
[recursor](lean-manual://section/recursors) for a structure, and can be used when the resulting type
is not necessarily a proposition.
While it is very general, this recursor can be tricky to use. The following simpler alternatives may
be easier to use:
* `Quotient.lift` is useful for defining non-dependent functions.
* `Quotient.ind` is useful for proving theorems about quotients.
* `Quotient.recOnSubsingleton` can be used whenever the target type is a `Subsingleton`.
* `Quotient.hrecOn` uses heterogeneous equality instead of rewriting with `Quotient.sound`.
`Quotient.rec` is a version of this recursor that takes the quotient parameter last.
-/
@[elab_as_elim]
protected abbrev recOn
(q : Quotient s)
@@ -1795,7 +1943,15 @@ protected abbrev recOn
: motive q :=
Quot.recOn q f h
/-- The analogue of `Quot.recOnSubsingleton` for `Quotient`. See `Quot.recOnSubsingleton`. -/
/--
An alternative recursion or induction principle for quotients that can be used when the target type
is a subsingleton, in which all elements are equal.
In these cases, the proof that the function respects the quotient's equivalence relation is trivial,
so any function can be lifted.
`Quotient.rec` does not assume that the target type is a subsingleton.
-/
@[elab_as_elim]
protected abbrev recOnSubsingleton
[h : (a : α) Subsingleton (motive (Quotient.mk s a))]
@@ -1804,7 +1960,13 @@ protected abbrev recOnSubsingleton
: motive q :=
Quot.recOnSubsingleton (h := h) q f
/-- The analogue of `Quot.hrecOn` for `Quotient`. See `Quot.hrecOn`. -/
/--
A dependent recursion principle for `Quotient` that uses [heterogeneous
equality](lean-manual://section/HEq), analogous to a [recursor](lean-manual://section/recursors) for
a structure.
`Quotient.recOn` is a version of this recursor that uses `Eq` instead of `HEq`.
-/
@[elab_as_elim]
protected abbrev hrecOn
(q : Quotient s)
@@ -1819,7 +1981,13 @@ universe uA uB uC
variable {α : Sort uA} {β : Sort uB} {φ : Sort uC}
variable {s₁ : Setoid α} {s₂ : Setoid β}
/-- Lift a binary function to a quotient on both arguments. -/
/--
Lifts a binary function from the underlying types to a binary function on quotients. The function
must respect both quotients' equivalence relations.
`Quotient.lift` is a version of this operation for unary functions. `Quotient.liftOn₂` is a version
that take the quotient parameters first.
-/
protected abbrev lift₂
(f : α β φ)
(c : (a₁ : α) (b₁ : β) (a₂ : α) (b₂ : β) a₁ a₂ b₁ b₂ f a₁ b₁ = f a₂ b₂)
@@ -1830,7 +1998,13 @@ protected abbrev lift₂
induction q₂ using Quotient.ind
apply c; assumption; apply Setoid.refl
/-- Lift a binary function to a quotient on both arguments. -/
/--
Lifts a binary function from the underlying types to a binary function on quotients. The function
must respect both quotients' equivalence relations.
`Quotient.liftOn` is a version of this operation for unary functions. `Quotient.lift₂` is a version
that take the quotient parameters last.
-/
protected abbrev liftOn₂
(q₁ : Quotient s₁)
(q₂ : Quotient s₂)
@@ -1895,6 +2069,9 @@ private theorem rel.refl {s : Setoid α} (q : Quotient s) : rel q q :=
private theorem rel_of_eq {s : Setoid α} {q₁ q₂ : Quotient s} : q₁ = q₂ rel q₁ q₂ :=
fun h => Eq.ndrecOn h (rel.refl q₁)
/--
If two values are equal in a quotient, then they are related by its equivalence relation.
-/
theorem exact {s : Setoid α} {a b : α} : Quotient.mk s a = Quotient.mk s b a b :=
fun h => rel_of_eq h
@@ -1905,7 +2082,13 @@ universe uA uB uC
variable {α : Sort uA} {β : Sort uB}
variable {s₁ : Setoid α} {s₂ : Setoid β}
/-- Lift a binary function to a quotient on both arguments. -/
/--
An alternative induction or recursion operator for defining binary operations on quotients that can
be used when the target type is a subsingleton.
In these cases, the proof that the function respects the quotient's equivalence relation is trivial,
so any function can be lifted.
-/
@[elab_as_elim]
protected abbrev recOnSubsingleton₂
{motive : Quotient s₁ Quotient s₂ Sort uC}
@@ -1925,10 +2108,6 @@ protected abbrev recOnSubsingleton₂
end
end Quotient
section
variable {α : Type u}
variable (r : α α Prop)
instance Quotient.decidableEq {α : Sort u} {s : Setoid α} [d : (a b : α), Decidable (a b)]
: DecidableEq (Quotient s) :=
fun (q₁ q₂ : Quotient s) =>
@@ -1969,28 +2148,39 @@ instance Pi.instSubsingleton {α : Sort u} {β : α → Sort v} [∀ a, Subsingl
/-! # Squash -/
/--
`Squash α` is the quotient of `α` by the always true relation.
It is empty if `α` is empty, otherwise it is a singleton.
(Thus it is unconditionally a `Subsingleton`.)
It is the "universal `Subsingleton`" mapped from `α`.
The quotient of `α` by the universal relation. The elements of `Squash α` are those of `α`, but all
of them are equal and cannot be distinguished.
It is similar to `Nonempty α`, which has the same properties, but unlike
`Nonempty` this is a `Type u`, that is, it is "data", and the compiler
represents an element of `Squash α` the same as `α` itself
(as compared to `Nonempty α`, whose elements are represented by a dummy value).
`Squash α` is a `Subsingleton`: it is empty if `α` is empty, otherwise it has just one element. It
is the “universal `Subsingleton`” mapped from `α`.
`Squash.lift` will extract a value in any subsingleton `β` from a function on `α`,
while `Nonempty.rec` can only do the same when `β` is a proposition.
`Nonempty α` also has these properties. It is a proposition, which means that its elements (i.e.
proofs) are erased from compiled code and represented by a dummy value. `Squash α` is a `Type u`,
and its representation in compiled code is identical to that of `α`.
Consequently, `Squash.lift` may extract an `α` value into any subsingleton type `β`, while
`Nonempty.rec` can only do the same when `β` is a proposition.
-/
def Squash (α : Sort u) := Quot (fun (_ _ : α) => True)
/-- The canonical quotient map into `Squash α`. -/
/--
Places a value into its squash type, in which it cannot be distinguished from any other.
-/
def Squash.mk {α : Sort u} (x : α) : Squash α := Quot.mk _ x
/--
A reasoning principle that allows proofs about squashed types to assume that all values are
constructed with `Squash.mk`.
-/
theorem Squash.ind {α : Sort u} {motive : Squash α Prop} (h : (a : α), motive (Squash.mk a)) : (q : Squash α), motive q :=
Quot.ind h
/-- If `β` is a subsingleton, then a function `α → β` lifts to `Squash α → β`. -/
/--
Extracts a squashed value into any subsingleton type.
If `β` is a subsingleton, a function `α → β` cannot distinguish between elements of `α` and thus
automatically respects the universal relation that `Squash` quotients with.
-/
@[inline] def Squash.lift {α β} [Subsingleton β] (s : Squash α) (f : α β) : β :=
Quot.lift f (fun _ _ _ => Subsingleton.elim _ _) s

View File

@@ -555,6 +555,10 @@ def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array
(xs.push a).unattach = xs.unattach.push a.1 := by
simp only [unattach, Array.map_push]
@[simp] theorem mem_unattach {p : α Prop} {xs : Array { x // p x }} {a} :
a xs.unattach h : p a, a, h xs := by
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
@[simp] theorem size_unattach {p : α Prop} {xs : Array { x // p x }} :
xs.unattach.size = xs.size := by
unfold unattach
@@ -676,6 +680,20 @@ and simplifies these to the function directly taking the value.
simp
rw [List.find?_subtype hf]
@[simp] theorem all_subtype {p : α Prop} {xs : Array { x // p x }} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) (w : stop = xs.size) :
xs.all f 0 stop = xs.unattach.all g := by
subst w
rcases xs with xs
simp [hf]
@[simp] theorem any_subtype {p : α Prop} {xs : Array { x // p x }} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) (w : stop = xs.size) :
xs.any f 0 stop = xs.unattach.any g := by
subst w
rcases xs with xs
simp [hf]
/-! ### Simp lemmas pushing `unattach` inwards. -/
@[simp] theorem unattach_filter {p : α Prop} {xs : Array { x // p x }}

View File

@@ -34,7 +34,7 @@ variable {α : Type u}
namespace Array
@[deprecated toList (since := "2024-10-13")] abbrev data := @toList
@[deprecated toList (since := "2024-09-10")] abbrev data := @toList
/-! ### Preliminary theorems -/
@@ -144,9 +144,9 @@ end List
namespace Array
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
theorem size_eq_length_toList (xs : Array α) : xs.size = xs.toList.length := rfl
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
/-! ### Externs -/
@@ -252,7 +252,7 @@ instance [BEq α] : BEq (Array α) :=
```
ofFn f = #[f 0, f 1, ... , f(n - 1)]
``` -/
def ofFn {n} (f : Fin n α) : Array α := go 0 (mkEmpty n) where
def ofFn {n} (f : Fin n α) : Array α := go 0 (emptyWithCapacity n) where
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
go (i : Nat) (acc : Array α) : Array α :=
@@ -503,7 +503,7 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
else
pure bs
decreasing_by simp_wf; decreasing_trivial_pre_omega
map 0 (mkEmpty as.size)
map 0 (emptyWithCapacity as.size)
@[deprecated mapM (since := "2024-11-11")] abbrev sequenceMap := @mapM
@@ -520,7 +520,7 @@ def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
apply Nat.le_add_right
have : i + (j + 1) = as.size := by rw [ inv, Nat.add_comm j 1, Nat.add_assoc]
map i (j+1) this (bs.push ( f j as[j] j_lt))
map as.size 0 rfl (mkEmpty as.size)
map as.size 0 rfl (emptyWithCapacity as.size)
@[inline]
def mapIdxM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : Nat α m β) (as : Array α) : m (Array β) :=
@@ -551,7 +551,7 @@ Note that the universe level is contrained to `Type` here,
to avoid having to have the predicate live in `p : α → m (ULift Bool)`.
-/
@[inline]
def findM? {α : Type} {m : Type Type} [Monad m] (p : α m Bool) (as : Array α) : m (Option α) := do
def findM? {α : Type} [Monad m] (p : α m Bool) (as : Array α) : m (Option α) := do
for a in as do
if ( p a) then
return a

View File

@@ -23,6 +23,18 @@ section countP
variable (p q : α Bool)
@[simp] theorem _root_.List.countP_toArray (l : List α) : countP p l.toArray = l.countP p := by
simp [countP]
induction l with
| nil => rfl
| cons hd tl ih =>
simp only [List.foldr_cons, ih, List.countP_cons]
split <;> simp_all
@[simp] theorem countP_toList (xs : Array α) : xs.toList.countP p = countP p xs := by
cases xs
simp
@[simp] theorem countP_empty : countP p #[] = 0 := rfl
@[simp] theorem countP_push_of_pos (xs) (pa : p a) : countP p (xs.push a) = countP p xs + 1 := by
@@ -150,6 +162,13 @@ section count
variable [BEq α]
@[simp] theorem _root_.List.count_toArray (l : List α) (a : α) : count a l.toArray = l.count a := by
simp [count, List.count_eq_countP]
@[simp] theorem count_toList (xs : Array α) (a : α) : xs.toList.count a = xs.count a := by
cases xs
simp
@[simp] theorem count_empty (a : α) : count a #[] = 0 := rfl
theorem count_push (a b : α) (xs : Array α) :

View File

@@ -282,6 +282,10 @@ end erase
/-! ### eraseIdx -/
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
simp [eraseIdxIfInBounds, h]
theorem eraseIdx_eq_take_drop_succ (xs : Array α) (i : Nat) (h) : xs.eraseIdx i = xs.take i ++ xs.drop (i + 1) := by
rcases xs with xs
simp only [List.size_toArray] at h

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@ Authors: Mario Carneiro, Kim Morrison
prelude
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
import Init.Data.Array.OfFn
import Init.Data.List.MapIdx
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.

View File

@@ -23,6 +23,13 @@ open Nat
/-! ### mapM -/
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] (xs : Array α) (f : α β) :
xs.mapM (m := m) (pure <| f ·) = pure (xs.map f) := by
induction xs; simp_all
@[simp] theorem mapM_id {xs : Array α} {f : α Id β} : xs.mapM f = xs.map f :=
mapM_pure _ _
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α m β) {xs ys : Array α} :
(xs ++ ys).mapM f = (return ( xs.mapM f) ++ ( ys.mapM f)) := by
rcases xs with xs
@@ -162,7 +169,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
rcases xs with xs
simp [List.foldlM_map]
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(xs : Array α) (f : (a : α) a xs β β) (init : β) :
forIn' xs init (fun a m b => pure (.yield (f a m b))) =
pure (f := m) (xs.attach.foldl (fun b a, h => f a h b) init) := by
@@ -204,7 +211,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
rcases xs with xs
simp [List.foldlM_map]
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(xs : Array α) (f : α β β) (init : β) :
forIn xs init (fun a b => pure (.yield (f a b))) =
pure (f := m) (xs.foldl (fun b a => f a b) init) := by
@@ -224,6 +231,32 @@ theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
rcases xs with xs
simp
/-! ### allM and anyM -/
@[simp] theorem anyM_pure [Monad m] [LawfulMonad m] (p : α Bool) (xs : Array α) :
xs.anyM (m := m) (pure <| p ·) = pure (xs.any p) := by
cases xs
simp
@[simp] theorem allM_pure [Monad m] [LawfulMonad m] (p : α Bool) (xs : Array α) :
xs.allM (m := m) (pure <| p ·) = pure (xs.all p) := by
cases xs
simp
/-! ### findM? and findSomeM? -/
@[simp]
theorem findM?_pure {m} [Monad m] [LawfulMonad m] (p : α Bool) (xs : Array α) :
findM? (m := m) (pure <| p ·) xs = pure (xs.find? p) := by
cases xs
simp
@[simp]
theorem findSomeM?_pure [Monad m] [LawfulMonad m] (f : α Option β) (xs : Array α) :
findSomeM? (m := m) (pure <| f ·) xs = pure (xs.findSome? f) := by
cases xs
simp
end Array
namespace List
@@ -354,12 +387,12 @@ and simplifies these to the function directly taking the value.
simp
rw [List.foldlM_subtype hf]
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β α m β) :
(wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : Array α) (f : β α m β) (init : β) :
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by
simp [wfParam]
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : Array (Subtype P)) (f : β α m β) :
xs.unattach.foldlM f = xs.foldlM fun b x, h =>
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : Array (Subtype P)) (f : β α m β) (init : β) :
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b x, h =>
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
f b (wfParam x) := by
simp [wfParam]
@@ -378,12 +411,12 @@ and simplifies these to the function directly taking the value.
rw [List.foldrM_subtype hf]
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α β m β) :
(wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : Array α) (f : α β m β) (init : β) :
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by
simp [wfParam]
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α β m β) :
xs.unattach.foldrM f = xs.foldrM fun x, h b =>
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : Array (Subtype P)) (f : α β m β) (init : β):
xs.unattach.foldrM f init = xs.foldrM (init := init) fun x, h b =>
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
f (wfParam x) b := by
simp [wfParam]

View File

@@ -16,6 +16,25 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
@[simp] theorem ofFn_zero (f : Fin 0 α) : ofFn f = #[] := rfl
theorem ofFn_succ (f : Fin (n+1) α) :
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f n, by omega) := by
ext i h₁ h₂
· simp
· simp [getElem_push]
split <;> rename_i h₃
· rfl
· congr
simp at h₁ h₂
omega
@[simp] theorem _rooy_.List.toArray_ofFn (f : Fin n α) : (List.ofFn f).toArray = Array.ofFn f := by
ext <;> simp
@[simp] theorem toList_ofFn (f : Fin n α) : (Array.ofFn f).toList = List.ofFn f := by
apply List.ext_getElem <;> simp
@[simp]
theorem ofFn_eq_empty_iff {f : Fin n α} : ofFn f = #[] n = 0 := by
rw [ Array.toList_inj]

View File

@@ -10,15 +10,29 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
universe u v w
structure Subarray (α : Type u) where
/--
A region of some underlying array.
A subarray contains an array together with the start and end indices of a region of interest.
Subarrays can be used to avoid copying or allocating space, while being more convenient than
tracking the bounds by hand. The region of interest consists of every index that is both greater
than or equal to `start` and strictly less than `stop`.
-/
structure Subarray (α : Type u) where
/-- The underlying array. -/
array : Array α
/-- The starting index of the region of interest (inclusive). -/
start : Nat
/-- The ending index of the region of interest (exclusive). -/
stop : Nat
start_le_stop : start stop
stop_le_array_size : stop array.size
namespace Subarray
/--
Computes the size of the subarray.
-/
def size (s : Subarray α) : Nat :=
s.stop - s.start
@@ -28,6 +42,11 @@ theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
apply Nat.le_trans (Nat.sub_le stop start)
assumption
/--
Extracts an element from the subarray.
The index is relative to the start of the subarray, rather than the underlying array.
-/
def get (s : Subarray α) (i : Fin s.size) : α :=
have : s.start + i.val < s.array.size := by
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size
@@ -40,12 +59,33 @@ def get (s : Subarray α) (i : Fin s.size) : α :=
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
/--
Extracts an element from the subarray, or returns a default value `v₀` when the index is out of
bounds.
The index is relative to the start and end of the subarray, rather than the underlying array.
-/
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
if h : i < s.size then s[i] else v₀
/--
Extracts an element from the subarray, or returns a default value when the index is out of bounds.
The index is relative to the start and end of the subarray, rather than the underlying array. The
default value is that provided by the `Inhabited α` instance.
-/
abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
getD s i default
/--
Shrinks the subarray by incrementing its starting index if possible, returning it unchanged if not.
Examples:
* `#[1,2,3].toSubarray.popFront.toArray = #[2, 3]`
* `#[1,2,3].toSubarray.popFront.popFront.toArray = #[3]`
* `#[1,2,3].toSubarray.popFront.popFront.popFront.toArray = #[]`
* `#[1,2,3].toSubarray.popFront.popFront.popFront.popFront.toArray = #[]`
-/
def popFront (s : Subarray α) : Subarray α :=
if h : s.start < s.stop then
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
@@ -54,6 +94,8 @@ def popFront (s : Subarray α) : Subarray α :=
/--
The empty subarray.
This empty subarray is backed by an empty array.
-/
protected def empty : Subarray α where
array := #[]
@@ -88,46 +130,197 @@ protected opaque forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Mona
instance : ForIn m (Subarray α) α where
forIn := Subarray.forIn
/--
Folds a monadic operation from left to right over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
element of the subarray with the current accumulator value in turn. The monad in question may permit
early termination or repetition.
Examples:
```lean example
#eval #["red", "green", "blue"].toSubarray.foldlM (init := "") fun acc x => do
let l ← Option.guard (· ≠ 0) x.length
return s!"{acc}({l}){x} "
```
```output
some "(3)red (5)green (4)blue "
```
```lean example
#eval #["red", "green", "blue"].toSubarray.foldlM (init := 0) fun acc x => do
let l ← Option.guard (· ≠ 5) x.length
return s!"{acc}({l}){x} "
```
```output
none
```
-/
@[inline]
def foldlM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : β α m β) (init : β) (as : Subarray α) : m β :=
as.array.foldlM f (init := init) (start := as.start) (stop := as.stop)
/--
Folds a monadic operation from right to left over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and monadically combining each
element of the subarray with the current accumulator value in turn, moving from the end to the
start. The monad in question may permit early termination or repetition.
Examples:
```lean example
#eval #["red", "green", "blue"].toSubarray.foldrM (init := "") fun x acc => do
let l ← Option.guard (· ≠ 0) x.length
return s!"{acc}({l}){x} "
```
```output
some "(4)blue (5)green (3)red "
```
```lean example
#eval #["red", "green", "blue"].toSubarray.foldrM (init := 0) fun x acc => do
let l ← Option.guard (· ≠ 5) x.length
return s!"{acc}({l}){x} "
```
```output
none
```
-/
@[inline]
def foldrM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α β m β) (init : β) (as : Subarray α) : m β :=
as.array.foldrM f (init := init) (start := as.stop) (stop := as.start)
/--
Checks whether any of the elements in a subarray satisfy a monadic Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that satisfies the predicate is found.
Example:
```lean example
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.anyM fun x => do
IO.println x
pure (x == "blue")
```
```output
green
blue
```
```output
true
```
-/
@[inline]
def anyM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Subarray α) : m Bool :=
as.array.anyM p (start := as.start) (stop := as.stop)
/--
Checks whether all of the elements in a subarray satisfy a monadic Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that does not satisfy the predicate is found.
Example:
```lean example
#eval #["red", "green", "blue", "orange"].toSubarray.popFront.allM fun x => do
IO.println x
pure (x.length == 5)
```
```output
green
blue
```
```output
false
```
-/
@[inline]
def allM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Subarray α) : m Bool :=
as.array.allM p (start := as.start) (stop := as.stop)
/--
Runs a monadic action on each element of a subarray.
The elements are processed starting at the lowest index and moving up.
-/
@[inline]
def forM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Subarray α) : m PUnit :=
as.array.forM f (start := as.start) (stop := as.stop)
/--
Runs a monadic action on each element of a subarray, in reverse order.
The elements are processed starting at the highest index and moving down.
-/
@[inline]
def forRevM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Subarray α) : m PUnit :=
as.array.forRevM f (start := as.stop) (stop := as.start)
/--
Folds an operation from left to right over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and combining each
element of the subarray with the current accumulator value in turn.
Examples:
* `#["red", "green", "blue"].toSubarray.foldl (· + ·.length) 0 = 12`
* `#["red", "green", "blue"].toSubarray.popFront.foldl (· + ·.length) 0 = 9`
-/
@[inline]
def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldlM f (init := init)
/--
Folds an operation from right to left over the elements in a subarray.
An accumulator of type `β` is constructed by starting with `init` and combining each element of the
subarray with the current accumulator value in turn, moving from the end to the start.
Examples:
* `#eval #["red", "green", "blue"].toSubarray.foldr (·.length + ·) 0 = 12`
* `#["red", "green", "blue"].toSubarray.popFront.foldlr (·.length + ·) 0 = 9`
-/
@[inline]
def foldr {α : Type u} {β : Type v} (f : α β β) (init : β) (as : Subarray α) : β :=
Id.run <| as.foldrM f (init := init)
/--
Checks whether any of the elements in a subarray satisfy a Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that satisfies the predicate is found.
-/
@[inline]
def any {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.anyM p
/--
Checks whether all of the elements in a subarray satisfy a Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that does not satisfy the predicate is found.
-/
@[inline]
def all {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.allM p
/--
Applies a monadic function to each element in a subarray in reverse order, stopping at the first
element for which the function succeeds by returning a value other than `none`. The succeeding value
is returned, or `none` if there is no success.
Example:
```lean example
#eval #["red", "green", "blue"].toSubarray.findSomeRevM? fun x => do
IO.println x
return Option.guard (· = 5) x.length
```
```output
blue
green
```
```output
some 5
```
-/
@[inline]
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Subarray α) (f : α m (Option β)) : m (Option β) :=
let rec @[specialize] find : (i : Nat) i as.size m (Option β)
@@ -142,10 +335,39 @@ def findSomeRevM? {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
find i this
find as.size (Nat.le_refl _)
/--
Applies a monadic Boolean predicate to each element in a subarray in reverse order, stopping at the
first element that satisfies the predicate. The element that satisfies the predicate is returned, or
`none` if no element satisfies it.
Example:
```lean example
#eval #["red", "green", "blue"].toSubarray.findRevM? fun x => do
IO.println x
return (x.length = 5)
```
```output
blue
green
```
```output
some 5
```
-/
@[inline]
def findRevM? {α : Type} {m : Type Type w} [Monad m] (as : Subarray α) (p : α m Bool) : m (Option α) :=
as.findSomeRevM? fun a => return if ( p a) then some a else none
/--
Tests each element in a subarray with a Boolean predicate in reverse order, stopping at the first
element that satisfies the predicate. The element that satisfies the predicate is returned, or
`none` if no element satisfies the predicate.
Examples:
* `#["red", "green", "blue"].toSubarray.findRev? (·.length ≠ 4) = some "green"`
* `#["red", "green", "blue"].toSubarray.findRev? (fun _ => true) = some "blue"`
* `#["red", "green", "blue"].toSubarray 0 0 |>.findRev? (fun _ => true) = none`
-/
@[inline]
def findRev? {α : Type} (as : Subarray α) (p : α Bool) : Option α :=
Id.run <| as.findRevM? p
@@ -155,6 +377,12 @@ end Subarray
namespace Array
variable {α : Type u}
/--
Returns a subarray of an array, with the given bounds.
If `start` or `stop` are not valid bounds for a subarray, then they are clamped to array's size.
Additionally, the starting index is clamped to the ending index.
-/
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
if h₂ : stop as.size then
if h₁ : start stop then
@@ -177,6 +405,9 @@ def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Suba
start_le_stop := Nat.le_refl _,
stop_le_array_size := Nat.le_refl _ }
/--
Allocates a new array that contains the contents of the subarray.
-/
@[coe]
def ofSubarray (s : Subarray α) : Array α := Id.run do
let mut as := mkEmpty (s.stop - s.start)
@@ -186,8 +417,11 @@ def ofSubarray (s : Subarray α) : Array α := Id.run do
instance : Coe (Subarray α) (Array α) := ofSubarray
/-- A subarray with the provided bounds.-/
syntax:max term noWs "[" withoutPosition(term ":" term) "]" : term
/-- A subarray with the provided lower bound that extends to the rest of the array. -/
syntax:max term noWs "[" withoutPosition(term ":") "]" : term
/-- A subarray with the provided upper bound, starting at the index 0. -/
syntax:max term noWs "[" withoutPosition(":" term) "]" : term
macro_rules
@@ -197,6 +431,7 @@ macro_rules
end Array
@[inherit_doc Array.ofSubarray]
def Subarray.toArray (s : Subarray α) : Array α :=
Array.ofSubarray s

View File

@@ -20,7 +20,8 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Subarray
/--
Splits a subarray into two parts.
Splits a subarray into two parts, the first of which contains the first `i` elements and the second
of which contains the remainder.
-/
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
let i', isLt := i

View File

@@ -49,6 +49,14 @@ theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = fal
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b b == c a == c :=
PartialEquivBEq.trans
theorem BEq.congr_left [BEq α] [PartialEquivBEq α] {a b c : α} (h : a == b) :
(a == c) = (b == c) :=
Bool.eq_iff_iff.mpr BEq.trans (BEq.symm h), BEq.trans h
theorem BEq.congr_right [BEq α] [PartialEquivBEq α] {a b c : α} (h : b == c) :
(a == b) = (a == c) :=
Bool.eq_iff_iff.mpr fun h' => BEq.trans h' h, fun h' => BEq.trans h' (BEq.symm h)
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
(a == b) = false b == c (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))

View File

@@ -109,7 +109,12 @@ open Nat Bool
namespace Bool
/-- At least two out of three booleans are true. -/
/--
At least two out of three Booleans are true.
This function is typically used to model addition of binary numbers, to combine a carry bit with two
addend bits.
-/
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c
@[simp] theorem atLeastTwo_false_left : atLeastTwo false b c = (b && c) := by simp [atLeastTwo]
@@ -478,6 +483,36 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
case zero => exact hmsb
case succ => exact getMsbD_x _ hi (by omega)
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_ne_intMin`. -/
@[simp] theorem signExtend_neg_of_le {v w : Nat} (h : w v) (b : BitVec v) :
(-b).signExtend w = -b.signExtend w := by
apply BitVec.eq_of_getElem_eq
intro i hi
simp only [getElem_signExtend, getElem_neg]
rw [dif_pos (by omega), dif_pos (by omega)]
simp only [getLsbD_signExtend, Bool.and_eq_true, decide_eq_true_eq, Bool.ite_eq_true_distrib,
Bool.bne_right_inj, decide_eq_decide]
exact fun j, hj₁, hj₂ => j, hj₁, by omega, by rwa [if_pos (by omega)],
fun j, hj₁, hj₂, hj₃ => j, hj₁, by rwa [if_pos (by omega)] at hj₃
/-- This is false if `v < w` and `b = intMin`. See also `signExtend_neg_of_le`. -/
@[simp] theorem signExtend_neg_of_ne_intMin {v w : Nat} (b : BitVec v) (hb : b intMin v) :
(-b).signExtend w = -b.signExtend w := by
refine (by omega : w v v < w).elim (fun h => signExtend_neg_of_le h b) (fun h => ?_)
apply BitVec.eq_of_toInt_eq
rw [toInt_signExtend_of_le (by omega), toInt_neg_of_ne_intMin hb, toInt_neg_of_ne_intMin,
toInt_signExtend_of_le (by omega)]
apply ne_of_apply_ne BitVec.toInt
rw [toInt_signExtend_of_le (by omega), toInt_intMin_of_pos (by omega)]
have := b.le_two_mul_toInt
have : -2 ^ w < -2 ^ v := by
apply Int.neg_lt_neg
norm_cast
rwa [Nat.pow_lt_pow_iff_right (by omega)]
have : 2 * b.toInt -2 ^ w := by omega
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
/-! ### abs -/
theorem msb_abs {w : Nat} {x : BitVec w} :
@@ -544,6 +579,15 @@ theorem slt_eq_not_carry (x y : BitVec w) :
theorem sle_eq_not_slt (x y : BitVec w) : x.sle y = !y.slt x := by
simp only [BitVec.sle, BitVec.slt, decide_not, decide_eq_decide]; omega
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x x.msb = false := by
simp [zero_sle_eq_not_msb]
theorem toNat_toInt_of_sle {w : Nat} (b : BitVec w) (hb : BitVec.sle 0#w b) : b.toInt.toNat = b.toNat :=
toNat_toInt_of_msb b (zero_sle_iff_msb_eq_false.1 hb)
theorem sle_eq_carry (x y : BitVec w) :
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
@@ -1244,8 +1288,8 @@ theorem saddOverflow_eq {w : Nat} (x y : BitVec w) :
simp only [saddOverflow]
rcases w with _|w
· revert x y; decide
· have := le_toInt (x := x); have := toInt_lt (x := x)
have := le_toInt (x := y); have := toInt_lt (x := y)
· have := le_two_mul_toInt (x := x); have := two_mul_toInt_lt (x := x)
have := le_two_mul_toInt (x := y); have := two_mul_toInt_lt (x := y)
simp only [ decide_or, msb_eq_toInt, decide_beq_decide, toInt_add, decide_not, decide_and,
decide_eq_decide]
rw_mod_cast [Int.bmod_neg_iff (by omega) (by omega)]

View File

@@ -13,7 +13,9 @@ import Init.Data.Nat.Div.Lemmas
import Init.Data.Nat.Mod
import Init.Data.Nat.Div.Lemmas
import Init.Data.Int.Bitwise.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.Pow
import Init.Data.Int.LemmasAux
set_option linter.missingDocs true
@@ -239,12 +241,16 @@ theorem eq_of_getMsbD_eq {x y : BitVec w}
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp [ getLsbD_eq_getElem]
theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
theorem toInt_zero_length (x : BitVec 0) : x.toInt = 0 := by simp [of_length_zero]
theorem getLsbD_zero_length (x : BitVec 0) : x.getLsbD i = false := by simp
theorem getMsbD_zero_length (x : BitVec 0) : x.getMsbD i = false := by simp
theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero]
theorem toNat_of_zero_length (h : w = 0) (x : BitVec w) : x.toNat = 0 := by
subst h; simp [toNat_zero_length]
theorem toInt_of_zero_length (h : w = 0) (x : BitVec w) : x.toInt = 0 := by
subst h; simp [toInt_zero_length]
theorem getLsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getLsbD i = false := by
subst h; simp [getLsbD_zero_length]
theorem getMsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getMsbD i = false := by
@@ -323,8 +329,25 @@ theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
@[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' (2^w) x := rfl
@[simp] theorem finMk_toNat (x : BitVec w) : Fin.mk x.toNat x.isLt = x.toFin := rfl
@[simp] theorem toFin_ofNatLT {n : Nat} (h : n < 2 ^ w) : (BitVec.ofNatLT n h).toFin = Fin.mk n h := rfl
@[simp] theorem toFin_ofFin (n : Fin (2 ^ w)) : (BitVec.ofFin n).toFin = n := rfl
@[simp] theorem ofFin_toFin (x : BitVec w) : BitVec.ofFin x.toFin = x := rfl
@[simp] theorem ofNatLT_finVal (n : Fin (2 ^ w)) : BitVec.ofNatLT n.val n.isLt = BitVec.ofFin n := rfl
@[simp] theorem ofNatLT_toNat (x : BitVec w) : BitVec.ofNatLT x.toNat x.isLt = x := rfl
@[simp] theorem ofNat_finVal (n : Fin (2 ^ w)) : BitVec.ofNat w n.val = BitVec.ofFin n := by
rw [ BitVec.ofNatLT_eq_ofNat n.isLt, ofNatLT_finVal]
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) :
@@ -528,6 +551,9 @@ theorem toInt_eq_toNat_of_msb {x : BitVec w} (h : x.msb = false) :
x.toInt = x.toNat := by
simp [toInt_eq_msb_cond, h]
theorem toNat_toInt_of_msb {w : Nat} (b : BitVec w) (hb : b.msb = false) : b.toInt.toNat = b.toNat := by
simp [b.toInt_eq_toNat_of_msb hb]
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
simp only [toInt_eq_toNat_cond]
split
@@ -538,6 +564,16 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
rw [Int.bmod_neg] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
omega
theorem toInt_neg_of_msb_true {x : BitVec w} (h : x.msb = true) : x.toInt < 0 := by
simp only [BitVec.toInt]
have : 2 * x.toNat 2 ^ w := msb_eq_true_iff_two_mul_ge.mp h
omega
theorem toInt_nonneg_of_msb_false {x : BitVec w} (h : x.msb = false) : 0 x.toInt := by
simp only [BitVec.toInt]
have : 2 * x.toNat < 2 ^ w := msb_eq_false_iff_two_mul_lt.mp h
omega
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt x = y := by
intro eq
@@ -569,6 +605,11 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
have p : 0 i % (2^n : Nat) := by omega
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
(h : -2 ^ (w - 1) n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
have hw : w = (w - 1) + 1 := by omega
rw [toInt_ofInt, Int.bmod_eq_self_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
@[simp] theorem ofInt_natCast (w n : Nat) :
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
@@ -604,26 +645,50 @@ theorem toInt_zero {w : Nat} : (0#w).toInt = 0 := by
`x.toInt` is less than `2^(w-1)`.
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
-/
theorem toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by
theorem two_mul_toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by
simp only [BitVec.toInt]
rcases w with _|w'
· omega
· rw [ Nat.two_pow_pred_add_two_pow_pred (by omega), Nat.two_mul, Nat.add_sub_cancel]
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
norm_cast; omega
theorem two_mul_toInt_le {w : Nat} {x : BitVec w} : 2 * x.toInt 2 ^ w - 1 :=
Int.le_sub_one_of_lt two_mul_toInt_lt
theorem toInt_lt {w : Nat} {x : BitVec w} : x.toInt < 2 ^ (w - 1) := by
by_cases h : w = 0
· subst h
simp [eq_nil x]
· have := @two_mul_toInt_lt w x
rw_mod_cast [ Nat.two_pow_pred_add_two_pow_pred (by omega), Int.mul_comm, Int.natCast_add] at this
omega
theorem toInt_le {w : Nat} {x : BitVec w} : x.toInt 2 ^ (w - 1) - 1 :=
Int.le_sub_one_of_lt toInt_lt
/--
`x.toInt` is greater than or equal to `-2^(w-1)`.
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
-/
theorem le_toInt {w : Nat} {x : BitVec w} : -2 ^ w 2 * x.toInt := by
theorem le_two_mul_toInt {w : Nat} {x : BitVec w} : -2 ^ w 2 * x.toInt := by
simp only [BitVec.toInt]
rcases w with _|w'
· omega
· rw [ Nat.two_pow_pred_add_two_pow_pred (by omega), Nat.two_mul, Nat.add_sub_cancel]
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.Nat.cast_ofNat_Int]
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
norm_cast; omega
theorem le_toInt {w : Nat} (x : BitVec w) : -2 ^ (w - 1) x.toInt := by
by_cases h : w = 0
· subst h
simp [BitVec.eq_nil x]
· have := le_two_mul_toInt (w := w) (x := x)
generalize x.toInt = x at *
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
/-! ### slt -/
/--
@@ -645,6 +710,12 @@ theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w ↔ x.msb = true := by
simp [BitVec.slt, this]
omega
theorem slt_zero_eq_msb {w : Nat} {x : BitVec w} : x.slt 0#w = x.msb := by
rw [Bool.eq_iff_iff, BitVec.slt_zero_iff_msb_cond]
theorem sle_iff_toInt_le {w : Nat} {b b' : BitVec w} : b.sle b' b.toInt b'.toInt :=
decide_eq_true_iff
/-! ### setWidth, zeroExtend and truncate -/
@[simp]
@@ -996,6 +1067,11 @@ theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h
apply eq_of_toNat_eq
simp [extractLsb, show len - 1 + 1 = len by omega]
/-- Extracting all the bits of a bitvector is an identity operation. -/
@[simp] theorem extractLsb'_eq_self {x : BitVec w} : x.extractLsb' 0 w = x := by
apply eq_of_toNat_eq
simp [extractLsb']
/-! ### allOnes -/
@[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
@@ -1467,6 +1543,16 @@ theorem extractLsb_not_of_lt {x : BitVec w} {hi lo : Nat} (hlo : lo ≤ hi) (hhi
simp [hk, show k hi - lo by omega]
omega
@[simp]
theorem ne_not_self {a : BitVec w} (h : 0 < w) : a ~~~a := by
have : x, x < w := w - 1, by omega
simp [BitVec.eq_of_getElem_eq_iff, this]
@[simp]
theorem not_self_ne {a : BitVec w} (h : 0 < w) : ~~~a a := by
rw [ne_comm]
simp [h]
/-! ### cast -/
@[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by
@@ -1735,7 +1821,7 @@ theorem toInt_ushiftRight {x : BitVec w} {n : Nat} :
simp [hn]
@[simp]
theorem toFin_uShiftRight {x : BitVec w} {n : Nat} :
theorem toFin_ushiftRight {x : BitVec w} {n : Nat} :
(x >>> n).toFin = x.toFin / (Fin.ofNat' (2^w) (2^n)) := by
apply Fin.eq_of_val_eq
by_cases hn : n < w
@@ -1941,11 +2027,118 @@ theorem getMsbD_sshiftRight {x : BitVec w} {i n : Nat} :
by_cases h₄ : n + (w - 1 - i) < w <;> (simp only [h₄, reduceIte]; congr; omega)
· simp [h]
theorem toInt_shiftRight_lt {x : BitVec w} {n : Nat} :
x.toInt >>> n < 2 ^ (w - 1) := by
have := @Int.shiftRight_le_of_nonneg x.toInt n
have := @Int.shiftRight_le_of_nonpos x.toInt n
have := @BitVec.toInt_lt w x
have := @Nat.one_le_two_pow (w-1)
norm_cast at *
omega
theorem le_toInt_shiftRight {x : BitVec w} {n : Nat} :
-(2 ^ (w - 1)) x.toInt >>> n := by
have := @Int.le_shiftRight_of_nonpos x.toInt n
have := @Int.le_shiftRight_of_nonneg x.toInt n
have := @BitVec.le_toInt w x
have := @Nat.one_le_two_pow (w-1)
norm_cast at *
omega
theorem toNat_sshiftRight_of_msb_true {x : BitVec w} {n : Nat} (h : x.msb = true) :
(x.sshiftRight n).toNat = 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n := by
simp [sshiftRight_eq_of_msb_true, h]
theorem toNat_sshiftRight_of_msb_false {x : BitVec w} {n : Nat} (h : x.msb = false) :
(x.sshiftRight n).toNat = x.toNat >>> n := by
simp [sshiftRight_eq_of_msb_false, h]
theorem toNat_sshiftRight {x : BitVec w} {n : Nat} :
(x.sshiftRight n).toNat =
if x.msb
then 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n
else x.toNat >>> n := by
by_cases h : x.msb
· simp [toNat_sshiftRight_of_msb_true, h]
· rw [Bool.not_eq_true] at h
simp [toNat_sshiftRight_of_msb_false, h]
theorem toFin_sshiftRight_of_msb_true {x : BitVec w} {n : Nat} (h : x.msb = true) :
(x.sshiftRight n).toFin = Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n) := by
apply Fin.eq_of_val_eq
simp only [val_toFin, toNat_sshiftRight, h, reduceIte, Fin.val_ofNat']
rw [Nat.mod_eq_of_lt]
have := x.isLt
have ineq : y, 2 ^ w - 1 - y < 2 ^ w := by omega
exact ineq ((2 ^ w - 1 - x.toNat) >>> n)
theorem toFin_sshiftRight_of_msb_false {x : BitVec w} {n : Nat} (h : x.msb = false) :
(x.sshiftRight n).toFin = Fin.ofNat' (2^w) (x.toNat >>> n) := by
apply Fin.eq_of_val_eq
simp only [val_toFin, toNat_sshiftRight, h, Bool.false_eq_true, reduceIte, Fin.val_ofNat']
have := Nat.shiftRight_le x.toNat n
rw [Nat.mod_eq_of_lt (by omega)]
theorem toFin_sshiftRight {x : BitVec w} {n : Nat} :
(x.sshiftRight n).toFin =
if x.msb
then Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n)
else Fin.ofNat' (2^w) (x.toNat >>> n) := by
by_cases h : x.msb
· simp [toFin_sshiftRight_of_msb_true, h]
· simp [toFin_sshiftRight_of_msb_false, h]
@[simp]
theorem toInt_sshiftRight {x : BitVec w} {n : Nat} :
(x.sshiftRight n).toInt = x.toInt >>> n := by
by_cases h : w = 0
· subst h
simp [BitVec.eq_nil x]
· rw [sshiftRight, toInt_ofInt, Nat.two_pow_pred_add_two_pow_pred (by omega)]
have := @toInt_shiftRight_lt w x n
have := @le_toInt_shiftRight w x n
norm_cast at *
exact Int.bmod_eq_self_of_le (by omega) (by omega)
/-! ### sshiftRight reductions from BitVec to Nat -/
@[simp]
theorem sshiftRight_eq' (x : BitVec w) : x.sshiftRight' y = x.sshiftRight y.toNat := rfl
theorem toNat_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
(x.sshiftRight' y).toNat = 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat := by
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_true h]
theorem toNat_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
(x.sshiftRight' y).toNat = x.toNat >>> y.toNat := by
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_false h]
theorem toNat_sshiftRight' {x y : BitVec w} :
(x.sshiftRight' y).toNat =
if x.msb
then 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat
else x.toNat >>> y.toNat := by
rw [sshiftRight_eq', toNat_sshiftRight]
theorem toFin_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
(x.sshiftRight' y).toFin = Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat) := by
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_true h]
theorem toFin_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
(x.sshiftRight' y).toFin = Fin.ofNat' (2^w) (x.toNat >>> y.toNat) := by
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_false h]
theorem toFin_sshiftRight' {x y : BitVec w} :
(x.sshiftRight' y).toFin =
if x.msb
then Fin.ofNat' (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat)
else Fin.ofNat' (2^w) (x.toNat >>> y.toNat) := by
rw [sshiftRight_eq', toFin_sshiftRight]
theorem toInt_sshiftRight' {x y : BitVec w} :
(x.sshiftRight' y).toInt = x.toInt >>> y.toNat := by
rw [sshiftRight_eq', toInt_sshiftRight]
-- This should not be a `@[simp]` lemma as the left hand side is not in simp normal form.
theorem getLsbD_sshiftRight' {x y : BitVec w} {i : Nat} :
getLsbD (x.sshiftRight' y) i =
@@ -2036,14 +2229,18 @@ theorem msb_signExtend {x : BitVec w} :
· simp [h, BitVec.msb, getMsbD_signExtend, show ¬ (v - w = 0) by omega]
/-- Sign extending to a width smaller than the starting width is a truncation. -/
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v w):
theorem signExtend_eq_setWidth_of_le (x : BitVec w) {v : Nat} (hv : v w) :
x.signExtend v = x.setWidth v := by
ext i h
simp [getElem_signExtend, show i < w by omega]
@[deprecated signExtend_eq_setWidth_of_le (since := "2025-03-07")]
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v w) :
x.signExtend v = x.setWidth v := signExtend_eq_setWidth_of_le x hv
/-- Sign extending to the same bitwidth is a no op. -/
theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_setWidth_of_lt _ (Nat.le_refl _), setWidth_eq]
@[simp] theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_setWidth_of_le _ (Nat.le_refl _), setWidth_eq]
/-- Sign extending to a larger bitwidth depends on the msb.
If the msb is false, then the result equals the original value.
@@ -2080,47 +2277,65 @@ theorem toNat_signExtend (x : BitVec w) {v : Nat} :
(x.signExtend v).toNat = (x.setWidth v).toNat + if x.msb then 2^v - 2^w else 0 := by
by_cases h : v w
· have : 2^v 2^w := Nat.pow_le_pow_right Nat.two_pos h
simp [signExtend_eq_setWidth_of_lt x h, toNat_setWidth, Nat.sub_eq_zero_of_le this]
simp [signExtend_eq_setWidth_of_le x h, toNat_setWidth, Nat.sub_eq_zero_of_le this]
· have : 2^w 2^v := Nat.pow_le_pow_right Nat.two_pos (by omega)
rw [toNat_signExtend_of_le x (by omega), toNat_setWidth, Nat.mod_eq_of_lt (by omega)]
/-
/--
If the current width `w` is smaller than the extended width `v`,
then the value when interpreted as an integer does not change.
-/
theorem toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
theorem toInt_signExtend_of_le {x : BitVec w} (h : w v) :
(x.signExtend v).toInt = x.toInt := by
simp only [toInt_eq_msb_cond, toNat_signExtend]
have : (x.signExtend v).msb = x.msb := by
rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
by_cases hlt : w < v
· rw [toInt_signExtend_of_lt hlt]
· obtain rfl : w = v := by omega
simp
where
toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
(x.signExtend v).toInt = x.toInt := by
simp only [toInt_eq_msb_cond, toNat_signExtend]
have : (x.signExtend v).msb = x.msb := by
rw [msb_eq_getLsbD_last, getLsbD_eq_getElem (Nat.sub_one_lt_of_lt hv)]
simp [getElem_signExtend, Nat.le_sub_one_of_lt hv]
omega
have H : 2^w 2^v := Nat.pow_le_pow_right (by omega) (by omega)
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
omega
have H : 2^w 2^v := Nat.pow_le_pow_right (by omega) (by omega)
simp only [this, toNat_setWidth, Int.natCast_add, Int.ofNat_emod, Int.natCast_mul]
by_cases h : x.msb
<;> norm_cast
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H)]
omega
/-
/--
If the current width `w` is larger than the extended width `v`,
then the value when interpreted as an integer is truncated,
and we compute a modulo by `2^v`.
-/
theorem toInt_signExtend_of_le {x : BitVec w} (hv : v w) :
theorem toInt_signExtend_eq_toNat_bmod_of_le {x : BitVec w} (hv : v w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2^v) := by
simp [signExtend_eq_setWidth_of_lt _ hv]
simp [signExtend_eq_setWidth_of_le _ hv]
/-
/--
Interpreting the sign extension of `(x : BitVec w)` to width `v`
computes `x % 2^v` (where `%` is the balanced mod).
computes `x % 2^v` (where `%` is the balanced mod). See `toInt_signExtend` for a version stated
in terms of `toInt` instead of `toNat`.
-/
theorem toInt_signExtend (x : BitVec w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2^(min v w)) := by
theorem toInt_signExtend_eq_toNat_bmod (x : BitVec w) :
(x.signExtend v).toInt = Int.bmod x.toNat (2 ^ min v w) := by
by_cases hv : v w
· simp [toInt_signExtend_of_le hv, Nat.min_eq_left hv]
· simp [toInt_signExtend_eq_toNat_bmod_of_le hv, Nat.min_eq_left hv]
· simp only [Nat.not_le] at hv
rw [toInt_signExtend_of_lt hv, Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
rw [toInt_signExtend_of_le (Nat.le_of_lt hv),
Nat.min_eq_right (by omega), toInt_eq_toNat_bmod]
theorem toInt_signExtend (x : BitVec w) :
(x.signExtend v).toInt = x.toInt.bmod (2 ^ min v w) := by
rw [toInt_signExtend_eq_toNat_bmod, BitVec.toInt_eq_toNat_bmod, Int.bmod_bmod_of_dvd]
exact Nat.pow_dvd_pow _ (Nat.min_le_right v w)
theorem toInt_signExtend_eq_toInt_bmod_of_le (x : BitVec w) (h : v w) :
(x.signExtend v).toInt = x.toInt.bmod (2 ^ v) := by
rw [BitVec.toInt_signExtend, Nat.min_eq_left h]
/-! ### append -/
@@ -2265,6 +2480,42 @@ theorem msb_shiftLeft {x : BitVec w} {n : Nat} :
(x <<< n).msb = x.getMsbD n := by
simp [BitVec.msb]
/--
A `(x : BitVec v)` set to width `w` equals `(v - w)` zeros,
followed by the low `(min v w) bits of `x`
-/
theorem setWidth_eq_append_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} :
x.setWidth w = ((0#(w - v)) ++ x.extractLsb' 0 (min v w)).cast (by omega) := by
ext i hi
simp only [getElem_cast, getElem_append]
by_cases hiv : i < v
· simp [hi]
omega
· simp [getLsbD_ge x i (by omega)]
/--
A `(x : BitVec v)` set to a width `w ≥ v` equals `(w - v)` zeros, followed by `x`.
-/
theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v w) :
x.setWidth w = ((0#(w - v)) ++ x).cast (by omega) := by
rw [setWidth_eq_append_extractLsb']
ext i hi
simp only [getElem_cast, getElem_append]
by_cases hiv : i < v
· simp [hiv]
omega
· simp [hiv, getLsbD_ge x i (by omega)]
theorem setWidth_eq_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} (h : w v) :
x.setWidth w = x.extractLsb' 0 w := by
rw [setWidth_eq_append_extractLsb']
ext i hi
simp only [getElem_cast, getElem_append]
by_cases hiv : i < v
· simp [hi]
omega
· simp [getLsbD_ge x i (by omega)]
theorem ushiftRight_eq_extractLsb'_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
x >>> n = ((0#n) ++ (x.extractLsb' n (w - n))).cast (by omega) := by
ext i hi
@@ -2282,6 +2533,120 @@ theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
· simp [hi']
· simp [hi', show i - n < w by omega]
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
(x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
ext i h
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
Nat.not_lt]
intros hi
congr 1
omega
/-- Combine adjacent `~~~ (extractLsb _)'` operations into a single `~~~ (extractLsb _)'`. -/
theorem not_extractLsb'_append_not_extractLsb'_eq_not_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
(~~~ (x.extractLsb' start₂ len₂) ++ ~~~ (x.extractLsb' start₁ len₁)) =
(~~~ x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
ext i h
simp only [getElem_cast, getElem_not, getElem_extractLsb', getElem_append]
by_cases hi : i < len₁
· simp [hi]
· simp only [hi, reduceDIte, Bool.not_eq_eq_eq_not, Bool.not_not]
congr 1
omega
/-- A sign extension of `x : BitVec w` equals high bits of either `0` or `1` depending on `x.msb`,
followed by the low bits of `x`. -/
theorem signExtend_eq_append_extractLsb' {w v : Nat} {x : BitVec w} :
x.signExtend v =
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x.extractLsb' 0 (min w v)).cast (by omega) := by
ext i hi
simp only [getElem_cast]
cases hx : x.msb
· simp only [hx, signExtend_eq_setWidth_of_msb_false, getElem_setWidth, Bool.false_eq_true,
reduceIte, getElem_append, getElem_extractLsb', Nat.zero_add, getElem_zero, dite_eq_ite,
Bool.if_false_right, Bool.iff_and_self, decide_eq_true_eq]
intros hi
have hw : i < w := lt_of_getLsbD hi
omega
· simp [signExtend_eq_not_setWidth_not_of_msb_true hx, getElem_append, Nat.lt_min, hi]
/-- A sign extension of `x : BitVec w` to a larger bitwidth `v ≥ w`
equals high bits of either `0` or `1` depending on `x.msb`, followed by `x`. -/
theorem signExtend_eq_append_of_le {w v : Nat} {x : BitVec w} (h : w v) :
x.signExtend v =
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x).cast (by omega) := by
ext i hi
cases hx : x.msb <;>
simp [getElem_cast, hx, getElem_append, getElem_signExtend]
/--
The 'master theorem' for extracting bits from `(xhi ++ xlo)`,
which performs a case analysis on the start index, length, and the lengths of `xlo, xhi`.
· If the start index is entirely out of the `xlo` bitvector, then grab the bits from `xhi`.
· If the start index is entirely contained in the `xlo` bitvector, then grab the bits from `xlo`.
· If the start index is split between the two bitvectors,
then append `(w - start)` bits from `xlo` with `(len - (w - start))` bits from xhi.
Diagramatically:
```
xhi xlo
(<---------------------](<-------w--------]
start+len..start: (<-----len---*------]
w - start: *------*
len - (w -start): *------------*
```
-/
theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start len : Nat} :
extractLsb' start len (xhi ++ xlo) =
if hstart : start < w
then
if hlen : start + len < w
then extractLsb' start len xlo
else
(((extractLsb' (start - w) (len - (w - start)) xhi) ++
extractLsb' start (w - start) xlo)).cast (by omega)
else
extractLsb' (start - w) len xhi := by
by_cases hstart : start < w
· simp only [hstart, reduceDIte]
by_cases hlen : start + len < w
· simp only [hlen, reduceDIte]
ext i hi
simp only [getElem_extractLsb', getLsbD_append, ite_eq_left_iff, Nat.not_lt]
intros hcontra
omega
· simp only [hlen, reduceDIte]
ext i hi
simp only [getElem_extractLsb', getLsbD_append, getElem_cast,
getElem_append, dite_eq_ite]
by_cases hi₂ : start + i < w
· simp [hi₂, show i < min len w by omega, show i < w - start by omega]
· simp [hi₂, reduceIte, show ¬i < w - start by omega,
show start + i - w = start - w + (i - (w - start)) by omega]
· simp only [hstart, reduceDIte]
ext i hi
simp [getElem_extractLsb', getLsbD_append,
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
the bits from `xlo` when `start + len` is within `xlo`.
-/
theorem extractLsb'_append_eq_of_lt {v w} {xhi : BitVec v} {xlo : BitVec w}
{start len : Nat} (h : start + len < w) :
extractLsb' start len (xhi ++ xlo) = extractLsb' start len xlo := by
simp [extractLsb'_append_eq_ite, h]
omega
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
the bits from `xhi` when `start` is outside `xlo`.
-/
theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
{start len : Nat} (h : w start) :
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
simp [extractLsb'_append_eq_ite, h, show ¬ start < w by omega]
/-! ### rev -/
theorem getLsbD_rev (x : BitVec w) (i : Fin w) :
@@ -2693,6 +3058,9 @@ theorem toInt_neg {x : BitVec w} :
rw [ BitVec.zero_sub, toInt_sub]
simp [BitVec.toInt_ofNat]
theorem ofInt_neg {w : Nat} {n : Int} : BitVec.ofInt w (-n) = -BitVec.ofInt w n :=
eq_of_toInt_eq (by simp [toInt_neg])
@[simp] theorem toFin_neg (x : BitVec n) :
(-x).toFin = Fin.ofNat' (2^n) (2^n - x.toNat) :=
rfl
@@ -2792,6 +3160,14 @@ theorem not_neg (x : BitVec w) : ~~~(-x) = x + -1#w := by
show (_ - x.toNat) % _ = _ by rw [Nat.mod_eq_of_lt (by omega)]]
omega
theorem neg_add {x y : BitVec w} : - (x + y) = - x - y := by
apply eq_of_toInt_eq
simp [toInt_neg, toInt_add, Int.neg_add, Int.add_neg_eq_sub]
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]
/- ### add/sub injectivity -/
@[simp]
@@ -2960,6 +3336,12 @@ 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 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]
/-! ### le and lt -/
@[bitvec_to_nat] theorem le_def {x y : BitVec n} :
@@ -3064,6 +3446,12 @@ theorem allOnes_le_iff {x : BitVec w} : allOnes w ≤ x ↔ x = allOnes w := by
exact Eq.symm (BitVec.le_antisymm h this)
· simp_all
@[simp]
theorem lt_allOnes_iff {x : BitVec w} : x < allOnes w x allOnes w := by
have := not_congr (@allOnes_le_iff w x)
rw [BitVec.not_le] at this
exact this
/-! ### udiv -/
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
@@ -3151,6 +3539,7 @@ then `x / y` is nonnegative, thus `toInt` and `toNat` coincide.
theorem toInt_udiv_of_msb {x : BitVec w} (h : x.msb = false) (y : BitVec w) :
(x / y).toInt = x.toNat / y.toNat := by
simp [toInt_eq_msb_cond, msb_udiv_eq_false_of h]
norm_cast
/-! ### umod -/
@@ -3950,7 +4339,6 @@ theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
/--
The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `... % 2 ^ w`.
-/
@[simp]
theorem toInt_intMin {w : Nat} :
(intMin w).toInt = -((2 ^ (w - 1) % 2 ^ w) : Nat) := by
by_cases h : w = 0
@@ -3962,10 +4350,16 @@ theorem toInt_intMin {w : Nat} :
rw [Nat.mul_comm]
simp [w_pos]
theorem toInt_intMin_of_pos {v : Nat} (hv : 0 < v) : (intMin v).toInt = -2 ^ (v - 1) := by
rw [toInt_intMin, Nat.mod_eq_of_lt]
· simp [Int.natCast_pow]
· rw [Nat.pow_lt_pow_iff_right (by omega)]
omega
theorem toInt_intMin_le (x : BitVec w) :
(intMin w).toInt x.toInt := by
cases w
case zero => simp [@of_length_zero x]
case zero => simp [toInt_intMin, @of_length_zero x]
case succ w =>
simp only [toInt_intMin, Nat.add_one_sub_one, Int.ofNat_emod]
have : 0 < 2 ^ w := Nat.two_pow_pos w
@@ -4109,9 +4503,7 @@ theorem sub_le_sub_iff_le {x y z : BitVec w} (hxz : z ≤ x) (hyz : z ≤ y) :
theorem msb_eq_toInt {x : BitVec w}:
x.msb = decide (x.toInt < 0) := by
by_cases h : x.msb <;>
· simp [h, toInt_eq_msb_cond]
omega
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond] <;> omega
theorem msb_eq_toNat {x : BitVec w}:
x.msb = decide (x.toNat 2 ^ (w - 1)) := by
@@ -4346,6 +4738,9 @@ instance instDecidableExistsBitVec :
set_option linter.missingDocs false
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
abbrev toFin_uShiftRight := @toFin_ushiftRight
@[deprecated signExtend_eq_setWidth_of_msb_false (since := "2024-12-08")]
abbrev signExtend_eq_not_setWidth_not_of_msb_false := @signExtend_eq_setWidth_of_msb_false
@@ -4458,7 +4853,7 @@ abbrev signExtend_eq_not_zeroExtend_not_of_msb_false := @signExtend_eq_setWidth
abbrev signExtend_eq_not_zeroExtend_not_of_msb_true := @signExtend_eq_not_setWidth_not_of_msb_true
@[deprecated signExtend_eq_setWidth_of_lt (since := "2024-09-18")]
abbrev signExtend_eq_truncate_of_lt := @signExtend_eq_setWidth_of_lt
abbrev signExtend_eq_truncate_of_lt := @signExtend_eq_setWidth_of_le
@[deprecated truncate_append (since := "2024-09-18")]
abbrev truncate_append := @setWidth_append

View File

@@ -9,7 +9,19 @@ import Init.NotationExtra
namespace Bool
/-- Boolean exclusive or -/
/--
Boolean “exclusive or”. `xor x y` can be written `x ^^ y`.
`x ^^ y` is `true` when precisely one of `x` or `y` is `true`. Unlike `and` and `or`, it does not
have short-circuiting behavior, because one argument's value never determines the final value. Also
unlike `and` and `or`, there is no commonly-used corresponding propositional connective.
Examples:
* `false ^^ false = false`
* `true ^^ false = true`
* `false ^^ true = true`
* `true ^^ true = false`
-/
abbrev xor : Bool Bool Bool := bne
@[inherit_doc] infixl:33 " ^^ " => xor
@@ -367,7 +379,9 @@ theorem and_or_inj_left_iff :
/-! ## toNat -/
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
/--
Converts `true` to `1` and `false` to `0`.
-/
def toNat (b : Bool) : Nat := cond b 1 0
@[simp, bitvec_to_nat] theorem toNat_false : false.toNat = 0 := rfl
@@ -388,7 +402,9 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
/-! ## toInt -/
/-- convert a `Bool` to an `Int`, `false -> 0`, `true -> 1` -/
/--
Converts `true` to `1` and `false` to `0`.
-/
def toInt (b : Bool) : Int := cond b 1 0
@[simp] theorem toInt_false : false.toInt = 0 := rfl
@@ -539,8 +555,8 @@ theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
@[simp] theorem cond_eq_false_distrib : (c t f : Bool),
(cond c t f = false) = ite (c = true) (t = false) (f = false) := by decide
protected theorem cond_true {α : Type u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := cond_false a b
protected theorem cond_true {α : Sort u} {a b : α} : cond true a b = a := cond_true a b
protected theorem cond_false {α : Sort u} {a b : α} : cond false a b = b := cond_false a b
@[simp] theorem cond_true_left : (c f : Bool), cond c true f = ( c || f) := by decide
@[simp] theorem cond_false_left : (c f : Bool), cond c false f = (!c && f) := by decide

View File

@@ -18,10 +18,13 @@ attribute [extern "lean_byte_array_data"] ByteArray.data
namespace ByteArray
@[extern "lean_mk_empty_byte_array"]
def mkEmpty (c : @& Nat) : ByteArray :=
def emptyWithCapacity (c : @& Nat) : ByteArray :=
{ data := #[] }
def empty : ByteArray := mkEmpty 0
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
def empty : ByteArray := emptyWithCapacity 0
instance : Inhabited ByteArray where
default := empty
@@ -334,6 +337,9 @@ def prevn : Iterator → Nat → Iterator
end Iterator
end ByteArray
/--
Converts a list of bytes into a `ByteArray`.
-/
def List.toByteArray (bs : List UInt8) : ByteArray :=
let rec loop
| [], r => r

View File

@@ -15,7 +15,15 @@ Note that values in `[0xd800, 0xdfff]` are reserved for [UTF-16 surrogate pairs]
namespace Char
/--
One character is less than another if its code point is strictly less than the other's.
-/
protected def lt (a b : Char) : Prop := a.val < b.val
/--
One character is less than or equal to another if its code point is less than or equal to the
other's.
-/
protected def le (a b : Char) : Prop := a.val b.val
instance : LT Char := Char.lt
@@ -27,7 +35,10 @@ instance (a b : Char) : Decidable (a < b) :=
instance (a b : Char) : Decidable (a b) :=
UInt32.decLe _ _
/-- Determines if the given nat is a valid [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value).-/
/--
True for natural numbers that are valid [Unicode scalar
values](https://www.unicode.org/glossary/#unicode_scalar_value).
-/
abbrev isValidCharNat (n : Nat) : Prop :=
n < 0xd800 (0xdfff < n n < 0x110000)
@@ -50,55 +61,93 @@ theorem isValidChar_of_isValidCharNat (n : Nat) (h : isValidCharNat n) : isValid
theorem isValidChar_zero : isValidChar 0 :=
Or.inl (by decide)
/-- Underlying unicode code point as a `Nat`. -/
/--
The character's Unicode code point as a `Nat`.
-/
@[inline] def toNat (c : Char) : Nat :=
c.val.toNat
/-- Convert a character into a `UInt8`, by truncating (reducing modulo 256) if necessary. -/
/--
Converts a character into a `UInt8` that contains its code point.
If the code point is larger than 255, it is truncated (reduced modulo 256).
-/
@[inline] def toUInt8 (c : Char) : UInt8 :=
c.val.toUInt8
/-- The numbers from 0 to 256 are all valid UTF-8 characters, so we can embed one in the other. -/
/--
Converts an 8-bit unsigned integer into a character.
The integer's value is interpreted as a Unicode code point.
-/
def ofUInt8 (n : UInt8) : Char := n.toUInt32, .inl (Nat.lt_trans n.toBitVec.isLt (by decide))
instance : Inhabited Char where
default := 'A'
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
/--
Returns `true` if the character is a space `(' ', U+0020)`, a tab `('\t', U+0009)`, a carriage
return `('\r', U+000D)`, or a newline `('\n', U+000A)`.
-/
@[inline] def isWhitespace (c : Char) : Bool :=
c = ' ' || c = '\t' || c = '\r' || c = '\n'
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
/--
Returns `true` if the character is a uppercase ASCII letter.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
-/
@[inline] def isUpper (c : Char) : Bool :=
c.val 65 && c.val 90
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
/--
Returns `true` if the character is a lowercase ASCII letter.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
-/
@[inline] def isLower (c : Char) : Bool :=
c.val 97 && c.val 122
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
/--
Returns `true` if the character is an ASCII letter.
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
-/
@[inline] def isAlpha (c : Char) : Bool :=
c.isUpper || c.isLower
/-- Is the character in `0123456789`? -/
/--
Returns `true` if the character is an ASCII digit.
The ASCII digits are the following: `0123456789`.
-/
@[inline] def isDigit (c : Char) : Bool :=
c.val 48 && c.val 57
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
/--
Returns `true` if the character is an ASCII letter or digit.
The ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.
The ASCII digits are the following: `0123456789`.
-/
@[inline] def isAlphanum (c : Char) : Bool :=
c.isAlpha || c.isDigit
/-- Convert an upper case character to its lower case character.
/--
Converts an uppercase ASCII letter to the corresponding lowercase letter. Letters outside the ASCII
alphabet are returned unchanged.
Only works on basic latin letters.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
-/
def toLower (c : Char) : Char :=
let n := toNat c;
if n >= 65 n <= 90 then ofNat (n + 32) else c
/-- Convert a lower case character to its upper case character.
/--
Converts a lowercase ASCII letter to the corresponding uppercase letter. Letters outside the ASCII
alphabet are returned unchanged.
Only works on basic latin letters.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
-/
def toUpper (c : Char) : Char :=
let n := toNat c;

View File

@@ -14,22 +14,23 @@ instance coeToNat : CoeOut (Fin n) Nat :=
fun v => v.val
/--
From the empty type `Fin 0`, any desired result `α` can be derived. This is similar to `Empty.elim`.
The type `Fin 0` is uninhabited, so it can be used to derive any result whatsoever.
This is similar to `Empty.elim`. It can be thought of as a compiler-checked assertion that a code
path is unreachable, or a logical contradiction from which `False` and thus anything else could be
derived.
-/
def elim0.{u} {α : Sort u} : Fin 0 α
| _, h => absurd h (not_lt_zero _)
/--
Returns the successor of the argument.
The successor, with an increased bound.
The bound in the result type is increased:
```
(2 : Fin 3).succ = (3 : Fin 4)
```
This differs from addition, which wraps around:
```
(2 : Fin 3) + 1 = (0 : Fin 3)
```
This differs from adding `1`, which instead wraps around.
Examples:
* `(2 : Fin 3).succ = (3 : Fin 4)`
* `(2 : Fin 3) + 1 = (0 : Fin 3)`
-/
def succ : Fin n Fin (n + 1)
| i, h => i+1, Nat.succ_lt_succ h
@@ -53,7 +54,13 @@ protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
-- `i.toNat` to `i.val`.
@[inline, inherit_doc val]
/--
Extracts the underlying `Nat` value.
This function is a synonym for `Fin.val`, which is the simp normal form. `Fin.val` is also a
coercion, so values of type `Fin n` are automatically converted to `Nat`s as needed.
-/
@[inline]
protected def toNat (i : Fin n) : Nat :=
i.val
@@ -65,15 +72,34 @@ private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
have : n > 0 := Nat.lt_trans (Nat.zero_lt_succ _) h;
Nat.mod_lt _ this
/-- Addition modulo `n` -/
/--
Addition modulo `n`, usually invoked via the `+` operator.
Examples:
* `(2 : Fin 8) + (2 : Fin 8) = (4 : Fin 8)`
* `(2 : Fin 3) + (2 : Fin 3) = (1 : Fin 3)`
-/
protected def add : Fin n Fin n Fin n
| a, h, b, _ => (a + b) % n, mlt h
/-- Multiplication modulo `n` -/
/--
Multiplication modulo `n`, usually invoked via the `*` operator.
Examples:
* `(2 : Fin 10) * (2 : Fin 10) = (4 : Fin 10)`
* `(2 : Fin 10) * (7 : Fin 10) = (4 : Fin 10)`
* `(3 : Fin 10) * (7 : Fin 10) = (1 : Fin 10)`
-/
protected def mul : Fin n Fin n Fin n
| a, h, b, _ => (a * b) % n, mlt h
/-- Subtraction modulo `n` -/
/--
Subtraction modulo `n`, usually invoked via the `-` operator.
Examples:
* `(5 : Fin 11) - (3 : Fin 11) = (2 : Fin 11)`
* `(3 : Fin 11) - (5 : Fin 11) = (9 : Fin 11)`
-/
protected def sub : Fin n Fin n Fin n
/-
The definition of `Fin.sub` has been updated to improve performance.
@@ -100,27 +126,76 @@ we are trying to minimize the number of Nat theorems
needed to bootstrap Lean.
-/
/--
Modulus of bounded numbers, usually invoked via the `%` operator.
The resulting value is that computed by the `%` operator on `Nat`.
-/
protected def mod : Fin n Fin n Fin n
| a, h, b, _ => a % b, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h
/--
Division of bounded numbers, usually invoked via the `/` operator.
The resulting value is that computed by the `/` operator on `Nat`. In particular, the result of
division by `0` is `0`.
Examples:
* `(5 : Fin 10) / (2 : Fin 10) = (2 : Fin 10)`
* `(5 : Fin 10) / (0 : Fin 10) = (0 : Fin 10)`
* `(5 : Fin 10) / (7 : Fin 10) = (0 : Fin 10)`
-/
protected def div : Fin n Fin n Fin n
| a, h, b, _ => a / b, Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h
/--
Modulus of bounded numbers with respect to a `Nat`.
The resulting value is that computed by the `%` operator on `Nat`.
-/
def modn : Fin n Nat Fin n
| a, h, m => a % m, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h
/--
Bitwise and.
-/
def land : Fin n Fin n Fin n
| a, h, b, _ => (Nat.land a b) % n, mlt h
/--
Bitwise or.
-/
def lor : Fin n Fin n Fin n
| a, h, b, _ => (Nat.lor a b) % n, mlt h
/--
Bitwise xor (“exclusive or”).
-/
def xor : Fin n Fin n Fin n
| a, h, b, _ => (Nat.xor a b) % n, mlt h
/--
Bitwise left shift of bounded numbers, with wraparound on overflow.
Examples:
* `(1 : Fin 10) <<< (1 : Fin 10) = (2 : Fin 10)`
* `(1 : Fin 10) <<< (3 : Fin 10) = (8 : Fin 10)`
* `(1 : Fin 10) <<< (4 : Fin 10) = (6 : Fin 10)`
-/
def shiftLeft : Fin n Fin n Fin n
| a, h, b, _ => (a <<< b) % n, mlt h
/--
Bitwise right shift of bounded numbers.
This operator corresponds to logical rather than arithmetic bit shifting. The new bits are always
`0`.
Examples:
* `(15 : Fin 16) >>> (1 : Fin 16) = (7 : Fin 16)`
* `(15 : Fin 16) >>> (2 : Fin 16) = (3 : Fin 16)`
* `(15 : Fin 17) >>> (2 : Fin 17) = (3 : Fin 17)`
-/
def shiftRight : Fin n Fin n Fin n
| a, h, b, _ => (a >>> b) % n, mlt h
@@ -174,39 +249,125 @@ theorem val_lt_of_le (i : Fin b) (h : b ≤ n) : i.val < n :=
protected theorem pos (i : Fin n) : 0 < n :=
Nat.lt_of_le_of_lt (Nat.zero_le _) i.2
/-- The greatest value of `Fin (n+1)`. -/
/--
The greatest value of `Fin (n+1)`, namely `n`.
Examples:
* `Fin.last 4 = (4 : Fin 5)`
* `(Fin.last 0).val = (0 : Nat)`
-/
@[inline] def last (n : Nat) : Fin (n + 1) := n, n.lt_succ_self
/-- `castLT i h` embeds `i` into a `Fin` where `h` proves it belongs into. -/
/--
Replaces the bound with another that is suitable for the value.
The proof embedded in `i` can be used to cast to a larger bound even if the concrete value is not
known.
Examples:
```lean example
example : Fin 12 := (7 : Fin 10).castLT (by decide : 7 < 12)
```
```lean example
example (i : Fin 10) : Fin 12 :=
i.castLT <| by
cases i; simp; omega
```
-/
@[inline] def castLT (i : Fin m) (h : i.1 < n) : Fin n := i.1, h
/-- `castLE h i` embeds `i` into a larger `Fin` type. -/
/--
Coarsens a bound to one at least as large.
See also `Fin.castAdd` for a version that represents the larger bound with addition rather than an
explicit inequality proof.
-/
@[inline] def castLE (h : n m) (i : Fin n) : Fin m := i, Nat.lt_of_lt_of_le i.2 h
/-- `cast eq i` embeds `i` into an equal `Fin` type. -/
/--
Uses a proof that two bounds are equal to allow a value bounded by one to be used with the other.
In other words, when `eq : n = m`, `Fin.cast eq i` converts `i : Fin n` into a `Fin m`.
-/
@[inline] protected def cast (eq : n = m) (i : Fin n) : Fin m := i, eq i.2
/-- `castAdd m i` embeds `i : Fin n` in `Fin (n+m)`. See also `Fin.natAdd` and `Fin.addNat`. -/
/--
Coarsens a bound to one at least as large.
See also `Fin.natAdd` and `Fin.addNat` for addition functions that increase the bound, and
`Fin.castLE` for a version that uses an explicit inequality proof.
-/
@[inline] def castAdd (m) : Fin n Fin (n + m) :=
castLE <| Nat.le_add_right n m
/-- `castSucc i` embeds `i : Fin n` in `Fin (n+1)`. -/
/--
Coarsens a bound by one.
-/
@[inline] def castSucc : Fin n Fin (n + 1) := castAdd 1
/-- `addNat m i` adds `m` to `i`, generalizes `Fin.succ`. -/
/--
Adds a natural number to a `Fin`, increasing the bound.
This is a generalization of `Fin.succ`.
`Fin.natAdd` is a version of this function that takes its `Nat` parameter first.
Examples:
* `Fin.addNat (5 : Fin 8) 3 = (8 : Fin 11)`
* `Fin.addNat (0 : Fin 8) 1 = (1 : Fin 9)`
* `Fin.addNat (1 : Fin 8) 2 = (3 : Fin 10)`
-/
def addNat (i : Fin n) (m) : Fin (n + m) := i + m, Nat.add_lt_add_right i.2 _
/-- `natAdd n i` adds `n` to `i` "on the left". -/
/--
Adds a natural number to a `Fin`, increasing the bound.
This is a generalization of `Fin.succ`.
`Fin.addNat` is a version of this function that takes its `Nat` parameter second.
Examples:
* `Fin.natAdd 3 (5 : Fin 8) = (8 : Fin 11)`
* `Fin.natAdd 1 (0 : Fin 8) = (1 : Fin 9)`
* `Fin.natAdd 1 (2 : Fin 8) = (3 : Fin 9)`
-/
def natAdd (n) (i : Fin m) : Fin (n + m) := n + i, Nat.add_lt_add_left i.2 _
/-- Maps `0` to `n-1`, `1` to `n-2`, ..., `n-1` to `0`. -/
/--
Replaces a value with its difference from the largest value in the type.
Considering the values of `Fin n` as a sequence `0`, `1`, …, `n-2`, `n-1`, `Fin.rev` finds the
corresponding element of the reversed sequence. In other words, it maps `0` to `n-1`, `1` to `n-2`,
..., and `n-1` to `0`.
Examples:
* `(5 : Fin 6).rev = (0 : Fin 6)`
* `(0 : Fin 6).rev = (5 : Fin 6)`
* `(2 : Fin 5).rev = (2 : Fin 5)`
-/
@[inline] def rev (i : Fin n) : Fin n := n - (i + 1), Nat.sub_lt i.pos (Nat.succ_pos _)
/-- `subNat i h` subtracts `m` from `i`, generalizes `Fin.pred`. -/
/--
Subtraction of a natural number from a `Fin`, with the bound narrowed.
This is a generalization of `Fin.pred`. It is guaranteed to not underflow or wrap around.
Examples:
* `(5 : Fin 9).subNat 2 (by decide) = (3 : Fin 7)`
* `(5 : Fin 9).subNat 0 (by decide) = (5 : Fin 9)`
* `(3 : Fin 9).subNat 3 (by decide) = (0 : Fin 6)`
-/
@[inline] def subNat (m) (i : Fin (n + m)) (h : m i) : Fin n :=
i - m, Nat.sub_lt_right_of_lt_add h i.2
/-- Predecessor of a nonzero element of `Fin (n+1)`. -/
/--
The predecessor of a non-zero element of `Fin (n+1)`, with the bound decreased.
Examples:
* `(4 : Fin 8).pred (by decide) = (3 : Fin 7)`
* `(1 : Fin 2).pred (by decide) = (0 : Fin 1)`
-/
@[inline] def pred {n : Nat} (i : Fin (n + 1)) (h : i 0) : Fin n :=
subNat 1 i <| Nat.pos_of_ne_zero <| mt (Fin.eq_of_val_eq (j := 0)) h

View File

@@ -10,14 +10,26 @@ import Init.Data.Fin.Lemmas
namespace Fin
/-- Folds over `Fin n` from the left: `foldl 3 f x = f (f (f x 0) 1) 2`. -/
/--
Combine all the values that can be represented by `Fin n` with an initial value, starting at `0` and
nesting to the left.
Example:
* `Fin.foldl 3 (· + ·.val) (0 : Nat) = ((0 + (0 : Fin 3).val) + (1 : Fin 3).val) + (2 : Fin 3).val`
-/
@[inline] def foldl (n) (f : α Fin n α) (init : α) : α := loop init 0 where
/-- Inner loop for `Fin.foldl`. `Fin.foldl.loop n f x i = f (f (f x i) ...) (n-1)` -/
@[semireducible, specialize] loop (x : α) (i : Nat) : α :=
if h : i < n then loop (f x i, h) (i+1) else x
termination_by n - i
/-- Folds over `Fin n` from the right: `foldr 3 f x = f 0 (f 1 (f 2 x))`. -/
/--
Combine all the values that can be represented by `Fin n` with an initial value, starting at `n - 1`
and nesting to the right.
Example:
* `Fin.foldr 3 (·.val + ·) (0 : Nat) = (0 : Fin 3).val + ((1 : Fin 3).val + ((2 : Fin 3).val + 0))`
-/
@[inline] def foldr (n) (f : Fin n α α) (init : α) : α := loop n (Nat.le_refl n) init where
/-- Inner loop for `Fin.foldr`. `Fin.foldr.loop n f i x = f 0 (f ... (f (i-1) x))` -/
@[specialize] loop : (i : _) i n α α
@@ -26,7 +38,9 @@ namespace Fin
termination_by structural i => i
/--
Folds a monadic function over `Fin n` from left to right:
Folds a monadic function over all the values in `Fin n` from left to right, starting with `0`.
It is the sequence of steps:
```
Fin.foldlM n f x₀ = do
let x₁ ← f x₀ 0
@@ -53,7 +67,9 @@ Fin.foldlM n f x₀ = do
decreasing_by decreasing_trivial_pre_omega
/--
Folds a monadic function over `Fin n` from right to left:
Folds a monadic function over `Fin n` from right to left, starting with `n-1`.
It is the sequence of steps:
```
Fin.foldrM n f xₙ = do
let xₙ₋₁ ← f (n-1) xₙ

View File

@@ -10,10 +10,18 @@ import Init.Data.Fin.Basic
namespace Fin
/--
`hIterateFrom f i bnd a` applies `f` over indices `[i:n]` to compute `P n`
from `P i`.
Applies an index-dependent function `f` to all of the values in `[i:n]`, starting at `i` with an
initial accumulator `a`.
See `hIterate` below for more details.
Concretely, `Fin.hIterateFrom P f i a` is equal to
```lean
a |> f i |> f (i + 1) |> ... |> f (n - 1)
```
Theorems about `Fin.hIterateFrom` can be proven using the general theorem `Fin.hIterateFrom_elim` or
other more specialized theorems.
`Fin.hIterate` is a variant that always starts at `0`.
-/
def hIterateFrom (P : Nat Sort _) {n} (f : (i : Fin n), P i.val P (i.val+1))
(i : Nat) (ubnd : i n) (a : P i) : P n :=
@@ -26,20 +34,18 @@ def hIterateFrom (P : Nat → Sort _) {n} (f : ∀(i : Fin n), P i.val → P (i.
decreasing_by decreasing_trivial_pre_omega
/--
`hIterate` is a heterogeneous iterative operation that applies a
index-dependent function `f` to a value `init : P start` a total of
`stop - start` times to produce a value of type `P stop`.
Applies an index-dependent function to all the values less than the given bound `n`, starting at
`0` with an accumulator.
Concretely, `hIterate start stop f init` is equal to
Concretely, `Fin.hIterate P init f` is equal to
```lean
init |> f start _ |> f (start+1) _ ... |> f (end-1) _
init |> f 0 |> f 1 |> ... |> f (n-1)
```
Because it is heterogeneous and must return a value of type `P stop`,
`hIterate` requires proof that `start ≤ stop`.
Theorems about `Fin.hIterate` can be proven using the general theorem `Fin.hIterate_elim` or other more
specialized theorems.
One can prove properties of `hIterate` using the general theorem
`hIterate_elim` or other more specialized theorems.
`Fin.hIterateFrom` is a variant that takes a custom starting value instead of `0`.
-/
def hIterate (P : Nat Sort _) {n : Nat} (init : P 0) (f : (i : Fin n), P i.val P (i.val+1)) :
P n :=

View File

@@ -45,6 +45,7 @@ theorem val_ne_iff {a b : Fin n} : a.1 ≠ b.1 ↔ a ≠ b := not_congr val_inj
theorem forall_iff {p : Fin n Prop} : ( i, p i) i h, p i, h :=
fun h i hi => h i, hi, fun h i, hi => h i hi
/-- Restatement of `Fin.mk.injEq` as an `iff`. -/
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
(a, ha : Fin n) = b, hb a = b := Fin.ext_iff
@@ -55,6 +56,14 @@ theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
theorem mk_val (i : Fin n) : (i, i.isLt : Fin n) = i := Fin.eta ..
@[simp] theorem mk_eq_zero {n a : Nat} {ha : a < n} [NeZero n] :
(a, ha : Fin n) = 0 a = 0 :=
mk.inj_iff
@[simp] theorem zero_eq_mk {n a : Nat} {ha : a < n} [NeZero n] :
0 = (a, ha : Fin n) a = 0 := by
simp [eq_comm]
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat' n a).val = a % n := rfl
@@ -661,12 +670,20 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n i) :
natAdd n (subNat n (i.cast (Nat.add_comm ..)) h) = i := by simp [ cast_addNat]
/-! ### recursion and induction principles -/
/-! ### Recursion and induction principles -/
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0` of an
`(n+1)`-tuple, and `succ n i` defines `(i+1)`-st element of `(n+1)`-tuple based on `n`, `i`, and
`i`-th element of `n`-tuple. -/
/--
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
applications of `Fin.succ`.
The cases in the induction are:
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
bound `n`
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
step. `Fin.succRecOn` is a version of this induction principle that takes the `Fin` argument first.
-/
-- FIXME: Performance review
@[elab_as_elim] def succRec {motive : n, Fin n Sort _}
(zero : n, motive n.succ (0 : Fin (n + 1)))
@@ -675,13 +692,18 @@ This function has two arguments: `zero n` defines `0`-th element `motive (n+1) 0
| Nat.succ n, 0, _ => by rw [mk_zero]; exact zero n
| Nat.succ _, Nat.succ i, h => succ _ _ (succRec zero succ i, Nat.lt_of_succ_lt_succ h)
/-- Define `motive n i` by induction on `i : Fin n` interpreted as `(0 : Fin (n - i)).succ.succ…`.
This function has two arguments:
`zero n` defines the `0`-th element `motive (n+1) 0` of an `(n+1)`-tuple, and
`succ n i` defines the `(i+1)`-st element of an `(n+1)`-tuple based on `n`, `i`,
and the `i`-th element of an `n`-tuple.
/--
An induction principle for `Fin` that considers a given `i : Fin n` as given by a sequence of `i`
applications of `Fin.succ`.
A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
The cases in the induction are:
* `zero` demonstrates the motive for `(0 : Fin (n + 1))` for all bounds `n`
* `succ` demonstrates the motive for `Fin.succ` applied to an arbitrary `Fin` for an arbitrary
bound `n`
Unlike `Fin.induction`, the motive quantifies over the bound, and the bound varies at each inductive
step. `Fin.succRec` is a version of this induction principle that takes the `Fin` argument last.
-/
-- FIXME: Performance review
@[elab_as_elim] def succRecOn {n : Nat} (i : Fin n) {motive : n, Fin n Sort _}
(zero : n, motive (n + 1) 0) (succ : n i, motive n i motive (Nat.succ n) i.succ) :
@@ -696,9 +718,17 @@ A version of `Fin.succRec` taking `i : Fin n` as the first argument. -/
cases i; rfl
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
This function has two arguments: `zero` handles the base case on `motive 0`,
and `succ` defines the inductive step using `motive i.castSucc`.
/--
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
For the induction:
* `zero` is the base case, demonstrating `motive 0`.
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
`Fin.castSucc`) and demonstrating it for `i.succ`.
`Fin.inductionOn` is a version of this induction principle that takes the `Fin` as its first
parameter, `Fin.cases` is the corresponding case analysis operator, and `Fin.reverseInduction` is a
version that starts at the greatest value instead of `0`.
-/
-- FIXME: Performance review
@[elab_as_elim] def induction {motive : Fin (n + 1) Sort _} (zero : motive 0)
@@ -719,18 +749,30 @@ where
(succ : i : Fin n, motive (castSucc i) motive i.succ) (i : Fin n) :
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
/-- Define `motive i` by induction on `i : Fin (n + 1)` via induction on the underlying `Nat` value.
This function has two arguments: `zero` handles the base case on `motive 0`,
and `succ` defines the inductive step using `motive i.castSucc`.
/--
Proves a statement by induction on the underlying `Nat` value in a `Fin (n + 1)`.
A version of `Fin.induction` taking `i : Fin (n + 1)` as the first argument.
For the induction:
* `zero` is the base case, demonstrating `motive 0`.
* `succ` is the inductive step, assuming the motive for `i : Fin n` (lifted to `Fin (n + 1)` with
`Fin.castSucc`) and demonstrating it for `i.succ`.
`Fin.induction` is a version of this induction principle that takes the `Fin` as its last
parameter.
-/
-- FIXME: Performance review
@[elab_as_elim] def inductionOn (i : Fin (n + 1)) {motive : Fin (n + 1) Sort _} (zero : motive 0)
(succ : i : Fin n, motive (castSucc i) motive i.succ) : motive i := induction zero succ i
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = 0` and
`i = j.succ`, `j : Fin n`. -/
/--
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`.
The two cases are:
* `zero`, used when the value is of the form `(0 : Fin (n + 1))`
* `succ`, used when the value is of the form `(j : Fin n).succ`
The corresponding induction principle is `Fin.induction`.
-/
@[elab_as_elim] def cases {motive : Fin (n + 1) Sort _}
(zero : motive 0) (succ : i : Fin n, motive i.succ) :
i : Fin (n + 1), motive i := induction zero fun i _ => succ i
@@ -768,9 +810,14 @@ theorem fin_two_eq_of_eq_zero_iff : ∀ {a b : Fin 2}, (a = 0 ↔ b = 0) → a =
simp only [forall_fin_two]; decide
/--
Define `motive i` by reverse induction on `i : Fin (n + 1)` via induction on the underlying `Nat`
value. This function has two arguments: `last` handles the base case on `motive (Fin.last n)`,
and `cast` defines the inductive step using `motive i.succ`, inducting downwards.
Proves a statement by reverse induction on the underlying `Nat` value in a `Fin (n + 1)`.
For the induction:
* `last` is the base case, demonstrating `motive (Fin.last n)`.
* `cast` is the inductive step, assuming the motive for `(j : Fin n).succ` and demonstrating it for
the predecessor `j.castSucc`.
`Fin.induction` is the non-reverse induction principle.
-/
@[elab_as_elim] def reverseInduction {motive : Fin (n + 1) Sort _} (last : motive (Fin.last n))
(cast : i : Fin n, motive i.succ motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
@@ -793,8 +840,16 @@ decreasing_by decreasing_with
succ i (reverseInduction zero succ i.succ) := by
rw [reverseInduction, dif_neg (Fin.ne_of_lt (Fin.castSucc_lt_last i))]; rfl
/-- Define `f : Π i : Fin n.succ, motive i` by separately handling the cases `i = Fin.last n` and
`i = j.castSucc`, `j : Fin n`. -/
/--
Proves a statement by cases on the underlying `Nat` value in a `Fin (n + 1)`, checking whether the
value is the greatest representable or a predecessor of some other.
The two cases are:
* `last`, used when the value is `Fin.last n`
* `cast`, used when the value is of the form `(j : Fin n).succ`
The corresponding induction principle is `Fin.reverseInduction`.
-/
@[elab_as_elim] def lastCases {n : Nat} {motive : Fin (n + 1) Sort _} (last : motive (Fin.last n))
(cast : i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
reverseInduction last (fun i _ => cast i) i
@@ -807,8 +862,16 @@ decreasing_by decreasing_with
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
reverseInduction_castSucc ..
/-- Define `f : Π i : Fin (m + n), motive i` by separately handling the cases `i = castAdd n i`,
`j : Fin m` and `i = natAdd m j`, `j : Fin n`. -/
/--
A case analysis operator for `i : Fin (m + n)` that separately handles the cases where `i < m` and
where `m ≤ i < m + n`.
The first case, where `i < m`, is handled by `left`. In this case, `i` can be represented as
`Fin.castAdd n (j : Fin m)`.
The second case, where `m ≤ i < m + n`, is handled by `right`. In this case, `i` can be represented
as `Fin.natAdd m (j : Fin n)`.
-/
@[elab_as_elim] def addCases {m n : Nat} {motive : Fin (m + n) Sort u}
(left : i, motive (castAdd n i)) (right : i, motive (natAdd m i))
(i : Fin (m + n)) : motive i :=

View File

@@ -6,4 +6,20 @@ Authors: Henrik Böving
prelude
import Init.Data.Nat.Log2
set_option linter.missingDocs true
/--
Logarithm base 2 for bounded numbers.
The resulting value is the same as that computed by `Nat.log2`. In particular, the result for `0` is
`0`.
Examples:
* `(8 : Fin 10).log2 = (3 : Fin 10)`
* `(7 : Fin 10).log2 = (2 : Fin 10)`
* `(4 : Fin 10).log2 = (2 : Fin 10)`
* `(3 : Fin 10).log2 = (1 : Fin 10)`
* `(1 : Fin 10).log2 = (0 : Fin 10)`
* `(0 : Fin 10).log2 = (0 : Fin 10)`
-/
def Fin.log2 (n : Fin m) : Fin m := Nat.log2 n.val, Nat.lt_of_le_of_lt (Nat.log2_le_self n.val) n.isLt

View File

@@ -17,11 +17,14 @@ attribute [extern "lean_float_array_data"] FloatArray.data
namespace FloatArray
@[extern "lean_mk_empty_float_array"]
def mkEmpty (c : @& Nat) : FloatArray :=
def emptyWithCapacity (c : @& Nat) : FloatArray :=
{ data := #[] }
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
def empty : FloatArray :=
mkEmpty 0
emptyWithCapacity 0
instance : Inhabited FloatArray where
default := empty
@@ -164,6 +167,9 @@ def foldl {β : Type v} (f : β → Float → β) (init : β) (as : FloatArray)
end FloatArray
/--
Converts a list of floats into a `FloatArray`.
-/
def List.toFloatArray (ds : List Float) : FloatArray :=
let rec loop
| [], r => r

View File

@@ -23,6 +23,12 @@ instance [ToFormat α] : ToFormat (List α) where
instance [ToFormat α] : ToFormat (Array α) where
format a := "#" ++ format a.toList
/--
Formats an optional value, with no expectation that the Lean parser should be able to parse the
result.
This function is usually accessed through the `ToFormat (Option α)` instance.
-/
def Option.format {α : Type u} [ToFormat α] : Option α Format
| none => "none"
| some a => "some " ++ Std.format a

View File

@@ -9,10 +9,23 @@ import Init.Core
namespace Function
/--
Transforms a function from pairs into an equivalent two-parameter function.
Examples:
* `Function.curry (fun (x, y) => x + y) 3 5 = 8`
* `Function.curry Prod.swap 3 "five" = ("five", 3)`
-/
@[inline]
def curry : (α × β φ) α β φ := fun f a b => f (a, b)
/-- Interpret a function with two arguments as a function on `α × β` -/
/--
Transforms a two-parameter function into an equivalent function from pairs.
Examples:
* `Function.uncurry List.drop (1, ["a", "b", "c"]) = ["b", "c"]`
* `[("orange", 2), ("android", 3) ].map (Function.uncurry String.take) = ["or", "and"]`
-/
@[inline]
def uncurry : (α β φ) α × β φ := fun f a => f a.1 a.2

View File

@@ -75,13 +75,19 @@ instance (P : Prop) : Hashable P where
@[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
mixHash u 11
/-- `LawfulHashable α` says that the `BEq α` and `Hashable α` instances on `α` are compatible, i.e.,
that `a == b` implies `hash a = hash b`. This is automatic if the `BEq` instance is lawful.
/--
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
`hash a = hash b`.
This is automatic if the `BEq` instance is lawful.
-/
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
/-- If `a == b`, then `hash a = hash b`. -/
hash_eq (a b : α) : a == b hash a = hash b
/--
A lawful hash function respects its Boolean equality test.
-/
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b hash a = hash b :=
LawfulHashable.hash_eq a b

View File

@@ -14,3 +14,4 @@ import Init.Data.Int.Order
import Init.Data.Int.Pow
import Init.Data.Int.Cooper
import Init.Data.Int.Linear
import Init.Data.Int.OfNat

View File

@@ -17,10 +17,12 @@ open Nat
This file defines the `Int` type as well as
* coercions, conversions, and compatibility with numeric literals,
* basic arithmetic operations add/sub/mul/div/mod/pow,
* basic arithmetic operations add/sub/mul/pow,
* a few `Nat`-related operations such as `negOfNat` and `subNatNat`,
* relations `<`/`≤`/`≥`/`>`, the `NonNeg` property and `min`/`max`,
* decidability of equality, relations and `NonNeg`.
Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
-/
/--
@@ -291,13 +293,16 @@ def toNat : Int → Nat
| negSucc _ => 0
/--
* If `n : Nat`, then `int.toNat' n = some n`
* If `n : Int` is negative, then `int.toNat' n = none`.
* If `n : Nat`, then `Int.toNat? n = some n`
* If `n : Int` is negative, then `Int.toNat? n = none`.
-/
def toNat' : Int Option Nat
def toNat? : Int Option Nat
| (n : Nat) => some n
| -[_+1] => none
@[deprecated toNat? (since := "2025-03-11"), inherit_doc toNat?]
abbrev toNat' := toNat?
/-! ## divisibility -/
/--

View File

@@ -1,50 +1,8 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
Authors: Kim Morrison
-/
prelude
import Init.Data.Int.Basic
import Init.Data.Nat.Bitwise.Basic
namespace Int
/-! ## bit operations -/
/--
Bitwise not
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
```
-/
protected def not : Int -> Int
| Int.ofNat n => Int.negSucc n
| Int.negSucc n => Int.ofNat n
instance : Complement Int := .not
/--
Bitwise shift right.
Conceptually, this treats 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
```
-/
protected def shiftRight : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n >>> s)
| Int.negSucc n, s => Int.negSucc (n >>> s)
instance : HShiftRight Int Nat Int := .shiftRight
end Int
import Init.Data.Int.Bitwise.Basic
import Init.Data.Int.Bitwise.Lemmas

View File

@@ -0,0 +1,50 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.Int.Basic
import Init.Data.Nat.Bitwise.Basic
namespace Int
/-! ## bit operations -/
/--
Bitwise not
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
```
-/
protected def not : Int Int
| Int.ofNat n => Int.negSucc n
| Int.negSucc n => Int.ofNat n
instance : Complement Int := .not
/--
Bitwise shift right.
Conceptually, this treats 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
```
-/
protected def shiftRight : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n >>> s)
| Int.negSucc n, s => Int.negSucc (n >>> s)
instance : HShiftRight Int Nat Int := .shiftRight
end Int

View File

@@ -5,12 +5,13 @@ Authors: Siddharth Bhat, Jeremy Avigad
-/
prelude
import Init.Data.Nat.Bitwise.Lemmas
import Init.Data.Int.Bitwise
import Init.Data.Int.Bitwise.Basic
import Init.Data.Int.DivMod.Lemmas
namespace Int
theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl
@[simp]
theorem natCast_shiftRight (n s : Nat) : (n : Int) >>> s = n >>> s := rfl
@@ -27,7 +28,7 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
m >>> n = m / ((2 ^ n) : Nat) := by
simp only [shiftRight_eq, Int.shiftRight, Nat.shiftRight_eq_div_pow]
split
· simp
· simp; norm_cast
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
rfl
@@ -39,4 +40,47 @@ theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow]
theorem le_shiftRight_of_nonpos {n : Int} {s : Nat} (h : n 0) : n n >>> s := by
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
split
case _ _ _ m =>
simp only [ofNat_eq_coe] at h
by_cases hm : m = 0
· simp [hm]
· omega
case _ _ _ m =>
by_cases hm : m = 0
· simp [hm]
· have := Nat.shiftRight_le m s
omega
theorem shiftRight_le_of_nonneg {n : Int} {s : Nat} (h : 0 n) : n >>> s n := by
simp only [Int.shiftRight_eq, Int.shiftRight, Int.ofNat_eq_coe]
split
case _ _ _ m =>
simp only [Int.ofNat_eq_coe] at h
by_cases hm : m = 0
· simp [hm]
· have := Nat.shiftRight_le m s
simp
omega
case _ _ _ m =>
omega
theorem le_shiftRight_of_nonneg {n : Int} {s : Nat} (h : 0 n) : 0 (n >>> s) := by
rw [Int.shiftRight_eq_div_pow]
by_cases h' : s = 0
· simp [h', h]
· have := @Nat.pow_pos 2 s (by omega)
have := @Int.ediv_nonneg n (2^s) h (by norm_cast at *; omega)
norm_cast at *
theorem shiftRight_le_of_nonpos {n : Int} {s : Nat} (h : n 0) : (n >>> s) 0 := by
rw [Int.shiftRight_eq_div_pow]
by_cases h' : s = 0
· simp [h', h]
· have : 1 < 2 ^ s := Nat.one_lt_two_pow (by omega)
have rl : n / 2 ^ s 0 := Int.ediv_nonpos_of_nonpos_of_neg (by omega) (by norm_cast at *; omega)
norm_cast at *
end Int

View File

@@ -227,33 +227,4 @@ theorem cooper_resolution_dvd_right
· exact Int.mul_neg _ _ Int.neg_le_of_neg_le lower
· exact Int.mul_neg _ _ Int.neg_mul _ _ dvd
/--
Left Cooper resolution of an upper and lower bound.
-/
theorem cooper_resolution_left
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
( x, p a * x b * x q)
( k : Int, 0 k k < a b * k + b * p a * q a k + p) := by
have h := cooper_resolution_dvd_left
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt a_pos), Int.one_dvd, and_true,
and_self] at h
exact h
/--
Right Cooper resolution of an upper and lower bound.
-/
theorem cooper_resolution_right
{a b p q : Int} (a_pos : 0 < a) (b_pos : 0 < b) :
( x, p a * x b * x q)
( k : Int, 0 k k < b a * k + b * p a * q b k - q) := by
have h := cooper_resolution_dvd_right
a_pos b_pos Int.zero_lt_one (c := 1) (s := 0) (p := p) (q := q)
have : k : Int, (b -k + q) (b k - q) := by
intro k
rw [ Int.dvd_neg, Int.neg_add, Int.neg_neg, Int.sub_eq_add_neg]
simp only [Int.mul_one, Int.one_mul, Int.mul_zero, Int.add_zero, gcd_one, Int.ofNat_one,
Int.ediv_one, lcm_self, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.one_dvd, and_true,
and_self, Int.neg_eq_neg_one_mul, this] at h
exact h
end Int

View File

@@ -21,26 +21,28 @@ and satisfy `x / 0 = 0` and `x % 0 = x`.
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`,
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,
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).
In September 2024, we decided to do this rename (with deprecations in place),
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
ever need to use these functions and their associated lemmas.
In December 2024, we removed `tdiv` and `tmod`, but have not yet renamed `ediv` and `emod`.
In December 2024, we removed `div` and `mod`, but have not yet renamed `ediv` and `emod`.
-/
/-! ### E-rounding division
This pair satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`.
This pair satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`.
-/
/--
Integer division. This version of `Int.div` uses the E-rounding convention
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
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`.
This means that `Int.ediv x y = floor (x / y)` when `y > 0` and `Int.ediv x y = ceil (x / y)` when `y < 0`.
This is the function powering the `/` notation on integers.
@@ -71,7 +73,7 @@ def ediv : (@& Int) → (@& Int) → Int
| -[m+1], -[n+1] => ofNat (succ (m / succ n))
/--
Integer modulus. This version of `Int.mod` uses the E-rounding convention
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`.
@@ -109,7 +111,7 @@ instance : Div Int where
instance : Mod Int where
mod := Int.emod
@[simp, norm_cast] theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
@[norm_cast] theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
theorem ofNat_ediv_ofNat {a b : Nat} : (a / b : Int) = (a / b : Nat) := rfl
@[norm_cast]
@@ -165,6 +167,9 @@ def tdiv : (@& Int) → (@& Int) → Int
unconditionally (see [`Int.tmod_add_tdiv`][theo tmod_add_tdiv]). In
particular, `a % 0 = a`.
`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`.
[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
@@ -229,7 +234,7 @@ def fdiv : Int → Int → Int
| -[m+1], -[n+1] => ofNat (succ m / succ n)
/--
Integer modulus. This version of `Int.mod` uses the F-rounding convention
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`.
@@ -268,11 +273,14 @@ Balanced mod (and balanced div) are a division and modulus pair such
that `b * (Int.bdiv a b) + Int.bmod a b = a` and
`-b/2 ≤ Int.bmod a b < b/2` for all `a : Int` and `b > 0`.
This is used in Omega as well as signed bitvectors.
Note that unlike `emod`, `fmod`, and `tmod`,
`bmod` takes a natural number as the second argument, rather than an integer.
This function is used in `omega` as well as signed bitvectors.
-/
/--
Balanced modulus. This version of Integer modulus uses the
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`.

View File

@@ -18,7 +18,7 @@ open Nat (succ)
namespace Int
-- /-! ### dvd -/
/-! ### dvd -/
protected theorem dvd_def (a b : Int) : (a b) = Exists (fun c => b = a * c) := rfl
@@ -53,7 +53,7 @@ protected theorem dvd_mul_left (a b : Int) : b a * b := ⟨_, Int.mul_comm .
constructor <;> exact fun k, e =>
-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]
protected theorem dvd_neg {a b : Int} : a -b a b := by
@[simp] protected theorem dvd_neg {a b : Int} : a -b a b := by
constructor <;> exact fun k, e =>
-k, by simp [ e, Int.neg_mul, Int.mul_neg, Int.neg_neg]
@@ -67,7 +67,7 @@ protected theorem dvd_neg {a b : Int} : a -b ↔ a b := by
theorem ofNat_dvd_left {n : Nat} {z : Int} : (n : Int) z n z.natAbs := by
rw [ natAbs_dvd_natAbs, natAbs_ofNat]
/-! ### *div zero -/
/-! ### ediv zero -/
@[simp] theorem zero_ediv : b : Int, 0 / b = 0
| ofNat _ => show ofNat _ = _ by simp
@@ -77,7 +77,7 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) z ↔ n z.natA
| ofNat _ => show ofNat _ = _ by simp
| -[_+1] => rfl
/-! ### mod zero -/
/-! ### emod zero -/
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := rfl
@@ -89,7 +89,6 @@ theorem ofNat_dvd_left {n : Nat} {z : Int} : (↑n : Int) z ↔ n z.natA
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : ((m % n) : Int) = m % n := rfl
/-! ### mod definitions -/
theorem emod_add_ediv : a b : Int, a % b + b * (a / b) = a
@@ -106,18 +105,23 @@ where
Int.neg_neg (_-_), Int.neg_sub, Int.sub_sub_self, Int.add_right_comm]
exact congrArg (fun x => -(ofNat x + 1)) (Nat.mod_add_div ..)
/-- Variant of `emod_add_ediv` with the multiplication written the other way around. -/
theorem emod_add_ediv' (a b : Int) : a % b + a / b * b = a := by
rw [Int.mul_comm]; exact emod_add_ediv ..
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
rw [Int.add_comm]; exact emod_add_ediv ..
/-- Variant of `ediv_add_emod` with the multiplication written the other way around. -/
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
rw [Int.mul_comm]; exact ediv_add_emod ..
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
rw [ Int.add_sub_cancel (a % b), emod_add_ediv]
/-! ### `/` ediv -/
@[simp] protected theorem ediv_neg : a b : Int, a / (-b) = -(a / b)
@[simp] theorem ediv_neg : a b : Int, a / (-b) = -(a / b)
| ofNat m, 0 => show ofNat (m / 0) = -(m / 0) by rw [Nat.div_zero]; rfl
| ofNat _, -[_+1] => (Int.neg_neg _).symm
| ofNat _, succ _ | -[_+1], 0 | -[_+1], succ _ | -[_+1], -[_+1] => rfl
@@ -154,6 +158,10 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
apply congrArg negSucc
rw [Nat.mul_comm, Nat.sub_mul_div]; rwa [Nat.mul_comm]
theorem add_mul_ediv_left (a : Int) {b : Int}
(c : Int) (H : b 0) : (a + b * c) / b = a / b + c :=
Int.mul_comm .. Int.add_mul_ediv_right _ _ H
theorem add_ediv_of_dvd_right {a b c : Int} (H : c b) : (a + b) / c = a / c + b / c :=
if h : c = 0 then by simp [h] else by
let k, hk := H
@@ -170,13 +178,14 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c a) : (a + b) / c = a / c +
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a 0) : (a * b) / a = b :=
Int.mul_comm .. Int.mul_ediv_cancel _ H
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b 0 a 0 := by
theorem ediv_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : 0 a / b 0 a := by
rw [Int.div_def]
match b, h with
| Int.ofNat (b+1), _ =>
rcases a with a <;> simp [Int.ediv]
norm_cast
simp
@[deprecated ediv_nonneg_iff_of_pos (since := "2025-02-28")]
abbrev div_nonneg_iff_of_pos := @ediv_nonneg_iff_of_pos
/-! ### emod -/
@@ -189,16 +198,6 @@ theorem emod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a % b < b :=
| ofNat _, _, _, rfl => ofNat_lt.2 (Nat.mod_lt _ (Nat.succ_pos _))
| -[_+1], _, _, rfl => Int.sub_lt_self _ (ofNat_lt.2 <| Nat.succ_pos _)
theorem mul_ediv_self_le {x k : Int} (h : k 0) : k * (x / k) x :=
calc k * (x / k)
_ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
_ = x := ediv_add_emod _ _
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
calc x
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
@[simp] theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
if cz : c = 0 then by
rw [cz, Int.mul_zero, Int.add_zero]
@@ -306,6 +305,18 @@ theorem emod_pos_of_not_dvd {a b : Int} (h : ¬ a b) : a = 0 0 < b % a :
· simp_all
· exact Or.inr (Int.lt_iff_le_and_ne.mpr emod_nonneg b w, Ne.symm h)
/-! ### `/` and ordering -/
theorem mul_ediv_self_le {x k : Int} (h : k 0) : k * (x / k) x :=
calc k * (x / k)
_ k * (x / k) + x % k := Int.le_add_of_nonneg_right (emod_nonneg x h)
_ = x := ediv_add_emod _ _
theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
calc x
_ = k * (x / k) + x % k := (ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
/-! ### bmod -/
@[simp] theorem bmod_emod : bmod x m % m = x % m := by

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,10 @@ import Init.Data.Int.DivMod.Lemmas
/-!
Definition and lemmas for gcd and lcm over Int
## Future work
Most of the material about `Nat.gcd` and `Nat.lcm` from `Init.Data.Nat.Gcd` and `Init.Data.Nat.Lcm`
has analogues for `Int.gcd` and `Int.lcm` that should be added to this file.
-/
namespace Int

View File

@@ -25,31 +25,32 @@ theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat
@[norm_cast] theorem ofNat_add (n m : Nat) : ((n + m) : Int) = n + m := rfl
@[norm_cast] theorem ofNat_mul (n m : Nat) : ((n * m) : Int) = n * m := rfl
theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
@[norm_cast] theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
@[local simp] theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
@[local simp] theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
@[local simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
theorem negSucc_coe (n : Nat) : -[n+1] = -(n + 1) := rfl
theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
/-! ## These are only for internal use -/
@[simp] theorem add_def {a b : Int} : Int.add a b = a + b := rfl
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
/-!
## These are only for internal use
Ideally these could all be made private, but they are used in downstream libraries.
-/
@[local simp] theorem ofNat_add_ofNat (m n : Nat) : (m + n : Int) = (m + n) := rfl
@[local simp] theorem ofNat_add_negSucc (m n : Nat) : m + -[n+1] = subNatNat m (succ n) := rfl
@[local simp] theorem negSucc_add_ofNat (m n : Nat) : -[m+1] + n = subNatNat n (succ m) := rfl
@[local simp] theorem negSucc_add_negSucc (m n : Nat) : -[m+1] + -[n+1] = -[succ (m + n) +1] := rfl
@[simp] theorem mul_def {a b : Int} : Int.mul a b = a * b := rfl
@[local simp] theorem ofNat_mul_ofNat (m n : Nat) : (m * n : Int) = (m * n) := rfl
@[local simp] theorem ofNat_mul_negSucc' (m n : Nat) : m * -[n+1] = negOfNat (m * succ n) := rfl
@[local simp] theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * n = negOfNat (succ m * n) := rfl
@[local simp] theorem negSucc_mul_negSucc' (m n : Nat) :
@[local simp] private theorem ofNat_mul_negSucc' (m n : Nat) : m * -[n+1] = negOfNat (m * succ n) := rfl
@[local simp] private theorem negSucc_mul_ofNat' (m n : Nat) : -[m+1] * n = negOfNat (succ m * n) := rfl
@[local simp] private theorem negSucc_mul_negSucc' (m n : Nat) :
-[m+1] * -[n+1] = ofNat (succ m * succ n) := rfl
/- ## some basic functions and properties -/
@@ -64,11 +65,14 @@ theorem negSucc_inj : negSucc m = negSucc n ↔ m = n := ⟨negSucc.inj, fun H =
theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_coe (n : Nat) : -[n+1] = -(n + 1) := rfl
@[simp] theorem negSucc_ne_zero (n : Nat) : -[n+1] 0 := nofun
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 -[n+1] := nofun
@[simp, norm_cast] theorem Nat.cast_ofNat_Int :
@[simp, norm_cast] theorem cast_ofNat_Int :
(Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
/- ## neg -/
@@ -78,7 +82,7 @@ theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
| succ _ => rfl
| -[_+1] => rfl
protected theorem neg_inj {a b : Int} : -a = -b a = b :=
@[simp] protected theorem neg_inj {a b : Int} : -a = -b a = b :=
fun h => by rw [ Int.neg_neg a, Int.neg_neg b, h], congrArg _
@[simp] protected theorem neg_eq_zero : -a = 0 a = 0 := Int.neg_inj (b := 0)
@@ -86,12 +90,13 @@ protected theorem neg_inj {a b : Int} : -a = -b ↔ a = b :=
protected theorem neg_ne_zero : -a 0 a 0 := not_congr Int.neg_eq_zero
protected theorem sub_eq_add_neg {a b : Int} : a - b = a + -b := rfl
protected theorem add_neg_eq_sub {a b : Int} : a + -b = a - b := rfl
theorem add_neg_one (i : Int) : i + -1 = i - 1 := rfl
/- ## basic properties of subNatNat -/
-- @[elabAsElim] -- TODO(Mario): unexpected eliminator resulting type
@[elab_as_elim]
theorem subNatNat_elim (m n : Nat) (motive : Nat Nat Int Prop)
(hp : i n, motive (n + i) n i)
(hn : i m, motive m (m + i + 1) -[i+1]) :
@@ -140,29 +145,6 @@ theorem subNatNat_of_lt {m n : Nat} (h : m < n) : subNatNat m n = -[pred (n - m)
rw [Nat.sub_eq_iff_eq_add' h]
simp
/- # Additive group properties -/
/- addition -/
protected theorem add_comm : a b : Int, a + b = b + a
| ofNat n, ofNat m => by simp [Nat.add_comm]
| ofNat _, -[_+1] => rfl
| -[_+1], ofNat _ => rfl
| -[_+1], -[_+1] => by simp [Nat.add_comm]
instance : Std.Commutative (α := Int) (· + ·) := Int.add_comm
@[simp] protected theorem add_zero : a : Int, a + 0 = a
| ofNat _ => rfl
| -[_+1] => rfl
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. a.add_zero
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
left_id := Int.zero_add
right_id := Int.add_zero
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
theorem subNatNat_sub (h : n m) (k : Nat) : subNatNat (m - n) k = subNatNat m (k + n) := by
rwa [ subNatNat_add_add _ _ n, Nat.sub_add_cancel]
@@ -191,6 +173,34 @@ theorem subNatNat_add_negSucc (m n k : Nat) :
Nat.add_assoc, succ_sub_succ_eq_sub, Nat.add_comm n,Nat.add_sub_assoc (Nat.le_of_lt h'),
Nat.add_comm]
theorem subNatNat_self : n, subNatNat n n = 0
| 0 => rfl
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
/- # Additive group properties -/
/- addition -/
protected theorem add_comm : a b : Int, a + b = b + a
| ofNat n, ofNat m => by simp [Nat.add_comm]
| ofNat _, -[_+1] => rfl
| -[_+1], ofNat _ => rfl
| -[_+1], -[_+1] => by simp [Nat.add_comm]
instance : Std.Commutative (α := Int) (· + ·) := Int.add_comm
@[simp] protected theorem add_zero : a : Int, a + 0 = a
| ofNat _ => rfl
| -[_+1] => rfl
@[simp] protected theorem zero_add (a : Int) : 0 + a = a := Int.add_comm .. a.add_zero
instance : Std.LawfulIdentity (α := Int) (· + ·) 0 where
left_id := Int.zero_add
right_id := Int.add_zero
theorem ofNat_add_negSucc_of_lt (h : m < n.succ) : ofNat m + -[n+1] = -[n - m+1] :=
show subNatNat .. = _ by simp [succ_sub (le_of_lt_succ h), subNatNat]
protected theorem add_assoc : a b c : Int, a + b + c = a + (b + c)
| (m:Nat), (n:Nat), _ => aux1 ..
| Nat.cast m, b, Nat.cast k => by
@@ -222,18 +232,12 @@ protected theorem add_right_comm (a b c : Int) : a + b + c = a + c + b := by
/- ## negation -/
theorem subNatNat_self : n, subNatNat n n = 0
protected theorem add_left_neg : a : Int, -a + a = 0
| 0 => rfl
| succ m => by rw [subNatNat_of_sub_eq_zero (Nat.sub_self ..), Nat.sub_self, ofNat_zero]
| succ m => by simp [neg_ofNat_succ]
| -[m+1] => by simp [neg_negSucc]
attribute [local simp] subNatNat_self
@[local simp] protected theorem add_left_neg : a : Int, -a + a = 0
| 0 => rfl
| succ m => by simp
| -[m+1] => by simp
@[local simp] protected theorem add_right_neg (a : Int) : a + -a = 0 := by
protected theorem add_right_neg (a : Int) : a + -a = 0 := by
rw [Int.add_comm, Int.add_left_neg]
protected theorem neg_eq_of_add_eq_zero {a b : Int} (h : a + b = 0) : -a = b := by
@@ -264,11 +268,22 @@ protected theorem add_left_cancel {a b c : Int} (h : a + b = a + c) : b = c := b
have h₁ : -a + (a + b) = -a + (a + c) := by rw [h]
simp [ Int.add_assoc, Int.add_left_neg, Int.zero_add] at h₁; exact h₁
@[local simp] protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
protected theorem neg_add {a b : Int} : -(a + b) = -a + -b := by
apply Int.add_left_cancel (a := a + b)
rw [Int.add_right_neg, Int.add_comm a, Int.add_assoc, Int.add_assoc b,
Int.add_right_neg, Int.add_zero, Int.add_right_neg]
/--
If a predicate on the integers is invariant under negation,
then it is sufficient to prove it for the nonnegative integers.
-/
theorem wlog_sign {P : Int Prop} (inv : i, P i P (-i)) (w : n : Nat, P n) (i : Int) : P i := by
cases i with
| ofNat n => exact w n
| negSucc n =>
rw [negSucc_eq, inv, ofNat_succ]
apply w
/- ## subtraction -/
@[simp] theorem negSucc_sub_one (n : Nat) : -[n+1] - 1 = -[n + 1 +1] := rfl
@@ -292,13 +307,13 @@ protected theorem sub_eq_zero {a b : Int} : a - b = 0 ↔ a = b :=
Int.eq_of_sub_eq_zero, Int.sub_eq_zero_of_eq
protected theorem sub_sub (a b c : Int) : a - b - c = a - (b + c) := by
simp [Int.sub_eq_add_neg, Int.add_assoc]
simp [Int.sub_eq_add_neg, Int.add_assoc, Int.neg_add]
protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
simp [Int.sub_eq_add_neg, Int.add_comm]
simp [Int.sub_eq_add_neg, Int.add_comm, Int.neg_add]
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
simp [Int.sub_eq_add_neg, Int.add_assoc]
simp [Int.sub_eq_add_neg, Int.add_assoc, Int.neg_add, Int.add_right_neg]
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
@@ -309,7 +324,7 @@ protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
Int.add_neg_cancel_right a b
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.sub_eq_add_neg]
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_neg_eq_sub]
@[norm_cast] theorem ofNat_sub (h : m n) : ((n - m : Nat) : Int) = n - m := by
match m with
@@ -318,6 +333,7 @@ protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
show ofNat (n - succ m) = subNatNat n (succ m)
rw [subNatNat, Nat.sub_eq_zero_of_le h]
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_coe' (n : Nat) : -[n+1] = -n - 1 := by
rw [Int.sub_eq_add_neg, Int.neg_add]; rfl
@@ -327,11 +343,11 @@ protected theorem subNatNat_eq_coe {m n : Nat} : subNatNat m n = ↑m - ↑n :=
rw [Int.ofNat_add, Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm,
Int.add_right_neg, Int.add_zero]
· intros i n
simp only [negSucc_coe, ofNat_add, Int.sub_eq_add_neg, Int.neg_add, Int.add_assoc]
rw [ @Int.sub_eq_add_neg n, ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
simp only [negSucc_eq, ofNat_add, ofNat_one, Int.sub_eq_add_neg, Int.neg_add, Int.add_assoc]
rw [Int.add_neg_eq_sub (a := n), ofNat_sub, Nat.sub_self, ofNat_zero, Int.zero_add]
apply Nat.le_refl
theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
@[simp] theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
rw [ Int.subNatNat_eq_coe]
refine subNatNat_elim m n (fun m n i => toNat i = m - n) (fun i n => ?_) (fun i n => ?_)
· exact (Nat.add_sub_cancel_left ..).symm
@@ -347,6 +363,9 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
norm_cast
simp [eq_comm]
@[simp] theorem negSucc_eq_neg_ofNat_iff {a b : Nat} : -[a+1] = - (b : Int) a + 1 = b := by
rw [eq_comm, neg_ofNat_eq_negSucc_iff, eq_comm]
@[simp] theorem neg_ofNat_eq_negSucc_add_one_iff {a b : Nat} : - (a : Int) = -[b+1] + 1 a = b := by
cases b with
| zero => simp; norm_cast
@@ -355,30 +374,33 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
norm_cast
simp [eq_comm]
@[simp] theorem negSucc_add_one_eq_neg_ofNat_iff {a b : Nat} : -[a+1] + 1 = - (b : Int) a = b := by
rw [eq_comm, neg_ofNat_eq_negSucc_add_one_iff, eq_comm]
/- ## add/sub injectivity -/
protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by
@[simp] protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by
apply Iff.intro
· intro p
rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p]
· exact congrArg (· + k)
protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) i = j := by
@[simp] protected theorem add_right_inj {i j : Int} (k : Int) : (k + i = k + j) i = j := by
simp [Int.add_comm k, Int.add_left_inj]
protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) i = j := by
@[simp] protected theorem sub_right_inj {i j : Int} (k : Int) : (k - i = k - j) i = j := by
simp [Int.sub_eq_add_neg, Int.neg_inj, Int.add_right_inj]
protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) i = j := by
@[simp] protected theorem sub_left_inj {i j : Int} (k : Int) : (i - k = j - k) i = j := by
simp [Int.sub_eq_add_neg, Int.add_left_inj]
/- ## Ring properties -/
@[simp] theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -(m * succ n) := rfl
theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -(m * succ n) := rfl
@[simp] theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -(succ m * n) := rfl
theorem negSucc_mul_ofNat (m n : Nat) : -[m+1] * n = -(succ m * n) := rfl
@[simp] theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
theorem negSucc_mul_negSucc (m n : Nat) : -[m+1] * -[n+1] = succ m * succ n := rfl
protected theorem mul_comm (a b : Int) : a * b = b * a := by
cases a <;> cases b <;> simp [Nat.mul_comm]
@@ -396,11 +418,10 @@ theorem negSucc_mul_negOfNat (m n : Nat) : -[m+1] * negOfNat n = ofNat (succ m *
theorem negOfNat_mul_negSucc (m n : Nat) : negOfNat n * -[m+1] = ofNat (n * succ m) := by
rw [Int.mul_comm, negSucc_mul_negOfNat, Nat.mul_comm]
attribute [local simp] ofNat_mul_negOfNat negOfNat_mul_ofNat
negSucc_mul_negOfNat negOfNat_mul_negSucc
protected theorem mul_assoc (a b c : Int) : a * b * c = a * (b * c) := by
cases a <;> cases b <;> cases c <;> simp [Nat.mul_assoc]
cases a <;> cases b <;> cases c <;>
simp [Nat.mul_assoc, ofNat_mul_negOfNat, negOfNat_mul_ofNat, negSucc_mul_negOfNat, negOfNat_mul_negSucc]
instance : Std.Associative (α := Int) (· * ·) := Int.mul_assoc
protected theorem mul_left_comm (a b c : Int) : a * (b * c) = b * (a * c) := by
@@ -418,7 +439,7 @@ theorem negOfNat_eq_subNatNat_zero (n) : negOfNat n = subNatNat 0 n := by cases
theorem ofNat_mul_subNatNat (m n k : Nat) :
m * subNatNat n k = subNatNat (m * n) (m * k) := by
cases m with
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul]
| zero => simp [ofNat_zero, Int.zero_mul, Nat.zero_mul, subNatNat_self]
| succ m => cases n.lt_or_ge k with
| inl h =>
have h' : succ m * n < succ m * k := Nat.mul_lt_mul_of_pos_left h (Nat.succ_pos m)
@@ -446,8 +467,7 @@ theorem negSucc_mul_subNatNat (m n k : Nat) :
Nat.mul_sub_left_distrib, succ_pred_eq_of_pos (Nat.sub_pos_of_lt h₁)]; rfl
| inr h' => rw [Nat.le_antisymm h h', subNatNat_self, subNatNat_self, Int.mul_zero]
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat
attribute [local simp] ofNat_mul_subNatNat negOfNat_add negSucc_mul_subNatNat in
protected theorem mul_add : a b c : Int, a * (b + c) = a * b + a * c
| (m:Nat), (n:Nat), (k:Nat) => by simp [Nat.left_distrib]
| (m:Nat), (n:Nat), -[k+1] => by
@@ -495,7 +515,9 @@ instance : Std.LawfulIdentity (α := Int) (· * ·) 1 where
left_id := Int.one_mul
right_id := Int.mul_one
protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
@[simp] protected theorem mul_neg_one (a : Int) : a * -1 = -a := by rw [Int.mul_neg, Int.mul_one]
@[simp] protected theorem neg_one_mul (a : Int) : -1 * a = -a := by rw [Int.neg_mul, Int.one_mul]
protected theorem neg_eq_neg_one_mul : a : Int, -a = -1 * a
| 0 => rfl
@@ -544,16 +566,18 @@ The following lemmas are later subsumed by e.g. `Nat.cast_add` and `Nat.cast_mul
but it is convenient to have these earlier, for users who only need `Nat` and `Int`.
-/
theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
protected theorem natCast_zero : ((0 : Nat) : Int) = (0 : Int) := rfl
theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
protected theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
@[simp] theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
@[simp] protected theorem natCast_add (a b : Nat) : ((a + b : Nat) : Int) = (a : Int) + (b : Int) := by
-- Note this only works because of local simp attributes in this file,
-- so it still makes sense to tag the lemmas with `@[simp]`.
simp
@[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
protected theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : Int) = (n : Int) + 1 := rfl
@[simp] protected theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
simp
end Int

View File

@@ -15,6 +15,32 @@ import Init.Omega
namespace Int
/-! ### miscellaneous lemmas -/
@[simp] theorem natCast_le_zero : {n : Nat} (n : Int) 0 n = 0 := by omega
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c a = c + b := by omega
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c a = b + c := by omega
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i 0 0 i := by omega
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ((no_index (OfNat.ofNat n)) : Int) :=
ofNat_nonneg _
@[simp] theorem neg_natCast_le_natCast (n m : Nat) : -(n : Int) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_natCast_le_ofNat (n m : Nat) : -(n : Int) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_ofNat_le_ofNat (n m : Nat) : -(no_index (OfNat.ofNat n)) (no_index (OfNat.ofNat m)) :=
Int.le_trans (by simp) (ofNat_zero_le m)
@[simp] theorem neg_ofNat_le_natCast (n m : Nat) : -(no_index (OfNat.ofNat n)) (m : Int) :=
Int.le_trans (by simp) (ofNat_zero_le m)
/-! ### toNat -/
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by
symm
simp only [Int.toNat]
@@ -39,6 +65,60 @@ namespace Int
simp [toNat]
split <;> simp_all <;> omega
@[simp] theorem toNat_eq_zero : {n : Int}, n.toNat = 0 n 0 := by omega
@[simp] theorem toNat_le {m : Int} {n : Nat} : m.toNat n m n := by omega
@[simp] theorem toNat_lt' {m : Int} {n : Nat} (hn : 0 < n) : m.toNat < n m < n := by omega
/-! ### natAbs -/
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d n) (h₁ : n.natAbs < d.natAbs) :
n = 0 := by
obtain a, rfl := h
rw [natAbs_mul] at h₁
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
/-! ### min and max -/
@[simp] protected theorem min_assoc : (a b c : Int), min (min a b) c = min a (min b c) := by omega
instance : Std.Associative (α := Nat) min := Nat.min_assoc
@[simp] protected theorem min_self_assoc {m n : Int} : min m (min m n) = min m n := by
rw [ Int.min_assoc, Int.min_self]
@[simp] protected theorem min_self_assoc' {m n : Int} : min n (min m n) = min n m := by
rw [Int.min_comm m n, Int.min_assoc, Int.min_self]
@[simp] protected theorem max_assoc (a b c : Int) : max (max a b) c = max a (max b c) := by omega
instance : Std.Associative (α := Nat) max := Nat.max_assoc
@[simp] protected theorem max_self_assoc {m n : Int} : max m (max m n) = max m n := by
rw [ Int.max_assoc, Int.max_self]
@[simp] protected theorem max_self_assoc' {m n : Int} : max n (max m n) = max n m := by
rw [Int.max_comm m n, Int.max_assoc, Int.max_self]
protected theorem max_min_distrib_left (a b c : Int) : max a (min b c) = min (max a b) (max a c) := by omega
protected theorem min_max_distrib_left (a b c : Int) : min a (max b c) = max (min a b) (min a c) := by omega
protected theorem max_min_distrib_right (a b c : Int) :
max (min a b) c = min (max a c) (max b c) := by omega
protected theorem min_max_distrib_right (a b c : Int) :
min (max a b) c = max (min a c) (min b c) := by omega
protected theorem sub_min_sub_right (a b c : Int) : min (a - c) (b - c) = min a b - c := by omega
protected theorem sub_max_sub_right (a b c : Int) : max (a - c) (b - c) = max a b - c := by omega
protected theorem sub_min_sub_left (a b c : Int) : min (a - b) (a - c) = a - max b c := by omega
protected theorem sub_max_sub_left (a b c : Int) : max (a - b) (a - c) = a - min b c := by omega
/-! ### bmod -/
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m x) (h1 : x < m) :
(x.bmod m) < 0 (-(m / 2) x x < 0) ((m + 1) / 2 x) := by
simp only [Int.bmod_def]
@@ -46,4 +126,18 @@ theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m ≤ x) (h1 : x < m) :
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) n) (hn : n < (m + 1) / 2) :
n.bmod m = n := by
rw [ Int.sub_eq_zero]
have := le_bmod (x := n) (m := m) (by omega)
have := bmod_lt (x := n) (m := m) (by omega)
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
omega
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n m) :
(a.bmod m).bmod n = a.bmod n := by
rw [ Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
obtain k, rfl := hnm
simp [Int.mul_assoc]
end Int

View File

@@ -9,6 +9,7 @@ import Init.Data.Prod
import Init.Data.Int.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.DivMod.Bootstrap
import Init.Data.Int.Cooper
import Init.Data.Int.Gcd
import Init.Data.RArray
import Init.Data.AC
@@ -186,14 +187,13 @@ theorem cmod_gt_of_pos (a : Int) {b : Int} (h : 0 < b) : cmod a b > -b :=
theorem cmod_nonpos (a : Int) {b : Int} (h : b 0) : cmod a b 0 := by
have := Int.neg_le_neg (Int.emod_nonneg (-a) h)
simp at this
assumption
simpa [cmod] using this
theorem cmod_eq_zero_iff_emod_eq_zero (a b : Int) : cmod a b = 0 a%b = 0 := by
unfold cmod
have := @Int.emod_eq_emod_iff_emod_sub_eq_zero b b a
simp at this
simp [Int.neg_emod, this, Eq.comm]
simp [Int.neg_emod_eq_sub_emod, this, Eq.comm]
private abbrev div_mul_cancel_of_mod_zero :=
@Int.ediv_mul_cancel_of_emod_eq_zero
@@ -250,14 +250,24 @@ def Poly.divCoeffs (k : Int) : Poly → Bool
/--
`p.mul k` multiplies all coefficients and constant of the polynomial `p` by `k`.
-/
def Poly.mul (p : Poly) (k : Int) : Poly :=
def Poly.mul' (p : Poly) (k : Int) : Poly :=
match p with
| .num k' => .num (k*k')
| .add k' v p => .add (k*k') v (mul p k)
| .add k' v p => .add (k*k') v (mul' p k)
def Poly.mul (p : Poly) (k : Int) : Poly :=
if k == 0 then
.num 0
else
p.mul' k
@[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
induction p <;> simp [mul, denote, *]
rw [Int.mul_assoc, Int.mul_add]
simp [mul]
split
next => simp [*, denote]
next =>
induction p <;> simp [mul', denote, *]
rw [Int.mul_assoc, Int.mul_add]
attribute [local simp] Int.add_comm Int.add_assoc Int.add_left_comm Int.add_mul Int.mul_add
attribute [local simp] Poly.insert Poly.denote Poly.norm Poly.addConst
@@ -310,7 +320,6 @@ theorem Poly.denote_div_eq_of_divAll (ctx : Context) (p : Poly) (k : Int) : p.di
replace h₁ := div_mul_cancel_of_mod_zero h₁
have ih := ih h₂
simp [ih]
apply congrArg (denote ctx p + ·)
rw [Int.mul_right_comm, h₁]
attribute [local simp] Poly.divCoeffs Poly.getConst
@@ -410,7 +419,7 @@ theorem norm_eq_var_const (ctx : Context) (lhs rhs : Expr) (x : Var) (k : Int) (
simp [norm_eq_var_const_cert] at h
replace h := congrArg (Poly.denote ctx) h
simp at h
rw [Int.sub_eq_zero, h, Int.add_comm, Int.sub_eq_add_neg, Int.sub_eq_zero]
rw [Int.sub_eq_zero, h, Int.add_comm, Int.add_neg_eq_sub, Int.sub_eq_zero]
private theorem mul_eq_zero_iff (a k : Int) (h₁ : k > 0) : k * a = 0 a = 0 := by
conv => lhs; rw [ Int.mul_zero k]
@@ -531,8 +540,9 @@ def Poly.isValidLe (p : Poly) : Bool :=
| .num k => k 0
| _ => false
attribute [-simp] Int.not_le in
theorem le_eq_false (ctx : Context) (lhs rhs : Expr) : (lhs.sub rhs).norm.isUnsatLe (lhs.denote ctx rhs.denote ctx) = False := by
simp [Poly.isUnsatLe] <;> split <;> simp
simp only [Poly.isUnsatLe] <;> split <;> simp
next p k h =>
intro h'
replace h := congrArg (Poly.denote ctx) h
@@ -791,7 +801,7 @@ theorem dvd_solve_elim (ctx : Context) (d₁ : Int) (p₁ : Poly) (d₂ : Int) (
intro _ hd _; subst x₁ p; simp
intro h₁ h₂
rw [Int.add_comm] at h₁ h₂
rw [ Int.sub_eq_add_neg]
rw [Int.add_neg_eq_sub]
exact dvd_solve_elim' hd h₁ h₂
theorem dvd_norm (ctx : Context) (d : Int) (p₁ p₂ : Poly) : p₁.norm == p₂ d p₁.denote' ctx d p₂.denote' ctx := by
@@ -820,7 +830,7 @@ def le_neg_cert (p₁ p₂ : Poly) : Bool :=
theorem le_neg (ctx : Context) (p₁ p₂ : Poly) : le_neg_cert p₁ p₂ ¬ p₁.denote' ctx 0 p₂.denote' ctx 0 := by
simp [le_neg_cert]
intro; subst p₂; simp; intro h
replace h : _ + 1 -0 := Int.neg_lt_neg <| Int.lt_of_not_ge h
replace h : _ + 1 -0 := Int.neg_lt_neg h
simp at h
exact h
@@ -844,11 +854,28 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
· rw [ Int.zero_mul (Poly.denote ctx p₂)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
· rw [ Int.zero_mul (Poly.denote ctx p₁)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
def le_combine_coeff_cert (p₁ p₂ p₃ : Poly) (k : Int) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
let p := p₁.mul a₂ |>.combine (p₂.mul a₁)
k > 0 && (p.divCoeffs k && p₃ == p.div k)
theorem le_combine_coeff (ctx : Context) (p₁ p₂ p₃ : Poly) (k : Int)
: le_combine_coeff_cert p₁ p₂ p₃ k p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp only [le_combine_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
generalize h : (p₁.mul a₂ |>.combine (p₂.mul a₁)) = p
intro h₁ h₂ h₃ h₄ h₅
have := le_combine ctx p₁ p₂ p
simp only [le_combine_cert, beq_iff_eq] at this
have aux₁ := this h.symm h₄ h₅
have := le_coeff ctx p p₃ k
simp only [le_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp] at this
exact this h₁ h₂ h₃ aux₁
theorem le_unsat (ctx : Context) (p : Poly) : p.isUnsatLe p.denote' ctx 0 False := by
simp [Poly.isUnsatLe]; split <;> simp
intro h₁ h₂
have := Int.lt_of_le_of_lt h₂ h₁
simp at this
theorem eq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx = 0 p₂.denote' ctx = 0 := by
simp at h
@@ -990,10 +1017,8 @@ theorem eq_le_subst_nonpos (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly)
intro h
intro; subst p₃
intro h₁ h₂
simp [*]
simp [*, -Int.neg_nonpos_iff]
replace h₂ := Int.mul_le_mul_of_nonpos_left h₂ h; simp at h₂; clear h
rw [ Int.neg_zero]
apply Int.neg_le_neg
rw [Int.mul_comm]
assumption
@@ -1004,7 +1029,7 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: eq_of_core_cert p₁ p₂ p₃ p₁.denote' ctx = p₂.denote' ctx p₃.denote' ctx = 0 := by
simp [eq_of_core_cert]
intro; subst p₃; simp
intro h; rw [h, Int.sub_eq_add_neg, Int.sub_self]
intro h; rw [h, Int.add_neg_eq_sub, Int.sub_self]
def Poly.isUnsatDiseq (p : Poly) : Bool :=
match p with
@@ -1021,6 +1046,9 @@ theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p'
simp [eq_coeff_cert]
intro _ _; simp [mul_eq_zero_iff, *]
theorem diseq_neg (ctx : Context) (p p' : Poly) : p' == p.mul (-1) p.denote' ctx 0 p'.denote' ctx 0 := by
simp; intro _ _; simp [mul_eq_zero_iff, *]
theorem diseq_unsat (ctx : Context) (p : Poly) : p.isUnsatDiseq p.denote' ctx 0 False := by
simp [Poly.isUnsatDiseq] <;> split <;> simp
@@ -1041,7 +1069,693 @@ theorem diseq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
simp [eq_of_core_cert]
intro; subst p₃; simp
intro h; rw [ Int.sub_eq_zero] at h
rw [Int.sub_eq_add_neg]; assumption
rw [Int.add_neg_eq_sub]; assumption
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
p₂ == p₁.mul (-1)
theorem eq_of_le_ge (ctx : Context) (p₁ : Poly) (p₂ : Poly)
: eq_of_le_ge_cert p₁ p₂ p₁.denote' ctx 0 p₂.denote' ctx 0 p₁.denote' ctx = 0 := by
simp [eq_of_le_ge_cert]
intro; subst p₂; simp [-Int.neg_nonpos_iff]
intro h₁ h₂
simp [Int.eq_iff_le_and_ge, *]
def le_of_le_diseq_cert (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
-- Remark: we can generate two different certificates in the future, and avoid the `||` in the certificate.
(p₂ == p₁ || p₂ == p₁.mul (-1)) &&
p₃ == p₁.addConst 1
theorem le_of_le_diseq (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: le_of_le_diseq_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [le_of_le_diseq_cert]
have (a : Int) : a 0 ¬ a = 0 1 + a 0 := by
intro h₁ h₂; cases (Int.lt_or_gt_of_ne h₂)
next => apply Int.le_of_lt_add_one; rw [Int.add_comm, Int.add_lt_add_iff_right]; assumption
next h => have := Int.lt_of_le_of_lt h₁ h; simp at this
intro h; cases h <;> intro <;> subst p₂ p₃ <;> simp <;> apply this
def diseq_split_cert (p₁ p₂ p₃ : Poly) : Bool :=
p₂ == p₁.addConst 1 &&
p₃ == (p₁.mul (-1)).addConst 1
theorem diseq_split (ctx : Context) (p₁ p₂ p₃ : Poly)
: diseq_split_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [diseq_split_cert]
intro _ _; subst p₂ p₃; simp
generalize p₁.denote ctx = p
intro h; cases Int.lt_or_gt_of_ne h
next h => have := Int.add_one_le_of_lt h; rw [Int.add_comm]; simp [*]
next h => have := Int.add_one_le_of_lt (Int.neg_lt_neg h); simp at this; simp [*]
theorem diseq_split_resolve (ctx : Context) (p₁ p₂ p₃ : Poly)
: diseq_split_cert p₁ p₂ p₃ p₁.denote' ctx 0 ¬p₂.denote' ctx 0 p₃.denote' ctx 0 := by
intro h₁ h₂ h₃
exact (diseq_split ctx p₁ p₂ p₃ h₁ h₂).resolve_left h₃
def OrOver (n : Nat) (p : Nat Prop) : Prop :=
match n with
| 0 => False
| n+1 => p n OrOver n p
theorem orOver_one {p} : OrOver 1 p p 0 := by simp [OrOver]
theorem orOver_resolve {n p} : OrOver (n+1) p ¬ p n OrOver n p := by
intro h₁ h₂
rw [OrOver] at h₁
cases h₁
· contradiction
· assumption
def OrOver_cases_type (n : Nat) (p : Nat Prop) : Prop :=
match n with
| 0 => p 0
| n+1 => ¬ p (n+1) OrOver_cases_type n p
theorem orOver_cases {n p} : OrOver (n+1) p OrOver_cases_type n p := by
induction n <;> simp [OrOver_cases_type]
next => exact orOver_one
next n ih => intro h₁ h₂; exact ih (orOver_resolve h₁ h₂)
private theorem orOver_of_p {i n p} (h₁ : i < n) (h₂ : p i) : OrOver n p := by
induction n
next => simp at h₁
next n ih =>
simp [OrOver]
cases Nat.eq_or_lt_of_le <| Nat.le_of_lt_add_one h₁
next h => subst i; exact Or.inl h₂
next h => exact Or.inr (ih h)
private theorem orOver_of_exists {n p} : ( k, k < n p k) OrOver n p := by
intro k, h₁, h₂
apply orOver_of_p h₁ h₂
private theorem ofNat_toNat {a : Int} : a 0 Int.ofNat a.toNat = a := by cases a <;> simp
private theorem cast_toNat {a : Int} : a 0 a.toNat = a := by cases a <;> simp
private theorem ofNat_lt {a : Int} {n : Nat} : a 0 a < Int.ofNat n a.toNat < n := by cases a <;> simp
private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
private theorem lcm_one (a : Int) : Int.lcm a 1 = a.natAbs := by simp [Int.lcm]
private theorem cooper_dvd_left_core
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
(h₃ : d c * x + s)
: OrOver (Int.lcm a (a * d / Int.gcd (a * d) c)) fun k =>
b * p + (-a) * q + b * k 0
a p + k
a * d c * p + (-a) * s + c * k := by
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
have h₁' : p (-a)*x := by rw [Int.neg_mul, Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
have h₂' : b * x -q := by rw [ Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
have k, h₁, h₂, h₃, h₄, h₅ := Int.cooper_resolution_dvd_left a_pos' b_pos d_pos |>.mp x, h₁', h₂', h₃
rw [Int.neg_mul] at h₂
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
rw [Int.neg_ediv_of_dvd Int.gcd_dvd_left] at h₂
simp only [lcm_neg_right] at h₂
have : c * k + c * p + -(a * s) = c * p + -(a * s) + c * k := by ac_rfl
rw [this] at h₅; clear this
rw [ ofNat_toNat h₁] at h₃ h₄ h₅
rw [Int.add_comm] at h₄
have := ofNat_lt h₁ h₂
apply orOver_of_exists
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
have : b * Int.ofNat k.toNat + b * p + -(a * q) = b * p + -(a * q) + b * Int.ofNat k.toNat := by ac_rfl
rw [this] at h₃
exists k.toNat
def cooper_dvd_left_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
p₃.casesOn (fun _ => false) fun c z _ =>
.and (x == y) <| .and (x == z) <|
.and (a < 0) <| .and (b > 0) <|
.and (d > 0) <| n == Int.lcm a (a * d / Int.gcd (a * d) c)
def Poly.tail (p : Poly) : Poly :=
match p with
| .add _ _ p => p
| _ => p
def cooper_dvd_left_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let s := p₃.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let c := p₃.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
let p₂ := p.mul c |>.combine (s.mul (-a))
(p₁.addConst (b*k)).denote' ctx 0
a (p.addConst k).denote' ctx
a*d (p₂.addConst (c*k)).denote' ctx
private theorem denote'_mul_combine_mul_addConst_eq (ctx : Context) (p q : Poly) (a b c : Int)
: ((p.mul b |>.combine (q.mul a)).addConst c).denote' ctx = b*p.denote ctx + a*q.denote ctx + c := by
simp
private theorem denote'_addConst_eq (ctx : Context) (p : Poly) (a : Int)
: (p.addConst a).denote' ctx = p.denote ctx + a := by
simp
theorem cooper_dvd_left (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
: cooper_dvd_left_cert p₁ p₂ p₃ d n
p₁.denote' ctx 0
p₂.denote' ctx 0
d p₃.denote' ctx
OrOver n (cooper_dvd_left_split ctx p₁ p₂ p₃ d) := by
unfold cooper_dvd_left_split
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_left_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q c z s =>
intro _ _; subst y z
intro ha hb hd
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂ h₃
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq]
exact cooper_dvd_left_core ha hb hd h₁ h₂ h₃
def cooper_dvd_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
theorem cooper_dvd_left_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_dvd_left_split_ineq_cert, cooper_dvd_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_dvd_left_split_dvd1_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
a == p₁.leadCoeff && p' == p₁.tail.addConst k
theorem cooper_dvd_left_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd1_cert p₁ p' a k a p'.denote' ctx := by
simp [cooper_dvd_left_split_dvd1_cert, cooper_dvd_left_split]
intros; subst a p'; simp; assumption
def cooper_dvd_left_split_dvd2_cert (p₁ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
let p := p₁.tail
let s := p₃.tail
let a := p₁.leadCoeff
let c := p₃.leadCoeff
let p₂ := p.mul c |>.combine (s.mul (-a))
d' == a*d && p' == p₂.addConst (c*k)
theorem cooper_dvd_left_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
: cooper_dvd_left_split ctx p₁ p₂ p₃ d k cooper_dvd_left_split_dvd2_cert p₁ p₃ d k d' p' d' p'.denote' ctx := by
simp [cooper_dvd_left_split_dvd2_cert, cooper_dvd_left_split]
intros; subst d' p'; simp; assumption
private theorem cooper_left_core
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
: OrOver a.natAbs fun k =>
b * p + (-a) * q + b * k 0
a p + k := by
have d_pos : (0 : Int) < 1 := by decide
have h₃ : 1 0*x + 0 := Int.one_dvd _
have h := cooper_dvd_left_core a_neg b_pos d_pos h₁ h₂ h₃
simp only [Int.mul_one, gcd_zero, ofNat_natAbs_of_nonpos (Int.le_of_lt a_neg), Int.ediv_neg,
Int.ediv_self (Int.ne_of_lt a_neg), Int.reduceNeg, lcm_neg_right, lcm_one,
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
and_true] at h
assumption
def cooper_left_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
.and (x == y) <| .and (a < 0) <| .and (b > 0) <|
n == a.natAbs
def cooper_left_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
(p₁.addConst (b*k)).denote' ctx 0
a (p.addConst k).denote' ctx
theorem cooper_left (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
: cooper_left_cert p₁ p₂ n
p₁.denote' ctx 0
p₂.denote' ctx 0
OrOver n (cooper_left_split ctx p₁ p₂) := by
unfold cooper_left_split
cases p₁ <;> cases p₂ <;> simp [cooper_left_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q =>
intro; subst y
intro ha hb
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂
have := cooper_left_core ha hb h₁ h₂
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq]
assumption
def cooper_left_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (b : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
p₂.leadCoeff == b && p' == p₁.addConst (b*k)
theorem cooper_left_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
: cooper_left_split ctx p₁ p₂ k cooper_left_split_ineq_cert p₁ p₂ k b p' p'.denote' ctx 0 := by
simp [cooper_left_split_ineq_cert, cooper_left_split]
intros; subst p' b; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_left_split_dvd_cert (p₁ p' : Poly) (a : Int) (k : Int) : Bool :=
a == p₁.leadCoeff && p' == p₁.tail.addConst k
theorem cooper_left_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
: cooper_left_split ctx p₁ p₂ k cooper_left_split_dvd_cert p₁ p' a k a p'.denote' ctx := by
simp [cooper_left_split_dvd_cert, cooper_left_split]
intros; subst a p'; simp; assumption
private theorem cooper_dvd_right_core
{a b c d s p q x : Int} (a_neg : a < 0) (b_pos : 0 < b) (d_pos : 0 < d)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
(h₃ : d c * x + s)
: OrOver (Int.lcm b (b * d / Int.gcd (b * d) c)) fun k =>
b * p + (-a) * q + (-a) * k 0
b q + k
b * d (-c) * q + b * s + (-c) * k := by
have a_pos' : 0 < -a := by apply Int.neg_pos_of_neg; assumption
have h₁' : p (-a)*x := by rw [Int.neg_mul, Lean.Omega.Int.add_le_zero_iff_le_neg']; assumption
have h₂' : b * x -q := by rw [ Lean.Omega.Int.add_le_zero_iff_le_neg', Int.add_comm]; assumption
have k, h₁, h₂, h₃, h₄, h₅ := Int.cooper_resolution_dvd_right a_pos' b_pos d_pos |>.mp x, h₁', h₂', h₃
simp only [Int.neg_mul, neg_gcd, lcm_neg_left, Int.mul_neg, Int.neg_neg, Int.neg_dvd] at *
apply orOver_of_exists
have hlt := ofNat_lt h₁ h₂
replace h₃ := Int.add_le_add_right h₃ (-(a*q)); rw [Int.add_right_neg] at h₃
have : -(a * k) + b * p + -(a * q) = b * p + -(a * q) + -(a * k) := by ac_rfl
rw [this] at h₃; clear this
rw [Int.sub_neg, Int.add_comm] at h₄
have : -(c * k) + -(c * q) + b * s = -(c * q) + b * s + -(c * k) := by ac_rfl
rw [this] at h₅; clear this
exists k.toNat
simp only [hlt, true_and, and_true, cast_toNat h₁, h₃, h₄, h₅]
def cooper_dvd_right_cert (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
p₃.casesOn (fun _ => false) fun c z _ =>
.and (x == y) <| .and (x == z) <|
.and (a < 0) <| .and (b > 0) <|
.and (d > 0) <| n == Int.lcm b (b * d / Int.gcd (b * d) c)
def cooper_dvd_right_split (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let s := p₃.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let c := p₃.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
let p₂ := q.mul (-c) |>.combine (s.mul b)
(p₁.addConst ((-a)*k)).denote' ctx 0
b (q.addConst k).denote' ctx
b*d (p₂.addConst ((-c)*k)).denote' ctx
theorem cooper_dvd_right (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (n : Nat)
: cooper_dvd_right_cert p₁ p₂ p₃ d n
p₁.denote' ctx 0
p₂.denote' ctx 0
d p₃.denote' ctx
OrOver n (cooper_dvd_right_split ctx p₁ p₂ p₃ d) := by
unfold cooper_dvd_right_split
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp [cooper_dvd_right_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q c z s =>
intro _ _; subst y z
intro ha hb hd
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂ h₃
have := cooper_dvd_right_core ha hb hd h₁ h₂ h₃
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq, Int.neg_mul]
exact cooper_dvd_right_core ha hb hd h₁ h₂ h₃
def cooper_dvd_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let b := p₂.leadCoeff
let p₂ := p.mul b |>.combine (q.mul (-a))
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
theorem cooper_dvd_right_split_ineq (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (a : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_dvd_right_split_ineq_cert, cooper_dvd_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_dvd_right_split_dvd1_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
b == p₂.leadCoeff && p' == p₂.tail.addConst k
theorem cooper_dvd_right_split_dvd1 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (b : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd1_cert p₂ p' b k b p'.denote' ctx := by
simp [cooper_dvd_right_split_dvd1_cert, cooper_dvd_right_split]
intros; subst b p'; simp; assumption
def cooper_dvd_right_split_dvd2_cert (p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly): Bool :=
let q := p₂.tail
let s := p₃.tail
let b := p₂.leadCoeff
let c := p₃.leadCoeff
let p₂ := q.mul (-c) |>.combine (s.mul b)
d' == b*d && p' == p₂.addConst ((-c)*k)
theorem cooper_dvd_right_split_dvd2 (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (k : Nat) (d' : Int) (p' : Poly)
: cooper_dvd_right_split ctx p₁ p₂ p₃ d k cooper_dvd_right_split_dvd2_cert p₂ p₃ d k d' p' d' p'.denote' ctx := by
simp [cooper_dvd_right_split_dvd2_cert, cooper_dvd_right_split]
intros; subst d' p'; simp; assumption
private theorem cooper_right_core
{a b p q x : Int} (a_neg : a < 0) (b_pos : 0 < b)
(h₁ : a * x + p 0)
(h₂ : b * x + q 0)
: OrOver b.natAbs fun k =>
b * p + (-a) * q + (-a) * k 0
b q + k := by
have d_pos : (0 : Int) < 1 := by decide
have h₃ : 1 0*x + 0 := Int.one_dvd _
have h := cooper_dvd_right_core a_neg b_pos d_pos h₁ h₂ h₃
simp only [Int.mul_one, gcd_zero, Int.natAbs_of_nonneg (Int.le_of_lt b_pos), Int.ediv_neg,
Int.ediv_self (Int.ne_of_gt b_pos), Int.reduceNeg, lcm_neg_right, lcm_one,
Int.add_left_comm, Int.zero_mul, Int.mul_zero, Int.add_zero, Int.dvd_zero,
and_true, Int.neg_zero] at h
assumption
def cooper_right_cert (p₁ p₂ : Poly) (n : Nat) : Bool :=
p₁.casesOn (fun _ => false) fun a x _ =>
p₂.casesOn (fun _ => false) fun b y _ =>
.and (x == y) <| .and (a < 0) <| .and (b > 0) <| n == b.natAbs
def cooper_right_split (ctx : Context) (p₁ p₂ : Poly) (k : Nat) : Prop :=
let p := p₁.tail
let q := p₂.tail
let a := p₁.leadCoeff
let b := p₂.leadCoeff
let p₁ := p.mul b |>.combine (q.mul (-a))
(p₁.addConst ((-a)*k)).denote' ctx 0
b (q.addConst k).denote' ctx
theorem cooper_right (ctx : Context) (p₁ p₂ : Poly) (n : Nat)
: cooper_right_cert p₁ p₂ n
p₁.denote' ctx 0
p₂.denote' ctx 0
OrOver n (cooper_right_split ctx p₁ p₂) := by
unfold cooper_right_split
cases p₁ <;> cases p₂ <;> simp [cooper_right_cert, Poly.tail, -Poly.denote'_eq_denote]
next a x p b y q =>
intro; subst y
intro ha hb
intro; subst n
simp only [Poly.denote'_add, Poly.leadCoeff]
intro h₁ h₂
have := cooper_right_core ha hb h₁ h₂
simp only [denote'_mul_combine_mul_addConst_eq]
simp only [denote'_addConst_eq, Int.neg_mul]
assumption
def cooper_right_split_ineq_cert (p₁ p₂ : Poly) (k : Int) (a : Int) (p' : Poly) : Bool :=
let p := p₁.tail
let q := p₂.tail
let b := p₂.leadCoeff
let p₂ := p.mul b |>.combine (q.mul (-a))
p₁.leadCoeff == a && p' == p₂.addConst ((-a)*k)
theorem cooper_right_split_ineq (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (a : Int) (p' : Poly)
: cooper_right_split ctx p₁ p₂ k cooper_right_split_ineq_cert p₁ p₂ k a p' p'.denote' ctx 0 := by
simp [cooper_right_split_ineq_cert, cooper_right_split]
intros; subst a p'; simp [denote'_mul_combine_mul_addConst_eq]; assumption
def cooper_right_split_dvd_cert (p₂ p' : Poly) (b : Int) (k : Int) : Bool :=
b == p₂.leadCoeff && p' == p₂.tail.addConst k
theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b : Int) (p' : Poly)
: cooper_right_split ctx p₁ p₂ k cooper_right_split_dvd_cert p₂ p' b k b p'.denote' ctx := by
simp [cooper_right_split_dvd_cert, cooper_right_split]
intros; subst b p'; simp; assumption
private theorem one_emod_eq_one {a : Int} (h : a > 1) : 1 % a = 1 := by
have aux₁ := Int.ediv_add_emod 1 a
have : 1 / a = 0 := Int.ediv_eq_zero_of_lt (by decide) h
simp [this] at aux₁
assumption
private theorem ex_of_dvd {α β a b d x : Int}
(h₀ : d > 1)
(h₁ : d a*x + b)
(h₂ : α * a + β * d = 1)
: k, x = k * d + (- α * b) % d := by
have k, h₁ := h₁
have aux₁ : (α * a) % d = 1 := by
replace h₂ := congrArg (· % d) h₂; simp at h₂
rw [one_emod_eq_one h₀] at h₂
assumption
have : ((α * a) * x) % d = (- α * b) % d := by
replace h₁ := congrArg (α * ·) h₁; simp only at h₁
rw [Int.mul_add] at h₁
replace h₁ := congrArg (· - α * b) h₁; simp only [Int.add_sub_cancel] at h₁
rw [ Int.mul_assoc, Int.mul_left_comm, Int.sub_eq_add_neg] at h₁
replace h₁ := congrArg (· % d) h₁; simp only at h₁
rw [Int.add_emod, Int.mul_emod_right, Int.zero_add, Int.emod_emod, Int.neg_mul] at h₁
assumption
have : x % d = (- α * b) % d := by
rw [Int.mul_emod, aux₁, Int.one_mul, Int.emod_emod] at this
assumption
have : x = (x / d)*d + (- α * b) % d := by
conv => lhs; rw [ Int.ediv_add_emod x d]
rw [Int.mul_comm, this]
exists x / d
private theorem cdiv_le {a d k : Int} : d > 0 a k * d cdiv a d k := by
intro h₁ h₂
simp [cdiv]
replace h₂ := Int.neg_le_neg h₂
rw [ Int.neg_mul] at h₂
replace h₂ := Int.le_ediv_of_mul_le h₁ h₂
replace h₂ := Int.neg_le_neg h₂
simp at h₂
assumption
private theorem cooper_unsat'_helper {a b d c k x : Int}
(d_pos : d > 0)
(h₁ : x = k * d + c)
(h₂ : a x)
(h₃ : x b)
: ¬ b < (cdiv (a - c) d) * d + c := by
intro h₄
have aux₁ : cdiv (a - c) d k := by
rw [h₁] at h₂
replace h₂ := Int.sub_right_le_of_le_add h₂
exact cdiv_le d_pos h₂
have aux₂ : cdiv (a - c) d * d k * d := Int.mul_le_mul_of_nonneg_right aux₁ (Int.le_of_lt d_pos)
have aux₃ : cdiv (a - c) d * d + c k * d + c := Int.add_le_add_right aux₂ _
have aux₄ : cdiv (a - c) d * d + c x := by rw [h₁] at aux₃; assumption
have aux₅ : cdiv (a - c) d * d + c b := Int.le_trans aux₄ h₃
have := Int.lt_of_le_of_lt aux₅ h₄
exact Int.lt_irrefl _ this
private theorem cooper_unsat' {a c b d e α β x : Int}
(h₁ : d > 1)
(h₂ : d c*x + e)
(h₃ : α * c + β * d = 1)
(h₄ : (-1)*x + a 0)
(h₅ : x + b 0)
(h₆ : -b < cdiv (a - -α * e % d) d * d + -α * e % d)
: False := by
have k, h := ex_of_dvd h₁ h₂ h₃
have d_pos : d > 0 := Int.lt_trans (by decide) h₁
replace h₄ := Int.le_neg_add_of_add_le h₄; simp at h₄
replace h₅ := Int.neg_le_neg (Int.le_neg_add_of_add_le h₅); simp at h₅
have := cooper_unsat'_helper d_pos h h₄ h₅
exact this h₆
abbrev Poly.casesOnAdd (p : Poly) (k : Int Var Poly Bool) : Bool :=
p.casesOn (fun _ => false) k
abbrev Poly.casesOnNum (p : Poly) (k : Int Bool) : Bool :=
p.casesOn k (fun _ _ _ => false)
def cooper_unsat_cert (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int) : Bool :=
p₁.casesOnAdd fun k₁ x p₁ =>
p₂.casesOnAdd fun k₂ y p₂ =>
p₃.casesOnAdd fun c z p₃ =>
p₁.casesOnNum fun a =>
p₂.casesOnNum fun b =>
p₃.casesOnNum fun e =>
(k₁ == -1) |>.and (k₂ == 1) |>.and
(x == y) |>.and (x == z) |>.and
(d > 1) |>.and (α * c + β * d == 1) |>.and
(-b < cdiv (a - -α * e % d) d * d + -α * e % d)
theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β : Int)
: cooper_unsat_cert p₁ p₂ p₃ d α β
p₁.denote' ctx 0 p₂.denote' ctx 0 d p₃.denote' ctx False := by
unfold cooper_unsat_cert <;> cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnAdd,
Bool.false_eq_true, Poly.denote'_add, mul_def, add_def, false_implies]
next k₁ x p₁ k₂ y p₂ c z p₃ =>
cases p₁ <;> cases p₂ <;> cases p₃ <;> simp only [Poly.casesOnNum, Int.reduceNeg,
Bool.and_eq_true, beq_iff_eq, decide_eq_true_eq, and_imp, Bool.false_eq_true,
mul_def, add_def, false_implies, Poly.denote]
next a b e =>
intro _ _ _ _; subst k₁ k₂ y z
intro h₁ h₃ h₆; generalize Var.denote ctx x = x'
intro h₄ h₅ h₂
rw [Int.one_mul] at h₅
exact cooper_unsat' h₁ h₂ h₃ h₄ h₅ h₆
theorem ediv_emod (x y : Int) : -1 * x + y * (x / y) + x % y = 0 := by
rw [Int.add_assoc, Int.ediv_add_emod x y, Int.add_comm]
simp
rw [Int.add_neg_eq_sub, Int.sub_self]
theorem emod_nonneg (x y : Int) : y != 0 -1 * (x % y) 0 := by
simp; intro h
have := Int.neg_le_neg (Int.emod_nonneg x h)
simp at this
assumption
def emod_le_cert (y n : Int) : Bool :=
y != 0 && n == 1 - y.natAbs
theorem emod_le (x y : Int) (n : Int) : emod_le_cert y n x % y + n 0 := by
simp [emod_le_cert]
intro h₁
cases Int.lt_or_gt_of_ne h₁
next h =>
rw [Int.ofNat_natAbs_of_nonpos (Int.le_of_lt h)]
simp only [Int.sub_neg]
intro; subst n
rw [Int.add_assoc, Int.add_left_comm]
apply Int.add_le_of_le_sub_left
rw [Int.zero_sub, Int.add_comm]
have : 0 < -y := by
have := Int.neg_lt_neg h
rw [Int.neg_zero] at this
assumption
have := Int.emod_lt_of_pos x this
rw [Int.emod_neg] at this
exact this
next h =>
rw [Int.natAbs_of_nonneg (Int.le_of_lt h)]
intro; subst n
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_comm]
apply Int.add_le_of_le_sub_left
simp only [Int.add_comm, Int.sub_neg, Int.add_zero]
exact Int.emod_lt_of_pos x h
theorem natCast_nonneg (x : Nat) : (-1:Int) * NatCast.natCast x 0 := by
simp
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₁
replace h₁ : p = d*k - b₁ := by
replace h := congrArg (· - b₁) h
simp only [Int.add_sub_cancel] at h
assumption
replace h₂ : d*k - b₁ + b₂ 0 := by
rw [h₁] at h₂; assumption
have : d*k b₁ - b₂ := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Lean.Omega.Int.add_le_zero_iff_le_neg,
Int.neg_add, Int.neg_neg, Int.add_neg_eq_sub] at h₂
assumption
replace this : k (b₁ - b₂)/d := by
rw [Int.mul_comm] at this; exact Int.le_ediv_of_mul_le hd this
replace this := Int.mul_le_mul_of_nonneg_left this (Int.le_of_lt hd)
rw [h] at this
replace this := Int.sub_nonpos_of_le this
rw [Int.add_sub_assoc] at this
exact this
private theorem eq_neg_addConst_add (ctx : Context) (p : Poly)
: p.denote' ctx = (p.addConst (-p.getConst)).denote' ctx + p.getConst := by
simp only [Poly.denote'_eq_denote, Poly.denote_addConst, Int.add_comm, Int.add_left_comm]
rw [Int.add_right_neg]
simp
def dvd_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
let b₁ := p₁.getConst
let b₂ := p₂.getConst
let p := p₁.addConst (-b₁)
d > 0 && (p₂ == p.addConst b₂ && p₃ == p.addConst (b₁ - d*((b₁ - b₂)/d)))
theorem dvd_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
: dvd_le_tight_cert d p₁ p₂ p₃ d p₁.denote' ctx p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp only [dvd_le_tight_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
generalize p₂.getConst = b₂
intro hd _ _; subst p₂ p₃
have := eq_neg_addConst_add ctx p₁
revert this
generalize p₁.getConst = b₁
generalize p₁.addConst (-b₁) = p
intro h₁; rw [h₁]; clear h₁
simp only [denote'_addConst_eq]
simp only [Poly.denote'_eq_denote]
exact dvd_le_tight' hd
def dvd_neg_le_tight_cert (d : Int) (p₁ p₂ p₃ : Poly) : Bool :=
let b₁ := p₁.getConst
let b₂ := p₂.getConst
let p := p₁.addConst (-b₁)
let b₁ := -b₁
let p := p.mul (-1)
d > 0 && (p₂ == p.addConst b₂ && p₃ == p.addConst (b₁ - d*((b₁ - b₂)/d)))
theorem Poly.mul_minus_one_getConst_eq (p : Poly) : (p.mul (-1)).getConst = -p.getConst := by
simp [Poly.mul, Poly.getConst]
induction p <;> simp [Poly.mul', Poly.getConst, *]
theorem dvd_neg_le_tight (ctx : Context) (d : Int) (p₁ p₂ p₃ : Poly)
: dvd_neg_le_tight_cert d p₁ p₂ p₃ d p₁.denote' ctx p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp only [dvd_neg_le_tight_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
generalize p₂.getConst = b₂
intro hd _ _; subst p₂ p₃
simp only [Poly.denote'_eq_denote, Int.reduceNeg, Poly.denote_addConst, Poly.denote_mul,
Int.mul_add, Int.neg_mul, Int.one_mul, Int.mul_neg, Int.neg_neg, Int.add_comm, Int.add_assoc]
intro h₁ h₂
replace h₁ := Int.dvd_neg.mpr h₁
have := eq_neg_addConst_add ctx (p₁.mul (-1))
simp [Poly.mul_minus_one_getConst_eq] at this
rw [ Int.add_assoc] at this
rw [this] at h₁; clear this
rw [ Int.add_assoc]
revert h₁ h₂
generalize -Poly.denote ctx p₁ + p₁.getConst = p
generalize -p₁.getConst = b₁
intro h₁ h₂; rw [Int.add_comm] at h₁
exact dvd_le_tight' hd h₂ h₁
theorem le_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_le ctx lhs rhs p h₁] at h₂
def not_le_norm_expr_cert (lhs rhs : Expr) (p : Poly) : Bool :=
p == (((lhs.sub rhs).norm).mul (-1)).addConst 1
theorem not_le_norm_expr (ctx : Context) (lhs rhs : Expr) (p : Poly)
: not_le_norm_expr_cert lhs rhs p ¬ lhs.denote ctx rhs.denote ctx p.denote' ctx 0 := by
simp [not_le_norm_expr_cert]
intro; subst p; simp
intro h
replace h := Int.sub_nonpos_of_le h
rw [Int.add_comm, Int.add_sub_assoc] at h
rw [Int.neg_sub]; assumption
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
end Int.Linear

View File

@@ -0,0 +1,70 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.Int.Lemmas
import Init.Data.Int.DivMod
import Init.Data.RArray
namespace Int.OfNat
/-!
Helper definitions and theorems for converting `Nat` expressions into `Int` one.
We use them to implement the arithmetic theories in `grind`
-/
abbrev Var := Nat
abbrev Context := Lean.RArray Nat
def Var.denote (ctx : Context) (v : Var) : Nat :=
ctx.get v
inductive Expr where
| num (v : Nat)
| var (i : Var)
| add (a b : Expr)
| mul (a b : Expr)
| div (a b : Expr)
| mod (a b : Expr)
deriving BEq
def Expr.denote (ctx : Context) : Expr Nat
| .num k => k
| .var v => v.denote ctx
| .add a b => Nat.add (denote ctx a) (denote ctx b)
| .mul a b => Nat.mul (denote ctx a) (denote ctx b)
| .div a b => Nat.div (denote ctx a) (denote ctx b)
| .mod a b => Nat.mod (denote ctx a) (denote ctx b)
def Expr.denoteAsInt (ctx : Context) : Expr Int
| .num k => Int.ofNat k
| .var v => Int.ofNat (v.denote ctx)
| .add a b => Int.add (denoteAsInt ctx a) (denoteAsInt ctx b)
| .mul a b => Int.mul (denoteAsInt ctx a) (denoteAsInt ctx b)
| .div a b => Int.ediv (denoteAsInt ctx a) (denoteAsInt ctx b)
| .mod a b => Int.emod (denoteAsInt ctx a) (denoteAsInt ctx b)
theorem Expr.denoteAsInt_eq (ctx : Context) (e : Expr) : e.denoteAsInt ctx = e.denote ctx := by
induction e <;> simp [denote, denoteAsInt, Int.ofNat_ediv, *] <;> rfl
theorem Expr.eq (ctx : Context) (lhs rhs : Expr)
: (lhs.denote ctx = rhs.denote ctx) = (lhs.denoteAsInt ctx = rhs.denoteAsInt ctx) := by
simp [denoteAsInt_eq, Int.ofNat_inj]
theorem Expr.le (ctx : Context) (lhs rhs : Expr)
: (lhs.denote ctx rhs.denote ctx) = (lhs.denoteAsInt ctx rhs.denoteAsInt ctx) := by
simp [denoteAsInt_eq, Int.ofNat_le]
theorem of_le (ctx : Context) (lhs rhs : Expr)
: lhs.denote ctx rhs.denote ctx lhs.denoteAsInt ctx rhs.denoteAsInt ctx := by
rw [Expr.le ctx lhs rhs]; simp
theorem of_not_le (ctx : Context) (lhs rhs : Expr)
: ¬ lhs.denote ctx rhs.denote ctx ¬ lhs.denoteAsInt ctx rhs.denoteAsInt ctx := by
rw [Expr.le ctx lhs rhs]; simp
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]
end Int.OfNat

View File

@@ -33,20 +33,18 @@ theorem lt_iff_add_one_le {a b : Int} : a < b ↔ a + 1 ≤ b := .rfl
theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a b := by
simp [le_def, h]; constructor
attribute [local simp] Int.add_left_neg Int.add_right_neg Int.neg_add
theorem le.intro {a b : Int} (n : Nat) (h : a + n = b) : a b :=
le.intro_sub n <| by rw [ h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]
le.intro_sub n <| by rw [ h, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_right_neg]
theorem le.dest_sub {a b : Int} (h : a b) : n : Nat, b - a = n := nonneg_def.1 h
theorem le.dest {a b : Int} (h : a b) : n : Nat, a + n = b :=
let n, h₁ := le.dest_sub h
n, by rw [ h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc]
n, by rw [ h₁, Int.add_comm]; simp [Int.sub_eq_add_neg, Int.add_assoc, Int.add_left_neg]
protected theorem le_total (a b : Int) : a b b a :=
(nonneg_or_nonneg_neg (b - a)).imp_right fun H => by
rwa [show -(b - a) = a - b by simp [Int.add_comm, Int.sub_eq_add_neg]] at H
rwa [show -(b - a) = a - b by simp [Int.neg_add,Int.add_comm, Int.sub_eq_add_neg]] at H
@[simp, norm_cast] theorem ofNat_le {m n : Nat} : (m : Int) n m n :=
fun h =>
@@ -61,14 +59,14 @@ protected theorem le_total (a b : Int) : a ≤ b b ≤ a :=
theorem eq_ofNat_of_zero_le {a : Int} (h : 0 a) : n : Nat, a = n := by
have t := le.dest_sub h; rwa [Int.sub_zero] at t
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : n : Nat, a = n.succ :=
theorem eq_succ_of_zero_lt {a : Int} (h : 0 < a) : n : Nat, a = n + 1 :=
let n, (h : (1 + n) = a) := le.dest h
n, by rw [Nat.add_comm] at h; exact h.symm
theorem lt_add_succ (a : Int) (n : Nat) : a < a + Nat.succ n :=
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]; rfl
theorem lt_add_succ (a : Int) (n : Nat) : a < a + (n + 1) :=
le.intro n <| by rw [Int.add_comm, Int.add_left_comm]
theorem lt.intro {a b : Int} {n : Nat} (h : a + Nat.succ n = b) : a < b :=
theorem lt.intro {a b : Int} {n : Nat} (h : a + (n + 1) = b) : a < b :=
h lt_add_succ a n
theorem lt.dest {a b : Int} (h : a < b) : n : Nat, a + Nat.succ n = b :=
@@ -117,7 +115,7 @@ protected theorem lt_iff_le_and_ne {a b : Int} : a < b ↔ a ≤ b ∧ a ≠ b :
have : n 0 := aneb.imp fun eq => by rw [ hn, eq, ofNat_zero, Int.add_zero]
apply lt.intro; rwa [ Nat.succ_pred_eq_of_pos (Nat.pos_of_ne_zero this)] at hn
theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
protected theorem lt_succ (a : Int) : a < a + 1 := Int.le_refl _
protected theorem zero_lt_one : (0 : Int) < 1 := _
@@ -133,12 +131,15 @@ protected theorem lt_of_not_ge {a b : Int} (h : ¬a ≤ b) : b < a :=
protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a b :=
(Int.lt_iff_le_not_le.mp h).right
protected theorem not_le {a b : Int} : ¬a b b < a :=
@[simp] protected theorem not_le {a b : Int} : ¬a b b < a :=
Iff.intro Int.lt_of_not_ge Int.not_le_of_gt
protected theorem not_lt {a b : Int} : ¬a < b b a :=
@[simp] protected theorem not_lt {a b : Int} : ¬a < b b a :=
by rw [ Int.not_le, Decidable.not_not]
protected theorem le_of_not_gt {a b : Int} (h : ¬ a > b) : a b :=
Int.not_lt.mp h
protected theorem lt_trichotomy (a b : Int) : a < b a = b b < a :=
if eq : a = b then .inr <| .inl eq else
if le : a b then .inl <| Int.lt_iff_le_and_ne.2 le, eq else
@@ -183,61 +184,19 @@ instance : Trans (α := Int) (· ≤ ·) (· < ·) (· < ·) := ⟨Int.lt_of_le_
instance : Trans (α := Int) (· < ·) (· < ·) (· < ·) := Int.lt_trans
protected theorem min_def (n m : Int) : min n m = if n m then n else m := rfl
protected theorem max_def (n m : Int) : max n m = if n m then m else n := rfl
protected theorem min_comm (a b : Int) : min a b = min b a := by
simp [Int.min_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₁ h₂
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) min := Int.min_comm
protected theorem min_le_right (a b : Int) : min a b b := by rw [Int.min_def]; split <;> simp [*]
protected theorem min_le_left (a b : Int) : min a b a := Int.min_comm .. Int.min_le_right ..
protected theorem min_eq_left {a b : Int} (h : a b) : min a b = a := by simp [Int.min_def, h]
protected theorem min_eq_right {a b : Int} (h : b a) : min a b = b := by
rw [Int.min_comm a b]; exact Int.min_eq_left h
protected theorem le_min {a b c : Int} : a min b c a b a c :=
fun h => Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..),
fun h₁, h₂ => by rw [Int.min_def]; split <;> assumption
protected theorem max_comm (a b : Int) : max a b = max b a := by
simp only [Int.max_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₂ h₁
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) max := Int.max_comm
protected theorem le_max_left (a b : Int) : a max a b := by rw [Int.max_def]; split <;> simp [*]
protected theorem le_max_right (a b : Int) : b max a b := Int.max_comm .. Int.le_max_left ..
protected theorem max_le {a b c : Int} : max a b c a c b c :=
fun h => Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h,
fun h₁, h₂ => by rw [Int.max_def]; split <;> assumption
protected theorem max_eq_right {a b : Int} (h : a b) : max a b = b := by
simp [Int.max_def, h, Int.not_lt.2 h]
protected theorem max_eq_left {a b : Int} (h : b a) : max a b = a := by
rw [ Int.max_comm b a]; exact Int.max_eq_right h
theorem eq_natAbs_of_zero_le {a : Int} (h : 0 a) : a = natAbs a := by
theorem eq_natAbs_of_nonneg {a : Int} (h : 0 a) : a = natAbs a := by
let n, e := eq_ofNat_of_zero_le h
rw [e]; rfl
@[deprecated eq_natAbs_of_nonneg (since := "2025-03-11")]
abbrev eq_natAbs_of_zero_le := @eq_natAbs_of_nonneg
theorem le_natAbs {a : Int} : a natAbs a :=
match Int.le_total 0 a with
| .inl h => by rw [eq_natAbs_of_zero_le h]; apply Int.le_refl
| .inl h => by rw [eq_natAbs_of_nonneg h]; apply Int.le_refl
| .inr h => Int.le_trans h (ofNat_zero_le _)
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
@[simp] theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
Int.not_le.1 fun h => let _, h := eq_ofNat_of_zero_le h; nomatch h
theorem negSucc_le_zero (n : Nat) : -[n+1] 0 :=
@@ -246,18 +205,6 @@ theorem negSucc_le_zero (n : Nat) : -[n+1] ≤ 0 :=
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 -[n+1] False := by
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (ofNat_zero_le n)]
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
rw [Int.max_eq_right (ofNat_zero_le n)]
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
rw [Int.max_eq_right (negSucc_le_zero _)]
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
rw [Int.max_eq_left (negSucc_le_zero _)]
protected theorem add_le_add_left {a b : Int} (h : a b) (c : Int) : c + a c + b :=
let n, hn := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
@@ -279,10 +226,10 @@ protected theorem le_of_add_le_add_left {a b c : Int} (h : a + b ≤ a + c) : b
protected theorem le_of_add_le_add_right {a b c : Int} (h : a + b c + b) : a c :=
Int.le_of_add_le_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
protected theorem add_le_add_iff_left (a : Int) : a + b a + c b c :=
@[simp] protected theorem add_le_add_iff_left (a : Int) : a + b a + c b c :=
Int.le_of_add_le_add_left, (Int.add_le_add_left · _)
protected theorem add_le_add_iff_right (c : Int) : a + c b + c a b :=
@[simp] protected theorem add_le_add_iff_right (c : Int) : a + c b + c a b :=
Int.le_of_add_le_add_right, (Int.add_le_add_right · _)
protected theorem add_le_add {a b c d : Int} (h₁ : a b) (h₂ : c d) : a + c b + d :=
@@ -301,6 +248,15 @@ protected theorem neg_le_neg {a b : Int} (h : a ≤ b) : -b ≤ -a := by
have : 0 + -b -a + b + -b := Int.add_le_add_right this (-b)
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
@[simp] protected theorem neg_le_neg_iff {a b : Int} : -a -b b a :=
fun h => by simpa using Int.neg_le_neg h, Int.neg_le_neg
@[simp] protected theorem neg_le_zero_iff {a : Int} : -a 0 0 a := by
rw [ Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
@[simp] protected theorem zero_le_neg_iff {a : Int} : 0 -a a 0 := by
rw [ Int.neg_zero, Int.neg_le_neg_iff, Int.neg_zero]
protected theorem le_of_neg_le_neg {a b : Int} (h : -b -a) : a b :=
suffices - -a - -b by simp [Int.neg_neg] at this; assumption
Int.neg_le_neg h
@@ -318,6 +274,15 @@ protected theorem neg_lt_neg {a b : Int} (h : a < b) : -b < -a := by
have : 0 + -b < -a + b + -b := Int.add_lt_add_right this (-b)
rwa [Int.add_neg_cancel_right, Int.zero_add] at this
@[simp] protected theorem neg_lt_neg_iff {a b : Int} : -a < -b b < a :=
fun h => by simpa using Int.neg_lt_neg h, Int.neg_lt_neg
@[simp] protected theorem neg_lt_zero_iff {a : Int} : -a < 0 0 < a := by
rw [ Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
@[simp] protected theorem zero_lt_neg_iff {a : Int} : 0 < -a a < 0 := by
rw [ Int.neg_zero, Int.neg_lt_neg_iff, Int.neg_zero]
protected theorem neg_neg_of_pos {a : Int} (h : 0 < a) : -a < 0 := by
have : -a < -0 := Int.neg_lt_neg h
rwa [Int.neg_zero] at this
@@ -358,8 +323,124 @@ protected theorem sub_lt_self (a : Int) {b : Int} (h : 0 < b) : a - b < a :=
theorem add_one_le_of_lt {a b : Int} (H : a < b) : a + 1 b := H
/- ### Order properties and multiplication -/
protected theorem le_iff_lt_add_one {a b : Int} : a b a < b + 1 := by
rw [Int.lt_iff_add_one_le]
exact (Int.add_le_add_iff_right 1).symm
/- ### min and max -/
protected theorem min_def (n m : Int) : min n m = if n m then n else m := rfl
protected theorem max_def (n m : Int) : max n m = if n m then m else n := rfl
@[simp] protected theorem neg_min_neg (a b : Int) : min (-a) (-b) = -max a b := by
rw [Int.min_def, Int.max_def]
simp
split <;> rename_i h₁ <;> split <;> rename_i h₂
· simpa using Int.le_antisymm h₂ h₁
· simp
· simp
· simp only [Int.not_le] at h₁ h₂
exfalso
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
@[simp] protected theorem min_add_right (a b c : Int) : min (a + c) (b + c) = min a b + c := by
rw [Int.min_def, Int.min_def]
simp only [Int.add_le_add_iff_right]
split <;> simp
@[simp] protected theorem min_add_left (a b c : Int) : min (a + b) (a + c) = a + min b c := by
rw [Int.min_def, Int.min_def]
simp only [Int.add_le_add_iff_left]
split <;> simp
protected theorem min_comm (a b : Int) : min a b = min b a := by
simp [Int.min_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₁ h₂
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) min := Int.min_comm
protected theorem min_le_right (a b : Int) : min a b b := by rw [Int.min_def]; split <;> simp [*]
protected theorem min_le_left (a b : Int) : min a b a := Int.min_comm .. Int.min_le_right ..
protected theorem min_eq_left {a b : Int} (h : a b) : min a b = a := by simp [Int.min_def, h]
protected theorem min_eq_right {a b : Int} (h : b a) : min a b = b := by
rw [Int.min_comm a b]; exact Int.min_eq_left h
protected theorem le_min {a b c : Int} : a min b c a b a c :=
fun h => Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..),
fun h₁, h₂ => by rw [Int.min_def]; split <;> assumption
protected theorem lt_min {a b c : Int} : a < min b c a < b a < c := Int.le_min
@[simp] protected theorem neg_max_neg (a b : Int) : max (-a) (-b) = -min a b := by
rw [Int.min_def, Int.max_def]
simp
split <;> rename_i h₁ <;> split <;> rename_i h₂
· simpa using Int.le_antisymm h₁ h₂
· simp
· simp
· simp only [Int.not_le] at h₁ h₂
exfalso
exact Int.lt_irrefl _ (Int.lt_trans h₁ h₂)
@[simp] protected theorem max_add_right (a b c : Int) : max (a + c) (b + c) = max a b + c := by
rw [Int.max_def, Int.max_def]
simp only [Int.add_le_add_iff_right]
split <;> simp
@[simp] protected theorem max_add_left (a b c : Int) : max (a + b) (a + c) = a + max b c := by
rw [Int.max_def, Int.max_def]
simp only [Int.add_le_add_iff_left]
split <;> simp
protected theorem max_comm (a b : Int) : max a b = max b a := by
simp only [Int.max_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
· exact Int.le_antisymm h₂ h₁
· cases not_or_intro h₁ h₂ <| Int.le_total ..
instance : Std.Commutative (α := Int) max := Int.max_comm
protected theorem le_max_left (a b : Int) : a max a b := by rw [Int.max_def]; split <;> simp [*]
protected theorem le_max_right (a b : Int) : b max a b := Int.max_comm .. Int.le_max_left ..
protected theorem max_eq_right {a b : Int} (h : a b) : max a b = b := by
simp [Int.max_def, h, Int.not_lt.2 h]
protected theorem max_eq_left {a b : Int} (h : b a) : max a b = a := by
rw [ Int.max_comm b a]; exact Int.max_eq_right h
protected theorem max_le {a b c : Int} : max a b c a c b c :=
fun h => Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h,
fun h₁, h₂ => by rw [Int.max_def]; split <;> assumption
protected theorem max_lt {a b c : Int} : max a b < c a < c b < c := by
simp only [Int.lt_iff_add_one_le]
simpa using Int.max_le (a := a + 1) (b := b + 1) (c := c)
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (ofNat_zero_le n)]
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
rw [Int.max_eq_right (ofNat_zero_le n)]
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
rw [Int.max_eq_right (negSucc_le_zero _)]
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
rw [Int.max_eq_left (negSucc_le_zero _)]
@[simp] protected theorem min_self (a : Int) : min a a = a := Int.min_eq_left (Int.le_refl _)
instance : Std.IdempotentOp (α := Int) min := Int.min_self
@[simp] protected theorem max_self (a : Int) : max a a = a := Int.max_eq_right (Int.le_refl _)
instance : Std.IdempotentOp (α := Int) max := Int.max_self
/- ### Order properties and multiplication -/
protected theorem mul_nonneg {a b : Int} (ha : 0 a) (hb : 0 b) : 0 a * b := by
let n, hn := eq_ofNat_of_zero_le ha
@@ -369,7 +450,8 @@ protected theorem mul_nonneg {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a
protected theorem mul_pos {a b : Int} (ha : 0 < a) (hb : 0 < b) : 0 < a * b := by
let n, hn := eq_succ_of_zero_lt ha
let m, hm := eq_succ_of_zero_lt hb
rw [hn, hm, ofNat_mul]; apply ofNat_succ_pos
rw [hn, hm]
apply ofNat_succ_pos
protected theorem mul_lt_mul_of_pos_left {a b c : Int}
(h₁ : a < b) (h₂ : 0 < c) : c * a < c * b := by
@@ -425,7 +507,7 @@ protected theorem mul_le_mul_of_nonpos_left {a b c : Int}
/- ## natAbs -/
@[simp] theorem natAbs_ofNat (n : Nat) : natAbs n = n := rfl
@[simp, norm_cast] theorem natAbs_ofNat (n : Nat) : natAbs n = n := rfl
@[simp] theorem natAbs_negSucc (n : Nat) : natAbs -[n+1] = n.succ := rfl
@[simp] theorem natAbs_zero : natAbs (0 : Int) = (0 : Nat) := rfl
@[simp] theorem natAbs_one : natAbs (1 : Int) = (1 : Nat) := rfl
@@ -470,6 +552,13 @@ theorem natAbs_of_nonneg {a : Int} (H : 0 ≤ a) : (natAbs a : Int) = a :=
theorem ofNat_natAbs_of_nonpos {a : Int} (H : a 0) : (natAbs a : Int) = -a := by
rw [ natAbs_neg, natAbs_of_nonneg (Int.neg_nonneg_of_nonpos H)]
theorem natAbs_sub_of_nonneg_of_le {a b : Int} (h₁ : 0 b) (h₂ : b a) :
(a - b).natAbs = a.natAbs - b.natAbs := by
rw [ Int.ofNat_inj]
rw [natAbs_of_nonneg, ofNat_sub, natAbs_of_nonneg (Int.le_trans h₁ h₂), natAbs_of_nonneg h₁]
· rwa [ Int.ofNat_le, natAbs_of_nonneg h₁, natAbs_of_nonneg (Int.le_trans h₁ h₂)]
· exact Int.sub_nonneg_of_le h₂
/-! ### toNat -/
theorem toNat_eq_max : a : Int, (toNat a : Int) = max a 0
@@ -492,8 +581,8 @@ theorem toNat_of_nonneg {a : Int} (h : 0 ≤ a) : (toNat a : Int) = a := by
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
match a with
| Int.ofNat n => simp
| Int.negSucc n => simp
| (n : Nat) => simp
| -(n + 1 : Nat) => norm_cast
theorem self_le_toNat (a : Int) : a toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
@@ -529,12 +618,15 @@ theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
| 0 => rfl
| _+1 => rfl
/-! ### toNat' -/
/-! ### toNat? -/
theorem mem_toNat' : {a : Int} {n : Nat}, toNat' a = some n a = n
| (m : Nat), n => by simp [toNat', Int.ofNat_inj]
theorem mem_toNat? : {a : Int} {n : Nat}, toNat? a = some n a = n
| (m : Nat), n => by simp [toNat?, Int.ofNat_inj]
| -[m+1], n => by constructor <;> nofun
@[deprecated mem_toNat? (since := "2025-03-11")]
abbrev mem_toNat' := @mem_toNat?
/-! ## Order properties of the integers -/
protected theorem le_of_not_le {a b : Int} : ¬ a b b a := (Int.le_total a b).resolve_left
@@ -554,10 +646,10 @@ protected theorem lt_of_add_lt_add_left {a b c : Int} (h : a + b < a + c) : b <
protected theorem lt_of_add_lt_add_right {a b c : Int} (h : a + b < c + b) : a < c :=
Int.lt_of_add_lt_add_left (a := b) <| by rwa [Int.add_comm b a, Int.add_comm b c]
protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c b < c :=
@[simp] protected theorem add_lt_add_iff_left (a : Int) : a + b < a + c b < c :=
Int.lt_of_add_lt_add_left, (Int.add_lt_add_left · _)
protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c a < b :=
@[simp] protected theorem add_lt_add_iff_right (c : Int) : a + c < b + c a < b :=
Int.lt_of_add_lt_add_right, (Int.add_lt_add_right · _)
protected theorem add_lt_add {a b c d : Int} (h₁ : a < b) (h₂ : c < d) : a + c < b + d :=
@@ -752,6 +844,18 @@ protected theorem sub_le_sub_right {a b : Int} (h : a ≤ b) (c : Int) : a - c
protected theorem sub_le_sub {a b c d : Int} (hab : a b) (hcd : c d) : a - d b - c :=
Int.add_le_add hab (Int.neg_le_neg hcd)
protected theorem le_of_sub_le_sub_left {a b c : Int} (h : c - a c - b) : b a :=
Int.le_of_neg_le_neg <| Int.le_of_add_le_add_left h
protected theorem le_of_sub_le_sub_right {a b c : Int} (h : a - c b - c) : a b :=
Int.le_of_add_le_add_right h
@[simp] protected theorem sub_le_sub_left_iff {a b c : Int} : c - a c - b b a :=
Int.le_of_sub_le_sub_left, (Int.sub_le_sub_left · c)
@[simp] protected theorem sub_le_sub_right_iff {a b c : Int} : a - c b - c a b :=
Int.le_of_sub_le_sub_right, (Int.sub_le_sub_right · c)
protected theorem add_lt_of_lt_neg_add {a b c : Int} (h : b < -a + c) : a + b < c := by
have h := Int.add_lt_add_left h a
rwa [Int.add_neg_cancel_left] at h
@@ -860,11 +964,11 @@ protected theorem lt_of_sub_lt_sub_right {a b c : Int} (h : a - c < b - c) : a <
Int.lt_of_sub_lt_sub_right, (Int.sub_lt_sub_right · c)
protected theorem sub_lt_sub_of_le_of_lt {a b c d : Int}
(hab : a b) (hcd : c < d) : a - d < b - c :=
(hab : a b) (hcd : c < d) : a - d < b - c :=
Int.add_lt_add_of_le_of_lt hab (Int.neg_lt_neg hcd)
protected theorem sub_lt_sub_of_lt_of_le {a b c d : Int}
(hab : a < b) (hcd : c d) : a - d < b - c :=
(hab : a < b) (hcd : c d) : a - d < b - c :=
Int.add_lt_add_of_lt_of_le hab (Int.neg_le_neg hcd)
protected theorem add_le_add_three {a b c d e f : Int}
@@ -938,6 +1042,22 @@ protected theorem mul_self_le_mul_self {a b : Int} (h1 : 0 ≤ a) (h2 : a ≤ b)
protected theorem mul_self_lt_mul_self {a b : Int} (h1 : 0 a) (h2 : a < b) : a * a < b * b :=
Int.mul_lt_mul' (Int.le_of_lt h2) h2 h1 (Int.lt_of_le_of_lt h1 h2)
protected theorem nonneg_of_mul_nonneg_left {a b : Int}
(h : 0 a * b) (hb : 0 < b) : 0 a :=
Int.le_of_not_gt fun ha => Int.not_le_of_gt (Int.mul_neg_of_neg_of_pos ha hb) h
protected theorem nonneg_of_mul_nonneg_right {a b : Int}
(h : 0 a * b) (ha : 0 < a) : 0 b :=
Int.le_of_not_gt fun hb => Int.not_le_of_gt (Int.mul_neg_of_pos_of_neg ha hb) h
protected theorem nonpos_of_mul_nonpos_left {a b : Int}
(h : a * b 0) (hb : 0 < b) : a 0 :=
Int.le_of_not_gt fun ha : a > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
protected theorem nonpos_of_mul_nonpos_right {a b : Int}
(h : a * b 0) (ha : 0 < a) : b 0 :=
Int.le_of_not_gt fun hb : b > 0 => Int.not_le_of_gt (Int.mul_pos ha hb) h
/- ## sign -/
@[simp] theorem sign_zero : sign 0 = 0 := rfl
@@ -988,10 +1108,10 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
| 0, h => nomatch h
| -[_+1], _ => negSucc_lt_zero _
theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 0 < a :=
@[simp] theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 0 < a :=
pos_of_sign_eq_one, sign_eq_one_of_pos
theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 a < 0 :=
@[simp] theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 a < 0 :=
neg_of_sign_eq_neg_one, sign_eq_neg_one_of_neg
@[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 a = 0 :=
@@ -1003,7 +1123,7 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
| .ofNat (_ + 1) => rfl
| .negSucc _ => rfl
@[simp] theorem sign_nonneg : 0 sign x 0 x := by
@[simp] theorem sign_nonneg_iff : 0 sign x 0 x := by
match x with
| 0 => rfl
| .ofNat (_ + 1) =>
@@ -1011,6 +1131,26 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
exact Int.le_add_one (ofNat_nonneg _)
| .negSucc _ => simp +decide [sign]
@[deprecated sign_nonneg_iff (since := "2025-03-11")] abbrev sign_nonneg := @sign_nonneg_iff
@[simp] theorem sign_pos_iff : 0 < sign x 0 < x := by
match x with
| 0
| .ofNat (_ + 1) => simp
| .negSucc x => simp
@[simp] theorem sign_nonpos_iff : sign x 0 x 0 := by
match x with
| 0 => rfl
| .ofNat (_ + 1) => simp
| .negSucc _ => simpa using negSucc_le_zero _
@[simp] theorem sign_neg_iff : sign x < 0 x < 0 := by
match x with
| 0 => simp
| .ofNat (_ + 1) => simpa using le.intro_sub _ rfl
| .negSucc _ => simp
@[simp] theorem mul_sign_self : i : Int, i * sign i = natAbs i
| succ _ => Int.mul_one _
| 0 => Int.mul_zero _
@@ -1021,6 +1161,12 @@ theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 ↔ a < 0 :=
@[simp] theorem sign_mul_self : sign i * i = natAbs i := by
rw [Int.mul_comm, mul_sign_self]
theorem sign_trichotomy (a : Int) : sign a = 1 sign a = 0 sign a = -1 := by
match a with
| 0 => simp
| .ofNat (_ + 1) => simp
| .negSucc _ => simp
/- ## natAbs -/
theorem natAbs_ne_zero {a : Int} : a.natAbs 0 a 0 := not_congr Int.natAbs_eq_zero
@@ -1029,7 +1175,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
| ofNat _ => rfl
| -[_+1] => rfl
theorem eq_nat_or_neg (a : Int) : n : Nat, a = n a = -n := _, natAbs_eq a
protected theorem eq_nat_or_neg (a : Int) : n : Nat, a = n a = -n := _, natAbs_eq a
theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs.eq_def]
@@ -1043,7 +1189,7 @@ theorem natAbs_eq_iff {a : Int} {n : Nat} : a.natAbs = n ↔ a = n a = -↑n
theorem natAbs_add_le (a b : Int) : natAbs (a + b) natAbs a + natAbs b := by
suffices a b : Nat, natAbs (subNatNat a b.succ) (a + b).succ by
match a, b with
| (a:Nat), (b:Nat) => rw [ofNat_add_ofNat, natAbs_ofNat]; apply Nat.le_refl
| (a:Nat), (b:Nat) => rw [ ofNat_add, natAbs_ofNat]; apply Nat.le_refl
| (a:Nat), -[b+1] => rw [natAbs_ofNat, natAbs_negSucc]; apply this
| -[a+1], (b:Nat) =>
rw [natAbs_negSucc, natAbs_ofNat, Nat.succ_add, Nat.add_comm a b]; apply this
@@ -1062,6 +1208,7 @@ theorem natAbs_add_le (a b : Int) : natAbs (a + b) ≤ natAbs a + natAbs b := by
theorem natAbs_sub_le (a b : Int) : natAbs (a - b) natAbs a + natAbs b := by
rw [ Int.natAbs_neg b]; apply natAbs_add_le
@[deprecated negSucc_eq (since := "2025-03-11")]
theorem negSucc_eq' (m : Nat) : -[m+1] = -m - 1 := by simp only [negSucc_eq, Int.neg_add]; rfl
theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
@@ -1069,7 +1216,10 @@ theorem natAbs_lt_natAbs_of_nonneg_of_lt {a b : Int}
match a, b, eq_ofNat_of_zero_le w₁, eq_ofNat_of_zero_le (Int.le_trans w₁ (Int.le_of_lt w₂)) with
| _, _, _, rfl, _, rfl => ofNat_lt.1 w₂
theorem eq_natAbs_iff_mul_eq_zero : natAbs a = n (a - n) * (a + n) = 0 := by
theorem natAbs_eq_iff_mul_eq_zero : natAbs a = n (a - n) * (a + n) = 0 := by
rw [natAbs_eq_iff, Int.mul_eq_zero, Int.sub_neg, Int.sub_eq_zero, Int.sub_eq_zero]
@[deprecated natAbs_eq_iff_mul_eq_zero (since := "2025-03-11")]
abbrev eq_natAbs_iff_mul_eq_zero := @natAbs_eq_iff_mul_eq_zero
end Int

View File

@@ -11,7 +11,7 @@ namespace Int
/-! # pow -/
protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
@[simp] protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
@@ -27,11 +27,11 @@ abbrev pow_le_pow_of_le_right := @Nat.pow_le_pow_right
abbrev pos_pow_of_pos := @Nat.pow_pos
@[norm_cast]
theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
protected theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
match n with
| 0 => rfl
| n + 1 =>
simp only [Nat.pow_succ, Int.pow_succ, natCast_mul, natCast_pow _ n]
simp only [Nat.pow_succ, Int.pow_succ, Int.natCast_mul, Int.natCast_pow _ n]
@[simp]
protected theorem two_pow_pred_sub_two_pow {w : Nat} (h : 0 < w) :

View File

@@ -13,29 +13,44 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/-- `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
a list `l : List α`, given a proof that every element of `l` in fact satisfies `P`.
`O(|l|)`. `List.pmap`, named for “partial map,” is the equivalent of `List.map` for such partial
functions.
-/
def pmap {P : α Prop} (f : a, P a β) : l : List α, (H : a l, P a) List β
| [], _ => []
| a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
Unsafe implementation of `attachWith` that takes advantage of the fact that the representation of
`List {x // P x}` is the same as the input `List α`.
(Someday, the compiler might do this optimization automatically, but until then...)
-/
@[inline] private unsafe def attachWithImpl
(l : List α) (P : α Prop) (_ : x l, P x) : List {x // P x} := unsafeCast l
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `l` to produce a new list
with the same elements but in the type `{x // P x}`. -/
/--
“Attaches” individual proofs to a list of values that satisfy a predicate `P`, returning a list of
elements in the corresponding subtype `{ x // P x }`.
`O(1)`.
-/
@[implemented_by attachWithImpl] def attachWith
(l : List α) (P : α Prop) (H : x l, P x) : List {x // P x} := pmap Subtype.mk l H
/-- `O(1)`. "Attach" the proof that the elements of `l` are in `l` to produce a new list
with the same elements but in the type `{x // x ∈ l}`. -/
/--
"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)`.
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
`List.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 (l : List α) : List {x // x l} := attachWith l _ fun _ => id
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
@@ -650,11 +665,19 @@ 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 an intermediate step by lemmas such as `map_subtype`,
and is ideally subsequently simplified away by `unattach_attach`.
Maps a list 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 [List.unattach, -List.map_subtype]` to unfold.
This is the inverse of `List.attachWith` and a synonym for `l.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 [List.unattach, -List.map_subtype]`.
-/
def unattach {α : Type _} {p : α Prop} (l : List { x // p x }) : List α := l.map (·.val)
@@ -662,6 +685,10 @@ def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α :
@[simp] theorem unattach_cons {p : α Prop} {a : { x // p x }} {l : List { x // p x }} :
(a :: l).unattach = a.val :: l.unattach := rfl
@[simp] theorem mem_unattach {p : α Prop} {l : List { x // p x }} {a} :
a l.unattach h : p a, a, h l := by
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
@[simp] theorem length_unattach {p : α Prop} {l : List { x // p x }} :
l.unattach.length = l.length := by
unfold unattach
@@ -766,6 +793,16 @@ and simplifies these to the function directly taking the value.
simp [hf, find?_cons]
split <;> simp [ih]
@[simp] theorem all_subtype {p : α Prop} {l : List { x // p x }} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) :
l.all f = l.unattach.all g := by
simp [all_eq, hf]
@[simp] theorem any_subtype {p : α Prop} {l : List { x // p x }} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) :
l.any f = l.unattach.any g := by
simp [any_eq, hf]
/-! ### Simp lemmas pushing `unattach` inwards. -/
@[simp] theorem unattach_filter {p : α Prop} {l : List { x // p x }}

File diff suppressed because it is too large Load Diff

View File

@@ -79,10 +79,16 @@ theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a
/-! ### getD -/
/--
Returns the `i`-th element in the list (zero-based).
Returns the element at the provided index, counting from `0`. Returns `fallback` if the index is out
of bounds.
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
See also `get?` and `get!`.
To return an `Option` depending on whether the index is in bounds, use `as[i]?`. To panic if the
index is out of bounds, use `as[i]!`.
Examples:
* `["spring", "summer", "fall", "winter"].getD 2 "never" = "fall"`
* `["spring", "summer", "fall", "winter"].getD 0 "never" = "spring"`
* `["spring", "summer", "fall", "winter"].getD 4 "never" = "never"`
-/
def getD (as : List α) (i : Nat) (fallback : α) : α :=
as[i]?.getD fallback
@@ -92,10 +98,16 @@ def getD (as : List α) (i : Nat) (fallback : α) : α :=
/-! ### getLast! -/
/--
Returns the last element in the list.
Returns the last element in the list. Panics and returns `default` if the list is empty.
If the list is empty, this function panics when executed, and returns `default`.
See `getLast` and `getLastD` for safer alternatives.
Safer alternatives include:
* `getLast?`, which returns an `Option`,
* `getLastD`, which takes a fallback value for empty lists, and
* `getLast`, which requires a proof that the list is non-empty.
Examples:
* `["circle", "rectangle"].getLast! = "rectangle"`
* `["circle"].getLast! = "circle"`
-/
def getLast! [Inhabited α] : List α α
| [] => panic! "empty list"
@@ -106,10 +118,12 @@ def getLast! [Inhabited α] : List αα
/-! ### head! -/
/--
Returns the first element in the list.
Returns the first element in the list. If the list is empty, panics and returns `default`.
If the list is empty, this function panics when executed, and returns `default`.
See `head` and `headD` for safer alternatives.
Safer alternatives include:
* `List.head`, which requires a proof that the list is non-empty,
* `List.head?`, which returns an `Option`, and
* `List.headD`, which returns an explicitly-provided fallback value on empty lists.
-/
def head! [Inhabited α] : List α α
| [] => panic! "empty list"
@@ -118,10 +132,17 @@ def head! [Inhabited α] : List αα
/-! ### tail! -/
/--
Drops the first element of the list.
Drops the first element of a nonempty list, returning the tail. If the list is empty, this function
panics when executed and returns the empty list.
If the list is empty, this function panics when executed, and returns the empty list.
See `tail` and `tailD` for safer alternatives.
Safer alternatives include
* `tail`, which returns the empty list without panicking,
* `tail?`, which returns an `Option`, and
* `tailD`, which returns a fallback value when passed the empty list.
Examples:
* `["apple", "banana", "grape"].tail! = ["banana", "grape"]`
* `["banana", "grape"].tail! = ["grape"]`
-/
def tail! : List α List α
| [] => panic! "empty list"
@@ -132,17 +153,30 @@ def tail! : List α → List α
/-! ### partitionM -/
/--
Monadic generalization of `List.partition`.
Returns a pair of lists that together contain all the elements of `as`. The first list contains
those elements for which the monadic predicate `p` returns `true`, and the second contains those for
which `p` returns `false`. The list's elements are examined in order, from left to right.
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic` or `Init.Data.List.Control`.
```
This is a monadic version of `List.partition`.
Example:
```lean example
def posOrNeg (x : Int) : Except String Bool :=
if x > 0 then pure true
else if x < 0 then pure false
else throw "Zero is not positive or negative"
partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
```
```lean example
#eval [-1, 2, 3].partitionM posOrNeg
```
```output
Except.ok ([2, 3], [-1])
```
```lean example
#eval [0, 2, 3].partitionM posOrNeg
```
```output
Except.error "Zero is not positive or negative"
```
-/
@[inline] def partitionM [Monad m] (p : α m Bool) (l : List α) : m (List α × List α) :=
@@ -162,12 +196,12 @@ where
/-! ### partitionMap -/
/--
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
whilst partitioning the result into a pair of lists, `List β × List γ`,
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
```
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
```
Applies a function that returns a disjoint union to each element of a list, collecting the `Sum.inl`
and `Sum.inr` results into separate lists.
Examples:
* `[0, 1, 2, 3].partitionMap (fun x => if x % 2 = 0 then .inl x else .inr x) = ([0, 2], [1, 3])`
* `[0, 1, 2, 3].partitionMap (fun x => if x = 0 then .inl x else .inr x) = ([0], [1, 2, 3])`
-/
@[inline] def partitionMap (f : α β γ) (l : List α) : List β × List γ := go l #[] #[] where
/-- Auxiliary for `partitionMap`:
@@ -199,19 +233,30 @@ For verification purposes, `List.mapMono = List.map`.
return b' :: bs'
/--
Monomorphic `List.mapM`. The internal implementation uses pointer equality, and does not allocate a new list
if the result of each `f a` is a pointer equal value `a`.
Applies a monadic function to each element of a list, returning the list 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 list if the result of each function call is
pointer-equal to its argument.
-/
@[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α m α) : m (List α) :=
match as with
| [] => return []
| a :: as => return ( f a) :: ( mapMonoM as f)
/--
Applies a function to each element of a list, returning the list 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 list if the result of each function call is
pointer-equal to its argument.
For verification purposes, `List.mapMono = List.map`.
-/
def mapMono (as : List α) (f : α α) : List α :=
Id.run <| as.mapMonoM f
/-! ## Additional lemmas required for bootstrapping `Array`. -/
@[simp]
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as ++ bs).length} :
(as ++ bs)[i] = as[i] := by
induction as generalizing i with
@@ -221,6 +266,7 @@ theorem getElem_append_left {as bs : List α} (h : i < as.length) {h' : i < (as
| zero => rfl
| succ i => apply ih
@[simp]
theorem getElem_append_right {as bs : List α} {i : Nat} (h₁ : as.length i) {h₂} :
(as ++ bs)[i]'h₂ =
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by

View File

@@ -49,8 +49,9 @@ 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
results.
See `List.forM` for the variant that discards the results.
See `List.mapA` for the variant that works with `Applicative`.
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
more convenient to reason about. `List.forM` is the variant that discards the results and
`List.mapA` is the variant that works with `Applicative`.
-/
@[inline]
def mapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m β) (as : List α) : m (List β) :=
@@ -60,15 +61,15 @@ def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α
loop as []
/--
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list of
results.
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list
of results.
NB: If `m` is also a `Monad`, then using `mapM` can be more efficient.
If `m` is also a `Monad`, then using `mapM` can be more efficient.
See `List.forA` for the variant that discards the results.
See `List.mapM` for the variant that works with `Monad`.
See `List.forA` for the variant that discards the results. See `List.mapM` for the variant that
works with `Monad`.
**Warning**: this function is not tail-recursive, meaning that it may fail with a stack overflow on long lists.
This function is not tail-recursive, so it may fail with a stack overflow on long lists.
-/
@[specialize]
def mapA {m : Type u Type v} [Applicative m] {α : Type w} {β : Type u} (f : α m β) : List α m (List β)
@@ -76,10 +77,10 @@ def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f
| a::as => List.cons <$> f a <*> mapA f as
/--
Applies the monadic action `f` on every element in the list, left-to-right.
Applies the monadic action `f` to every element in the list, in order.
See `List.mapM` for the variant that collects results.
See `List.forA` for the variant that works with `Applicative`.
`List.mapM` is a variant that collects results. `List.forA` is a variant that works on any
`Applicative`.
-/
@[specialize]
protected def forM {m : Type u Type v} [Monad m] {α : Type w} (as : List α) (f : α m PUnit) : m PUnit :=
@@ -88,12 +89,11 @@ protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α
| a :: as => do f a; List.forM as f
/--
Applies the applicative action `f` on every element in the list, left-to-right.
Applies the applicative action `f` to every element in the list, in order.
NB: If `m` is also a `Monad`, then using `forM` can be more efficient.
If `m` is also a `Monad`, then using `List.forM` can be more efficient.
See `List.mapA` for the variant that collects results.
See `List.forM` for the variant that works with `Monad`.
`List.mapA` is a variant that collects results.
-/
@[specialize]
def forA {m : Type u Type v} [Applicative m] {α : Type w} (as : List α) (f : α m PUnit) : m PUnit :=
@@ -110,8 +110,28 @@ def filterAuxM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) :
filterAuxM f t (cond b (h :: acc) acc)
/--
Applies the monadic predicate `p` on every element in the list, left-to-right, and returns those
elements `x` for which `p x` returns `true`.
Applies the monadic predicate `p` to every element in the list, in order from left to right, and
returns the list of elements for which `p` returns `true`.
`O(|l|)`.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterM fun x => do
IO.println s!"Checking {x}"
return x < 3
```
```output
Checking 1
Checking 2
Checking 5
Checking 2
Checking 7
Checking 7
```
```output
[1, 2, 2]
```
-/
@[inline]
def filterM {m : Type Type v} [Monad m] {α : Type} (p : α m Bool) (as : List α) : m (List α) := do
@@ -119,16 +139,56 @@ def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as
pure as.reverse
/--
Applies the monadic predicate `p` on every element in the list, right-to-left, and returns those
elements `x` for which `p x` returns `true`.
Applies the monadic predicate `p` on every element in the list in reverse order, from right to left,
and returns those elements for which `p` returns `true`. The elements of the returned list are in
the same order as in the input list.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterRevM fun x => do
IO.println s!"Checking {x}"
return x < 3
```
```output
Checking 7
Checking 7
Checking 2
Checking 5
Checking 2
Checking 1
```
```output
[1, 2, 2]
```
-/
@[inline]
def filterRevM {m : Type Type v} [Monad m] {α : Type} (p : α m Bool) (as : List α) : m (List α) :=
filterAuxM p as.reverse []
/--
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns those
results `y` for which `f x` returns `some y`.
Applies a monadic function that returns an `Option` to each element of a list, collecting the
non-`none` values.
`O(|l|)`.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterMapM fun x => do
IO.println s!"Examining {x}"
if x > 2 then return some (2 * x)
else return none
```
```output
Examining 1
Examining 2
Examining 5
Examining 2
Examining 7
Examining 7
```
```output
[10, 14, 14]
```
-/
@[inline]
def filterMapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (Option β)) (as : List α) : m (List β) :=
@@ -141,8 +201,8 @@ def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
loop as []
/--
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns the
concatenation of the results.
Applies a monadic function that returns a list to each element of a list, from left to right, and
concatenates the resulting lists.
-/
@[inline]
def flatMapM {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (List β)) (as : List α) : m (List β) :=
@@ -153,14 +213,20 @@ def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f :
loop as (bs' :: bs)
loop as []
/--
Folds a monadic function over a list from left to right:
```
foldlM f x₀ [a, b, c] = do
let x₁ ← f x₀ a
let x₂ ← f x₁ b
let x₃ ← f x₂ c
pure x₃
Folds a monadic function over a list from the left, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in order, using `f`.
Example:
```lean example
example [Monad m] (f : α → β → m α) :
List.foldlM (m := m) f x₀ [a, b, c] = (do
let x₁ ← f x₀ a
let x₂ ← f x₁ b
let x₃ ← f x₂ c
pure x₃)
:= by rfl
```
-/
@[specialize]
@@ -176,13 +242,18 @@ def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s
simp [List.foldlM]
/--
Folds a monadic function over a list from right to left:
```
foldrM f x₀ [a, b, c] = do
let x₁ ← f c x₀
let x₂ ← f b x₁
let x₃ ← f a x₂
pure x₃
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`.
Example:
```lean example
example [Monad m] (f : α → β → m β) :
List.foldrM (m := m) f x₀ [a, b, c] = (do
let x₁ ← f c x₀
let x₂ ← f b x₁
let x₃ ← f a x₂
pure x₃)
:= by rfl
```
-/
@[inline]
@@ -192,32 +263,70 @@ def foldrM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} (f : α
@[simp] theorem foldrM_nil [Monad m] (f : α β m β) (b) : [].foldrM f b = pure b := rfl
/--
Maps `f` over the list and collects the results with `<|>`.
```
firstM f [a, b, c] = f a <|> f b <|> f c <|> failure
```
Maps `f` over the list and collects the results with `<|>`. The result for the end of the list is
`failure`.
Examples:
* `[[], [1, 2], [], [2]].firstM List.head? = some 1`
* `[[], [], []].firstM List.head? = none`
* `[].firstM List.head? = none`
-/
@[specialize]
def firstM {m : Type u Type v} [Alternative m] {α : Type w} {β : Type u} (f : α m β) : List α m β
| [] => failure
| a::as => f a <|> firstM f as
/--
Returns true if the monadic predicate `p` returns `true` for any element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `true`. The elements in `l` are examined in
order from left to right.
-/
@[specialize]
def anyM {m : Type Type u} [Monad m] {α : Type v} (f : α m Bool) : List α m Bool
def anyM {m : Type Type u} [Monad m] {α : Type v} (p : α m Bool) : (l : List α) m Bool
| [] => pure false
| a::as => do
match ( f a) with
match ( p a) with
| true => pure true
| false => anyM f as
| false => anyM p as
/--
Returns true if the monadic predicate `p` returns `true` for every element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `false`. The elements in `l` are examined in
order from left to right.
-/
@[specialize]
def allM {m : Type Type u} [Monad m] {α : Type v} (f : α m Bool) : List α m Bool
def allM {m : Type Type u} [Monad m] {α : Type v} (p : α m Bool) : (l : List α) m Bool
| [] => pure true
| a::as => do
match ( f a) with
| true => allM f as
match ( p a) with
| true => allM p as
| false => pure false
/--
Returns the first element of the list for which the monadic predicate `p` returns `true`, or `none`
if no such element is found. Elements of the list are checked in order.
`O(|l|)`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].findM? fun i => do
if i < 5 then
return true
if i ≤ 6 then
IO.println s!"Almost! {i}"
return false
```
```output
Almost! 6
Almost! 5
```
```output
some 1
```
-/
@[specialize]
def findM? {m : Type Type u} [Monad m] {α : Type} (p : α m Bool) : List α m (Option α)
| [] => pure none
@@ -227,15 +336,43 @@ def findM? {m : Type → Type u} [Monad m] {α : Type} (p : α → m Bool) : Lis
| false => findM? p as
@[simp]
theorem findM?_id (p : α Bool) (as : List α) : findM? (m := Id) p as = as.find? p := by
theorem findM?_pure {m} [Monad m] [LawfulMonad m] (p : α Bool) (as : List α) :
findM? (m := m) (pure <| p ·) as = pure (as.find? p) := by
induction as with
| nil => rfl
| cons a as ih =>
simp only [findM?, find?]
cases p a with
| true => rfl
| false => rw [ih]; rfl
| true => simp
| false => simp [ih]
@[simp]
theorem findM?_id (p : α Bool) (as : List α) : findM? (m := Id) p as = as.find? p :=
findM?_pure _ _
/--
Returns the first non-`none` result of applying the monadic function `f` to each element of the
list, in order. Returns `none` if `f` returns `none` for all elements.
`O(|l|)`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].findSomeM? fun i => do
if i < 5 then
return some (i * 10)
if i ≤ 6 then
IO.println s!"Almost! {i}"
return none
```
```output
Almost! 6
Almost! 5
```
```output
some 10
```
-/
@[specialize]
def findSomeM? {m : Type u Type v} [Monad m] {α : Type w} {β : Type u} (f : α m (Option β)) : List α m (Option β)
| [] => pure none
@@ -245,14 +382,19 @@ def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
| none => findSomeM? f as
@[simp]
theorem findSomeM?_id (f : α Option β) (as : List α) : findSomeM? (m := Id) f as = as.findSome? f := by
theorem findSomeM?_pure [Monad m] [LawfulMonad m] (f : α Option β) (as : List α) :
findSomeM? (m := m) (pure <| f ·) as = pure (as.findSome? f) := by
induction as with
| nil => rfl
| cons a as ih =>
simp only [findSomeM?, findSome?]
cases f a with
| some b => rfl
| none => rw [ih]; rfl
| some b => simp
| none => simp [ih]
@[simp]
theorem findSomeM?_id (f : α Option β) (as : List α) : findSomeM? (m := Id) f as = as.findSome? f :=
findSomeM?_pure _ _
theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] (p : α m Bool) (as : List α) :
as.findM? p = as.findSomeM? fun a => return if ( p a) then some a else none := by

View File

@@ -11,7 +11,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/-- `finRange n` lists all elements of `Fin n` in order -/
/--
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)`
-/
def finRange (n : Nat) : List (Fin n) := ofFn fun i => i
@[simp] theorem length_finRange (n) : (List.finRange n).length = n := by

View File

@@ -17,7 +17,8 @@ then at runtime you will get non-tail recursive versions of the following defini
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
-- TODO: restore after an update-stage0
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
@@ -49,7 +50,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### set -/
/-- Tail recursive version of `List.set`. -/
/--
Replaces the value at (zero-based) index `n` in `l` with `a`. If the index is out of bounds, then
the list is returned unmodified.
This is a tail-recursive version of `List.set` that's used at runtime.
Examples:
* `["water", "coffee", "soda", "juice"].set 1 "tea" = ["water", "tea", "soda", "juice"]`
* `["water", "coffee", "soda", "juice"].set 4 "tea" = ["water", "coffee", "soda", "juice"]`
-/
@[inline] def setTR (l : List α) (n : Nat) (a : α) : List α := go l n #[] where
/-- Auxiliary for `setTR`: `setTR.go l a xs n acc = acc.toList ++ set xs a`,
unless `n ≥ l.length` in which case it returns `l` -/
@@ -69,7 +79,22 @@ The following operations are given `@[csimp]` replacements below:
/-! ### filterMap -/
/-- Tail recursive version of `filterMap`. -/
/--
Applies a function that returns an `Option` to each element of a list, collecting the non-`none`
values.
`O(|l|)`. This is a tail-recursive version of `List.filterMap`, used at runtime.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterMapTR fun x =>
if x > 2 then some (2 * x) else none
```
```output
[10, 14, 14]
```
-/
@[inline] def filterMapTR (f : α Option β) (l : List α) : List β := go l #[] where
/-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/
@[specialize] go : List α Array β List β
@@ -90,7 +115,17 @@ The following operations are given `@[csimp]` replacements below:
/-! ### foldr -/
/-- Tail recursive version of `List.foldr`. -/
/--
Folds a 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 reverse order, using `f`.
`O(|l|)`. This is the tail-recursive replacement for `List.foldr` in runtime code.
Examples:
* `[a, b, c].foldrTR f init = f a (f b (f c init))`
* `[1, 2, 3].foldrTR (toString · ++ ·) "" = "123"`
* `[1, 2, 3].foldrTR (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
-/
@[specialize] def foldrTR (f : α β β) (init : β) (l : List α) : β := l.toArray.foldr f init
@[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by
@@ -98,7 +133,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### flatMap -/
/-- Tail recursive version of `List.flatMap`. -/
/--
Applies a function that returns a list to each element of a list, and concatenates the resulting
lists.
This is the tail-recursive version of `List.flatMap` that's used at runtime.
Examples:
* `[2, 3, 2].flatMapTR List.range = [0, 1, 0, 1, 2, 0, 1]`
* `["red", "blue"].flatMapTR String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
-/
@[inline] def flatMapTR (f : α List β) (as : List α) : List β := go as #[] where
/-- Auxiliary for `flatMap`: `flatMap.go f as = acc.toList ++ bind f as` -/
@[specialize] go : List α Array β List β
@@ -114,7 +158,15 @@ The following operations are given `@[csimp]` replacements below:
/-! ### flatten -/
/-- Tail recursive version of `List.flatten`. -/
/--
Concatenates a list of lists into a single list, preserving the order of the elements.
`O(|flatten L|)`. This is a tail-recursive version of `List.flatten`, used in runtime code.
Examples:
* `[["a"], ["b", "c"]].flattenTR = ["a", "b", "c"]`
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flattenTR = ["a", "b", "c", "d", "e", "f"]`
-/
@[inline] def flattenTR (l : List (List α)) : List α := l.flatMapTR id
@[csimp] theorem flatten_eq_flattenTR : @flatten = @flattenTR := by
@@ -124,7 +176,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### take -/
/-- Tail recursive version of `List.take`. -/
/--
Extracts the first `n` elements of `xs`, or the whole list if `n` is greater than `xs.length`.
`O(min n |xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
Examples:
* `[a, b, c, d, e].takeTR 0 = []`
* `[a, b, c, d, e].takeTR 3 = [a, b, c]`
* `[a, b, c, d, e].takeTR 6 = [a, b, c, d, e]`
-/
@[inline] def takeTR (n : Nat) (l : List α) : List α := go l n #[] where
/-- Auxiliary for `take`: `take.go l xs n acc = acc.toList ++ take n xs`,
unless `n ≥ xs.length` in which case it returns `l`. -/
@@ -146,7 +207,17 @@ The following operations are given `@[csimp]` replacements below:
/-! ### takeWhile -/
/-- Tail recursive version of `List.takeWhile`. -/
/--
Returns the longest initial segment of `xs` for which `p` returns true.
`O(|xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
Examples:
* `[7, 6, 4, 8].takeWhileTR (· > 5) = [7, 6]`
* `[7, 6, 6, 5].takeWhileTR (· > 5) = [7, 6, 6]`
* `[7, 6, 6, 8].takeWhileTR (· > 5) = [7, 6, 6, 8]`
-/
@[inline] def takeWhileTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `takeWhile`: `takeWhile.go p l xs acc = acc.toList ++ takeWhile p xs`,
unless no element satisfying `p` is found in `xs` in which case it returns `l`. -/
@@ -169,7 +240,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### dropLast -/
/-- Tail recursive version of `dropLast`. -/
/--
Removes the last element of the list, if one exists.
This is a tail-recursive version of `List.dropLast`, used at runtime.
Examples:
* `[].dropLastTR = []`
* `["tea"].dropLastTR = []`
* `["tea", "coffee", "juice"].dropLastTR = ["tea", "coffee"]`
-/
@[inline] def dropLastTR (l : List α) : List α := l.toArray.pop.toList
@[csimp] theorem dropLast_eq_dropLastTR : @dropLast = @dropLastTR := by
@@ -179,7 +259,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### replace -/
/-- Tail recursive version of `List.replace`. -/
/--
Replaces the first element of the list `l` that is equal to `a` with `b`. If no element is equal to
`a`, then the list is returned unchanged.
`O(|l|)`. This is a tail-recursive version of `List.replace` that's used in runtime code.
Examples:
* `[1, 4, 2, 3, 3, 7].replaceTR 3 6 = [1, 4, 2, 6, 3, 7]`
* `[1, 4, 2, 3, 3, 7].replaceTR 5 6 = [1, 4, 2, 3, 3, 7]`
-/
@[inline] def replaceTR [BEq α] (l : List α) (b c : α) : List α := go l #[] where
/-- Auxiliary for `replace`: `replace.go l b c xs acc = acc.toList ++ replace xs b c`,
unless `b` is not found in `xs` in which case it returns `l`. -/
@@ -202,42 +291,76 @@ The following operations are given `@[csimp]` replacements below:
/-! ### modify -/
/-- Tail-recursive version of `modify`. -/
def modifyTR (f : α α) (n : Nat) (l : List α) : List α := go l n #[] where
/-- Auxiliary for `modifyTR`: `modifyTR.go f l n acc = acc.toList ++ modify f n l`. -/
/--
Replaces the element at the given index, if it exists, with the result of applying `f` to it.
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]`
-/
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`. -/
go : List α Nat Array α List α
| [], _, acc => acc.toList
| a :: l, 0, acc => acc.toListAppend (f a :: l)
| a :: l, n+1, acc => go l n (acc.push a)
| a :: l, i+1, acc => go l i (acc.push a)
theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify f i l
| [], n => by cases n <;> simp [modifyTR.go, modify]
theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify l i f
| [], i => by cases i <;> simp [modifyTR.go, modify]
| a :: l, 0 => by simp [modifyTR.go, modify]
| a :: l, n+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
| a :: l, i+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
@[csimp] theorem modify_eq_modifyTR : @modify = @modifyTR := by
funext α f n l; simp [modifyTR, modifyTR_go_eq]
funext α l i f; simp [modifyTR, modifyTR_go_eq]
/-! ### insertIdx -/
/-- Tail-recursive version of `insertIdx`. -/
@[inline] def insertIdxTR (n : Nat) (a : α) (l : List α) : List α := go n l #[] where
/--
Inserts an element into a list at the specified index. If the index is greater than the length of
the list, then the list is returned unmodified.
In other words, the new element is inserted into the list `l` after the first `i` elements of `l`.
This is a tail-recursive version of `List.insertIdx`, used at runtime.
Examples:
* `["tues", "thur", "sat"].insertIdxTR 1 "wed" = ["tues", "wed", "thur", "sat"]`
* `["tues", "thur", "sat"].insertIdxTR 2 "wed" = ["tues", "thur", "wed", "sat"]`
* `["tues", "thur", "sat"].insertIdxTR 3 "wed" = ["tues", "thur", "sat", "wed"]`
* `["tues", "thur", "sat"].insertIdxTR 4 "wed" = ["tues", "thur", "sat"]`
-/
@[inline] def insertIdxTR (l : List α) (n : Nat) (a : α) : List α := go n l #[] where
/-- Auxiliary for `insertIdxTR`: `insertIdxTR.go a n l acc = acc.toList ++ insertIdx n a l`. -/
go : Nat List α Array α List α
| 0, l, acc => acc.toListAppend (a :: l)
| _, [], acc => acc.toList
| n+1, a :: l, acc => go n l (acc.push a)
theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx i a l
theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a
| 0, l | _+1, [] => by simp [insertIdxTR.go, insertIdx]
| n+1, a :: l => by simp [insertIdxTR.go, insertIdx, insertIdxTR_go_eq n l]
@[csimp] theorem insertIdx_eq_insertIdxTR : @insertIdx = @insertIdxTR := by
funext α f n l; simp [insertIdxTR, insertIdxTR_go_eq]
funext α l i a; simp [insertIdxTR, insertIdxTR_go_eq]
/-! ### erase -/
/-- Tail recursive version of `List.erase`. -/
/--
Removes the first occurrence of `a` from `l`. If `a` does not occur in `l`, the list is returned
unmodified.
`O(|l|)`.
This is a tail-recursive version of `List.erase`, used in runtime code.
Examples:
* `[1, 5, 3, 2, 5].eraseTR 5 = [1, 3, 2, 5]`
* `[1, 5, 3, 2, 5].eraseTR 6 = [1, 5, 3, 2, 5]`
-/
@[inline] def eraseTR [BEq α] (l : List α) (a : α) : List α := go l #[] where
/-- Auxiliary for `eraseTR`: `eraseTR.go l a xs acc = acc.toList ++ erase xs a`,
unless `a` is not present in which case it returns `l` -/
@@ -257,7 +380,17 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
· rw [IH] <;> simp_all
· simp
/-- Tail-recursive version of `eraseP`. -/
/--
Removes the first element of a list for which `p` returns `true`. If no element satisfies `p`, then
the list is returned unchanged.
This is a tail-recursive version of `eraseP`, used at runtime.
Examples:
* `[2, 1, 2, 1, 3, 4].erasePTR (· < 2) = [2, 2, 1, 3, 4]`
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 2) = [2, 1, 2, 1, 4]`
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 8) = [2, 1, 2, 1, 3, 4]`
-/
@[inline] def erasePTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
@@ -277,7 +410,20 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
/-! ### eraseIdx -/
/-- Tail recursive version of `List.eraseIdx`. -/
/--
Removes the element at the specified index. If the index is out of bounds, the list is returned
unmodified.
`O(i)`.
This is a tail-recursive version of `List.eraseIdx`, used at runtime.
Examples:
* `[0, 1, 2, 3, 4].eraseIdxTR 0 = [1, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdxTR 1 = [0, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdxTR 5 = [0, 1, 2, 3, 4]`
-/
@[inline] def eraseIdxTR (l : List α) (n : Nat) : List α := go l n #[] where
/-- Auxiliary for `eraseIdxTR`: `eraseIdxTR.go l n xs acc = acc.toList ++ eraseIdx xs a`,
unless `a` is not present in which case it returns `l` -/
@@ -303,7 +449,18 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
/-! ### zipWith -/
/-- Tail recursive version of `List.zipWith`. -/
/--
Applies a function to the corresponding elements of two lists, stopping at the end of the shorter
list.
`O(min |xs| |ys|)`. This is a tail-recursive version of `List.zipWith` that's used at runtime.
Examples:
* `[1, 2].zipWithTR (· + ·) [5, 6] = [6, 8]`
* `[1, 2, 3].zipWithTR (· + ·) [5, 6, 10] = [6, 8, 13]`
* `[].zipWithTR (· + ·) [5, 6] = []`
* `[x₁, x₂, x₃].zipWithTR f [y₁, y₂, y₃, y₄] = [f x₁ y₁, f x₂ y₂, f x₃ y₃]`
-/
@[inline] def zipWithTR (f : α β γ) (as : List α) (bs : List β) : List γ := go as bs #[] where
/-- Auxiliary for `zipWith`: `zipWith.go f as bs acc = acc.toList ++ zipWith f as bs` -/
go : List α List β Array γ List γ
@@ -321,7 +478,16 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
/-! ### zipIdx -/
/-- Tail recursive version of `List.zipIdx`. -/
/--
Pairs each element of a list with its index, optionally starting from an index other than `0`.
`O(|l|)`. This is a tail-recursive version of `List.zipIdx` that's used at runtime.
Examples:
* `[a, b, c].zipIdxTR = [(a, 0), (b, 1), (c, 2)]`
* `[a, b, c].zipIdxTR 5 = [(a, 5), (b, 6), (c, 7)]`
-/
def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) :=
let as := l.toArray
(as.foldr (fun a (n, acc) => (n-1, (a, n-1) :: acc)) (n + as.size, [])).2
@@ -363,8 +529,18 @@ theorem enumFrom_eq_enumFromTR : @enumFrom = @enumFromTR := by
/-! ### intercalate -/
set_option linter.listVariables false in
/-- Tail recursive version of `List.intercalate`. -/
def intercalateTR (sep : List α) : List (List α) List α
/--
Alternates the lists in `xs` with the separator `sep`.
This is a tail-recursive version of `List.intercalate` used at runtime.
Examples:
* `List.intercalateTR sep [] = []`
* `List.intercalateTR sep [a] = a`
* `List.intercalateTR sep [a, b] = a ++ sep ++ b`
* `List.intercalateTR sep [a, b, c] = a ++ sep ++ b ++ sep ++ c`
-/
def intercalateTR (sep : List α) : (xs : List (List α)) List α
| [] => []
| [x] => x
| x::xs => go sep.toArray x xs #[]

View File

@@ -2535,6 +2535,14 @@ theorem flatMap_reverse {β} (l : List α) (f : α → List β) : (l.reverse.fla
simp only [foldrM]
induction l <;> simp_all
@[simp] theorem foldlM_pure [Monad m] [LawfulMonad m] (f : β α β) (b) (l : List α) :
l.foldlM (m := m) (pure <| f · ·) b = pure (l.foldl f b) := by
induction l generalizing b <;> simp [*]
@[simp] theorem foldrM_pure [Monad m] [LawfulMonad m] (f : α β β) (b) (l : List α) :
l.foldrM (m := m) (pure <| f · ·) b = pure (l.foldr f b) := by
induction l generalizing b <;> simp [*]
theorem foldl_eq_foldlM (f : β α β) (b) (l : List α) :
l.foldl f b = l.foldlM (m := Id) f b := by
induction l generalizing b <;> simp [*, foldl]
@@ -2689,12 +2697,20 @@ theorem foldr_hom (f : β₁ → β₂) (g₁ : α → β₁ → β₁) (g₂ :
induction l <;> simp [*, H]
/--
Prove a proposition about the result of `List.foldl`,
by proving it for the initial data,
and the implication that the operation applied to any element of the list preserves the property.
A reasoning principle for proving propositions about the result of `List.foldl` by establishing an
invariant that is true for the initial data and preserved by the operation being folded.
The motive can take values in `Sort _`, so this may be used to construct data,
as well as to prove propositions.
Because the motive can return a type in any sort, this function may be used to construct data as
well as to prove propositions.
Example:
```lean example
example {xs : List Nat} : xs.foldl (· + ·) 1 > 0 := by
apply List.foldlRecOn
. show 0 < 1; trivial
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < b + a
intros; omega
```
-/
def foldlRecOn {motive : β Sort _} : (l : List α) (op : β α β) (b : β) (_ : motive b)
(_ : (b : β) (_ : motive b) (a : α) (_ : a l), motive (op b a)), motive (List.foldl op b l)
@@ -2715,12 +2731,20 @@ def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α
rfl
/--
Prove a proposition about the result of `List.foldr`,
by proving it for the initial data,
and the implication that the operation applied to any element of the list preserves the property.
A reasoning principle for proving propositions about the result of `List.foldr` by establishing an
invariant that is true for the initial data and preserved by the operation being folded.
The motive can take values in `Sort _`, so this may be used to construct data,
as well as to prove propositions.
Because the motive can return a type in any sort, this function may be used to construct data as
well as to prove propositions.
Example:
```lean example
example {xs : List Nat} : xs.foldr (· + ·) 1 > 0 := by
apply List.foldrRecOn
. show 0 < 1; trivial
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < a + b
intros; omega
```
-/
def foldrRecOn {motive : β Sort _} : (l : List α) (op : α β β) (b : β) (_ : motive b)
(_ : (b : β) (_ : motive b) (a : α) (_ : a l), motive (op a b)), motive (List.foldr op b l)

View File

@@ -19,8 +19,11 @@ namespace List
/-! ## Operations using indexes -/
/--
Given a list `as = [a₀, a₁, ...]` and a function `f : (i : Nat) → α → (h : i < as.length) → β`, returns the list
`[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
Applies a function to each element of the list along with the index at which that element is found,
returning the list of results. In addition to the index, the function is also provided with a proof
that the index is valid.
`List.mapIdx` is a variant that does not provide the function with evidence that the index is valid.
-/
@[inline] def mapFinIdx (as : List α) (f : (i : Nat) α (h : i < as.length) β) : List β :=
go as #[] (by simp)
@@ -33,8 +36,11 @@ where
go as (acc.push (f acc.size a (by simp at h; omega))) (by simp at h ; omega)
/--
Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁, ...]`, returns the list
`[f 0 a₀, f 1 a₁, ...]`.
Applies a function to each element of the list along with the index at which that element is found,
returning the list of results.
`List.mapFinIdx` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline] def mapIdx (f : Nat α β) (as : List α) : List β := go as #[] where
/-- Auxiliary for `mapIdx`:
@@ -44,8 +50,12 @@ Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁,
| a :: as, acc => go as (acc.push (f acc.size a))
/--
Given a list `as = [a₀, a₁, ...]` and a monadic function `f : (i : Nat) → α → (h : i < as.length) → m β`,
returns the list `[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
Applies a monadic function to each element of the list along with the index at which that element is
found, returning the list of results. In addition to the index, the function is also provided with a
proof that the index is valid.
`List.mapIdxM` is a variant that does not provide the function with evidence that the index is
valid.
-/
@[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) α (h : i < as.length) m β) : m (List β) :=
go as #[] (by simp)
@@ -58,8 +68,11 @@ where
go as (acc.push ( f acc.size a (by simp at h; omega))) (by simp at h ; omega)
/--
Given a monadic function `f : Nat → α → m β` and `as : List α`, `as = [a₀, a₁, ...]`,
returns the list `[f 0 a₀, f 1 a₁, ...]`.
Applies a monadic function to each element of the list along with the index at which that element is
found, returning the list of results.
`List.mapFinIdxM` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline] def mapIdxM [Monad m] (f : Nat α m β) (as : List α) : m (List β) := go as #[] where
/-- Auxiliary for `mapIdxM`:

View File

@@ -31,10 +31,13 @@ attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
/-! ### mapM -/
/-- Alternate (non-tail-recursive) form of mapM for proofs.
/--
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
results.
Note that we can not have this as the main definition and replace it using a `@[csimp]` lemma,
because they are only equal when `m` is a `LawfulMonad`.
This is a non-tail-recursive variant of `List.mapM` that's easier to reason about. It cannot be used
as the main definition and replaced by the tail-recursive version because they can only be proved
equal when `m` is a `LawfulMonad`.
-/
def mapM' [Monad m] (f : α m β) : List α m (List β)
| [] => pure []
@@ -56,9 +59,13 @@ theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α)
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α m β) :
(a :: l).mapM f = (return ( f a) :: ( l.mapM f)) := by simp [ mapM'_eq_mapM, mapM']
@[simp] theorem mapM_id {l : List α} {f : α Id β} : l.mapM f = l.map f := by
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] (l : List α) (f : α β) :
l.mapM (m := m) (pure <| f ·) = pure (l.map f) := by
induction l <;> simp_all
@[simp] theorem mapM_id {l : List α} {f : α Id β} : l.mapM f = l.map f :=
mapM_pure _ _
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α m β) {l₁ l₂ : List α} :
(l₁ ++ l₂).mapM f = (return ( l₁.mapM f) ++ ( l₂.mapM f)) := by induction l₁ <;> simp [*]
@@ -323,7 +330,7 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
simp only [forIn'_eq_foldlM]
induction l.attach generalizing init <;> simp_all
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
@[simp] theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(l : List α) (f : (a : α) a l β β) (init : β) :
forIn' l init (fun a m b => pure (.yield (f a m b))) =
pure (f := m) (l.attach.foldl (fun b a, h => f a h b) init) := by
@@ -376,7 +383,7 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
simp only [forIn_eq_foldlM]
induction l generalizing init <;> simp_all
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
@[simp] theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(l : List α) (f : α β β) (init : β) :
forIn l init (fun a b => pure (.yield (f a b))) =
pure (f := m) (l.foldl (fun b a => f a b) init) := by
@@ -395,7 +402,7 @@ theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
forIn (l.map g) init f = forIn l init fun a y => f (g a) y := by
induction l generalizing init <;> simp_all
/-! ### allM -/
/-! ### allM and anyM -/
theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α m Bool) (as : List α) :
allM p as = (! ·) <$> anyM ((! ·) <$> p ·) as := by
@@ -407,6 +414,18 @@ theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] (p : α → m Bool) (as :
funext b
split <;> simp_all
@[simp] theorem anyM_pure [Monad m] [LawfulMonad m] (p : α Bool) (as : List α) :
as.anyM (m := m) (pure <| p ·) = pure (as.any p) := by
induction as with
| nil => simp
| cons a as ih =>
simp only [anyM, ih, pure_bind, all_cons]
split <;> simp_all
@[simp] theorem allM_pure [Monad m] [LawfulMonad m] (p : α Bool) (as : List α) :
as.allM (m := m) (pure <| p ·) = pure (as.all p) := by
simp [allM_eq_not_anyM_not, all_eq_not_any_not]
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
/--
@@ -422,12 +441,12 @@ and simplifies these to the function directly taking the value.
| nil => simp
| cons a l ih => simp [ih, hf]
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : List α) (f : β α m β) :
(wfParam xs).foldlM f = xs.attach.unattach.foldlM f := by
@[wf_preprocess] theorem foldlM_wfParam [Monad m] (xs : List α) (f : β α m β) (init : β) :
(wfParam xs).foldlM f init = xs.attach.unattach.foldlM f init := by
simp [wfParam]
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : List (Subtype P)) (f : β α m β) :
xs.unattach.foldlM f = xs.foldlM fun b x, h =>
@[wf_preprocess] theorem foldlM_unattach [Monad m] (P : α Prop) (xs : List (Subtype P)) (f : β α m β) (init : β):
xs.unattach.foldlM f init = xs.foldlM (init := init) fun b x, h =>
binderNameHint b f <| binderNameHint x (f b) <| binderNameHint h () <|
f b (wfParam x) := by
simp [wfParam]
@@ -449,12 +468,12 @@ and simplifies these to the function directly taking the value.
funext b
simp [hf]
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α β m β) :
(wfParam xs).foldrM f = xs.attach.unattach.foldrM f := by
@[wf_preprocess] theorem foldrM_wfParam [Monad m] [LawfulMonad m] (xs : List α) (f : α β m β) (init : β) :
(wfParam xs).foldrM f init = xs.attach.unattach.foldrM f init := by
simp [wfParam]
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : List (Subtype P)) (f : α β m β) :
xs.unattach.foldrM f = xs.foldrM fun x, h b =>
@[wf_preprocess] theorem foldrM_unattach [Monad m] [LawfulMonad m] (P : α Prop) (xs : List (Subtype P)) (f : α β m β) (init : β) :
xs.unattach.foldrM f init = xs.foldrM (init := init) fun x, h b =>
binderNameHint x f <| binderNameHint h () <| binderNameHint b (f x) <|
f (wfParam x) b := by
simp [wfParam]

View File

@@ -13,7 +13,8 @@ Proves various lemmas about `List.insertIdx`.
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
-- TODO: restore after an update-stage0
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
open Function Nat
@@ -28,29 +29,29 @@ section InsertIdx
variable {a : α}
@[simp]
theorem insertIdx_zero (s : List α) (x : α) : insertIdx 0 x s = x :: s :=
theorem insertIdx_zero (xs : List α) (x : α) : xs.insertIdx 0 x = x :: xs :=
rfl
@[simp]
theorem insertIdx_succ_nil (n : Nat) (a : α) : insertIdx (n + 1) a [] = [] :=
theorem insertIdx_succ_nil (n : Nat) (a : α) : ([] : List α).insertIdx (n + 1) a = [] :=
rfl
@[simp]
theorem insertIdx_succ_cons (s : List α) (hd x : α) (i : Nat) :
insertIdx (i + 1) x (hd :: s) = hd :: insertIdx i x s :=
theorem insertIdx_succ_cons (xs : List α) (hd x : α) (i : Nat) :
(hd :: xs).insertIdx (i + 1) x = hd :: xs.insertIdx i x :=
rfl
theorem length_insertIdx : i as, (insertIdx i a as).length = if i as.length then as.length + 1 else as.length
theorem length_insertIdx : i (as : List α), (as.insertIdx i a).length = if i as.length then as.length + 1 else as.length
| 0, _ => by simp
| n + 1, [] => by simp
| n + 1, a :: as => by
simp only [insertIdx_succ_cons, length_cons, length_insertIdx, Nat.add_le_add_iff_right]
split <;> rfl
theorem length_insertIdx_of_le_length (h : i length as) : length (insertIdx i a as) = length as + 1 := by
theorem length_insertIdx_of_le_length (h : i length as) : (as.insertIdx i a).length = as.length + 1 := by
simp [length_insertIdx, h]
theorem length_insertIdx_of_length_lt (h : length as < i) : length (insertIdx i a as) = length as := by
theorem length_insertIdx_of_length_lt (h : length as < i) : (as.insertIdx i a).length = as.length := by
simp [length_insertIdx, h]
@[simp]
@@ -60,7 +61,7 @@ theorem eraseIdx_insertIdx (i : Nat) (l : List α) : (l.insertIdx i a).eraseIdx
theorem insertIdx_eraseIdx_of_ge :
i m as,
i < length as i m insertIdx m a (as.eraseIdx i) = (as.insertIdx (m + 1) a).eraseIdx i
i < length as i m (as.eraseIdx i).insertIdx m a = (as.insertIdx (m + 1) a).eraseIdx i
| 0, 0, [], has, _ => (Nat.lt_irrefl _ has).elim
| 0, 0, _ :: as, _, _ => by simp [eraseIdx, insertIdx]
| 0, _ + 1, _ :: _, _, _ => rfl
@@ -70,7 +71,7 @@ theorem insertIdx_eraseIdx_of_ge :
theorem insertIdx_eraseIdx_of_le :
i j as,
i < length as j i insertIdx j a (as.eraseIdx i) = (as.insertIdx j a).eraseIdx (i + 1)
i < length as j i (as.eraseIdx i).insertIdx j a = (as.insertIdx j a).eraseIdx (i + 1)
| _, 0, _ :: _, _, _ => rfl
| n + 1, m + 1, a :: as, has, hmn =>
congrArg (cons a) <|
@@ -95,7 +96,7 @@ theorem mem_insertIdx {a b : α} :
or_assoc, @or_comm (a = a'), or_assoc, mem_cons]
theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length < i) :
insertIdx i x l = l := by
l.insertIdx i x = l := by
induction l generalizing i with
| nil =>
cases i
@@ -108,24 +109,24 @@ theorem insertIdx_of_length_lt (l : List α) (x : α) (i : Nat) (h : l.length <
simpa using ih _ h
@[simp]
theorem insertIdx_length_self (l : List α) (x : α) : insertIdx l.length x l = l ++ [x] := by
theorem insertIdx_length_self (l : List α) (x : α) : l.insertIdx l.length x = l ++ [x] := by
induction l with
| nil => simp
| cons x l ih => simpa using ih
theorem length_le_length_insertIdx (l : List α) (x : α) (i : Nat) :
l.length (insertIdx i x l).length := by
l.length (l.insertIdx i x).length := by
simp only [length_insertIdx]
split <;> simp
theorem length_insertIdx_le_succ (l : List α) (x : α) (i : Nat) :
(insertIdx i x l).length l.length + 1 := by
(l.insertIdx i x).length l.length + 1 := by
simp only [length_insertIdx]
split <;> simp
theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
(hk : j < (insertIdx i x l).length) :
(insertIdx i x l)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
(hk : j < (l.insertIdx i x).length) :
(l.insertIdx i x)[j] = l[j]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
induction i generalizing j l with
| zero => simp at hn
| succ n ih =>
@@ -138,8 +139,8 @@ theorem getElem_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (hn : j < i)
simpa using ih hn _
@[simp]
theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (insertIdx i x l).length) :
(insertIdx i x l)[i] = x := by
theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (l.insertIdx i x).length) :
(l.insertIdx i x)[i] = x := by
induction l generalizing i with
| nil =>
simp [length_insertIdx] at hi
@@ -153,8 +154,8 @@ theorem getElem_insertIdx_self {l : List α} {x : α} {i : Nat} (hi : i < (inser
simpa using ih hi
theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
(hk : j < (insertIdx i x l).length) :
(insertIdx i x l)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
(hk : j < (l.insertIdx i x).length) :
(l.insertIdx i x)[j] = l[j - 1]'(by simp [length_insertIdx] at hk; split at hk <;> omega) := by
induction l generalizing i j with
| nil =>
cases i with
@@ -182,8 +183,8 @@ theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
@[deprecated getElem_insertIdx_of_gt (since := "2025-02-04")]
abbrev getElem_insertIdx_of_ge := @getElem_insertIdx_of_gt
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (insertIdx i x l).length) :
(insertIdx i x l)[j] =
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertIdx i x).length) :
(l.insertIdx i x)[j] =
if h₁ : j < i then
l[j]'(by simp [length_insertIdx] at h; split at h <;> omega)
else
@@ -199,7 +200,7 @@ theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (insertIdx
· rw [getElem_insertIdx_of_gt (by omega)]
theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
(insertIdx i x l)[j]? =
(l.insertIdx i x)[j]? =
if j < i then
l[j]?
else
@@ -230,16 +231,16 @@ theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
split at h <;> omega
theorem getElem?_insertIdx_of_lt {l : List α} {x : α} {i j : Nat} (h : j < i) :
(insertIdx i x l)[j]? = l[j]? := by
(l.insertIdx i x)[j]? = l[j]? := by
rw [getElem?_insertIdx, if_pos h]
theorem getElem?_insertIdx_self {l : List α} {x : α} {i : Nat} :
(insertIdx i x l)[i]? = if i l.length then some x else none := by
(l.insertIdx i x)[i]? = if i l.length then some x else none := by
rw [getElem?_insertIdx, if_neg (by omega)]
simp
theorem getElem?_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (h : i < j) :
(insertIdx i x l)[j]? = l[j - 1]? := by
(l.insertIdx i x)[j]? = l[j - 1]? := by
rw [getElem?_insertIdx, if_neg (by omega), if_neg (by omega)]
@[deprecated getElem?_insertIdx_of_gt (since := "2025-02-04")]

View File

@@ -87,75 +87,75 @@ theorem eraseIdx_modifyHead_zero {f : αα} {l : List α} :
/-! ### modifyTailIdx -/
@[simp] theorem modifyTailIdx_id : n (l : List α), l.modifyTailIdx id n = l
@[simp] theorem modifyTailIdx_id : i (l : List α), l.modifyTailIdx i id = l
| 0, _ => rfl
| _+1, [] => rfl
| n+1, a :: l => congrArg (cons a) (modifyTailIdx_id n l)
theorem eraseIdx_eq_modifyTailIdx : i (l : List α), eraseIdx l i = modifyTailIdx tail i l
theorem eraseIdx_eq_modifyTailIdx : i (l : List α), eraseIdx l i = l.modifyTailIdx i tail
| 0, l => by cases l <;> rfl
| _+1, [] => rfl
| _+1, _ :: _ => congrArg (cons _) (eraseIdx_eq_modifyTailIdx _ _)
@[simp] theorem length_modifyTailIdx (f : List α List α) (H : l, length (f l) = length l) :
n l, length (modifyTailIdx f n l) = length l
| 0, _ => H _
| _+1, [] => rfl
| _+1, _ :: _ => congrArg (·+1) (length_modifyTailIdx _ H _ _)
@[simp] theorem length_modifyTailIdx (f : List α List α) (H : l, (f l).length = l.length) :
(l : List α) i, (l.modifyTailIdx i f).length = l.length
| _, 0 => H _
| [], _+1 => rfl
| _ :: _, _+1 => congrArg (·+1) (length_modifyTailIdx _ H _ _)
theorem modifyTailIdx_add (f : List α List α) (n) (l₁ l₂ : List α) :
modifyTailIdx f (l₁.length + n) (l₁ ++ l₂) = l₁ ++ modifyTailIdx f n l₂ := by
theorem modifyTailIdx_add (f : List α List α) (i) (l₁ l₂ : List α) :
(l₁ ++ l₂).modifyTailIdx (l₁.length + i) f = l₁ ++ l₂.modifyTailIdx i f := by
induction l₁ <;> simp [*, Nat.succ_add]
theorem modifyTailIdx_eq_take_drop (f : List α List α) (H : f [] = []) :
i l, modifyTailIdx f i l = take i l ++ f (drop i l)
| 0, _ => rfl
| _ + 1, [] => H.symm
| n + 1, b :: l => congrArg (cons b) (modifyTailIdx_eq_take_drop f H n l)
(l : List α) i, l.modifyTailIdx i f = l.take i ++ f (l.drop i)
| _, 0 => rfl
| [], _ + 1 => H.symm
| b :: l, i + 1 => congrArg (cons b) (modifyTailIdx_eq_take_drop f H l i)
theorem exists_of_modifyTailIdx (f : List α List α) {n} {l : List α} (h : n l.length) :
l₁ l₂, l = l₁ ++ l₂ l₁.length = n modifyTailIdx f n l = l₁ ++ f l₂ :=
have _, _, eq, hl : l₁ l₂, l = l₁ ++ l₂ l₁.length = n :=
_, _, (take_append_drop n l).symm, length_take_of_le h
_, _, eq, hl, hl eq modifyTailIdx_add (n := 0) ..
theorem exists_of_modifyTailIdx (f : List α List α) {i} {l : List α} (h : i l.length) :
l₁ l₂, l = l₁ ++ l₂ l₁.length = i l.modifyTailIdx i f = l₁ ++ f l₂ := by
obtain l₁, l₂, rfl, rfl : l₁ l₂, l = l₁ ++ l₂ l₁.length = i :=
_, _, (take_append_drop i l).symm, length_take_of_le h
exact l₁, l, rfl, rfl, modifyTailIdx_add f 0 l₁ l₂
theorem modifyTailIdx_modifyTailIdx {f g : List α List α} (m : Nat) :
(n) (l : List α),
(l.modifyTailIdx f n).modifyTailIdx g (m + n) =
l.modifyTailIdx (fun l => (f l).modifyTailIdx g m) n
theorem modifyTailIdx_modifyTailIdx {f g : List α List α} (i : Nat) :
(j) (l : List α),
(l.modifyTailIdx j f).modifyTailIdx (i + j) g =
l.modifyTailIdx j (fun l => (f l).modifyTailIdx i g)
| 0, _ => rfl
| _ + 1, [] => rfl
| n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx m n l)
| n + 1, a :: l => congrArg (List.cons a) (modifyTailIdx_modifyTailIdx i n l)
theorem modifyTailIdx_modifyTailIdx_le {f g : List α List α} (m n : Nat) (l : List α)
(h : n m) :
(l.modifyTailIdx f n).modifyTailIdx g m =
l.modifyTailIdx (fun l => (f l).modifyTailIdx g (m - n)) n := by
theorem modifyTailIdx_modifyTailIdx_le {f g : List α List α} (i j : Nat) (l : List α)
(h : j i) :
(l.modifyTailIdx j f).modifyTailIdx i g =
l.modifyTailIdx j (fun l => (f l).modifyTailIdx (i - j) g) := by
rcases Nat.exists_eq_add_of_le h with m, rfl
rw [Nat.add_comm, modifyTailIdx_modifyTailIdx, Nat.add_sub_cancel]
theorem modifyTailIdx_modifyTailIdx_self {f g : List α List α} (n : Nat) (l : List α) :
(l.modifyTailIdx f n).modifyTailIdx g n = l.modifyTailIdx (g f) n := by
rw [modifyTailIdx_modifyTailIdx_le n n l (Nat.le_refl n), Nat.sub_self]; rfl
theorem modifyTailIdx_modifyTailIdx_self {f g : List α List α} (i : Nat) (l : List α) :
(l.modifyTailIdx i f).modifyTailIdx i g = l.modifyTailIdx i (g f) := by
rw [modifyTailIdx_modifyTailIdx_le i i l (Nat.le_refl i), Nat.sub_self]; rfl
/-! ### modify -/
@[simp] theorem modify_nil (f : α α) (i) : [].modify f i = [] := by cases i <;> rfl
@[simp] theorem modify_nil (f : α α) (i) : [].modify i f = [] := by cases i <;> rfl
@[simp] theorem modify_zero_cons (f : α α) (a : α) (l : List α) :
(a :: l).modify f 0 = f a :: l := rfl
(a :: l).modify 0 f = f a :: l := rfl
@[simp] theorem modify_succ_cons (f : α α) (a : α) (l : List α) (i) :
(a :: l).modify f (i + 1) = a :: l.modify f i := by rfl
(a :: l).modify (i + 1) f = a :: l.modify i f := by rfl
theorem modifyHead_eq_modify_zero (f : α α) (l : List α) :
l.modifyHead f = l.modify f 0 := by cases l <;> simp
l.modifyHead f = l.modify 0 f := by cases l <;> simp
@[simp] theorem modify_eq_nil_iff {f : α α} {i} {l : List α} :
l.modify f i = [] l = [] := by cases l <;> cases i <;> simp
l.modify i f = [] l = [] := by cases l <;> cases i <;> simp
theorem getElem?_modify (f : α α) :
i (l : List α) j, (modify f i l)[j]? = (fun a => if i = j then f a else a) <$> l[j]?
i (l : List α) j, (l.modify i f)[j]? = (fun a => if i = j then f a else a) <$> l[j]?
| n, l, 0 => by cases l <;> cases n <;> simp
| n, [], _+1 => by cases n <;> rfl
| 0, _ :: l, j+1 => by cases h : l[j]? <;> simp [h, modify, j.succ_ne_zero.symm]
@@ -165,32 +165,32 @@ theorem getElem?_modify (f : αα) :
cases h' : l[j]? <;> by_cases h : i = j <;>
simp [h, if_pos, if_neg, Option.map, mt Nat.succ.inj, not_false_iff, h']
@[simp] theorem length_modify (f : α α) : i l, length (modify f i l) = length l :=
@[simp] theorem length_modify (f : α α) : (l : List α) i, (l.modify i f).length = l.length :=
length_modifyTailIdx _ fun l => by cases l <;> rfl
@[simp] theorem getElem?_modify_eq (f : α α) (i) (l : List α) :
(modify f i l)[i]? = f <$> l[i]? := by
(l.modify i f)[i]? = f <$> l[i]? := by
simp only [getElem?_modify, if_pos]
@[simp] theorem getElem?_modify_ne (f : α α) {i j} (l : List α) (h : i j) :
(modify f i l)[j]? = l[j]? := by
(l.modify i f)[j]? = l[j]? := by
simp only [getElem?_modify, if_neg h, id_map']
theorem getElem_modify (f : α α) (i) (l : List α) (j) (h : j < (modify f i l).length) :
(modify f i l)[j] =
theorem getElem_modify (f : α α) (i) (l : List α) (j) (h : j < (l.modify i f).length) :
(l.modify i f)[j] =
if i = j then f (l[j]'(by simp at h; omega)) else l[j]'(by simp at h; omega) := by
rw [getElem_eq_iff, getElem?_modify]
simp at h
simp [h]
@[simp] theorem getElem_modify_eq (f : α α) (i) (l : List α) (h) :
(modify f i l)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify]
(l.modify i f)[i] = f (l[i]'(by simpa using h)) := by simp [getElem_modify]
@[simp] theorem getElem_modify_ne (f : α α) {i j} (l : List α) (h : i j) (h') :
(modify f i l)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h]
(l.modify i f)[j] = l[j]'(by simpa using h') := by simp [getElem_modify, h]
theorem modify_eq_self {f : α α} {i} {l : List α} (h : l.length i) :
l.modify f i = l := by
l.modify i f = l := by
apply ext_getElem
· simp
· intro m h₁ h₂
@@ -199,7 +199,7 @@ theorem modify_eq_self {f : αα} {i} {l : List α} (h : l.length ≤ i) :
omega
theorem modify_modify_eq (f g : α α) (i) (l : List α) :
(modify f i l).modify g i = modify (g f) i l := by
(l.modify i f).modify i g = l.modify i (g f) := by
apply ext_getElem
· simp
· intro m h₁ h₂
@@ -207,7 +207,7 @@ theorem modify_modify_eq (f g : αα) (i) (l : List α) :
split <;> simp
theorem modify_modify_ne (f g : α α) {i j} (l : List α) (h : i j) :
(modify f i l).modify g j = (l.modify g j).modify f i := by
(l.modify i f).modify j g = (l.modify j g).modify i f := by
apply ext_getElem
· simp
· intro m' h₁ h₂
@@ -215,7 +215,7 @@ theorem modify_modify_ne (f g : αα) {i j} (l : List α) (h : i ≠ j) :
split <;> split <;> first | rfl | omega
theorem modify_eq_set [Inhabited α] (f : α α) (i) (l : List α) :
modify f i l = l.set i (f (l[i]?.getD default)) := by
l.modify i f = l.set i (f (l[i]?.getD default)) := by
apply ext_getElem
· simp
· intro m h₁ h₂
@@ -227,24 +227,24 @@ theorem modify_eq_set [Inhabited α] (f : αα) (i) (l : List α) :
· rfl
theorem modify_eq_take_drop (f : α α) :
i l, modify f i l = take i l ++ modifyHead f (drop i l) :=
(l : List α) i, l.modify i f = l.take i ++ modifyHead f (l.drop i) :=
modifyTailIdx_eq_take_drop _ rfl
theorem modify_eq_take_cons_drop {f : α α} {i} {l : List α} (h : i < l.length) :
modify f i l = take i l ++ f l[i] :: drop (i + 1) l := by
l.modify i f = l.take i ++ f l[i] :: l.drop (i + 1) := by
rw [modify_eq_take_drop, drop_eq_getElem_cons h]; rfl
theorem exists_of_modify (f : α α) {i} {l : List α} (h : i < l.length) :
l₁ a l₂, l = l₁ ++ a :: l₂ l₁.length = i modify f i l = l₁ ++ f a :: l₂ :=
l₁ a l₂, l = l₁ ++ a :: l₂ l₁.length = i l.modify i f = l₁ ++ f a :: l₂ :=
match exists_of_modifyTailIdx _ (Nat.le_of_lt h) with
| _, _::_, eq, hl, H => _, _, _, eq, hl, H
| _, [], eq, hl, _ => nomatch Nat.ne_of_gt h (eq append_nil _ hl)
@[simp] theorem modify_id (i) (l : List α) : l.modify id i = l := by
@[simp] theorem modify_id (i) (l : List α) : l.modify i id = l := by
simp [modify]
theorem take_modify (f : α α) (i j) (l : List α) :
(modify f i l).take j = (take j l).modify f i := by
(l.modify i f).take j = (l.take j).modify i f := by
induction j generalizing l i with
| zero => simp
| succ n ih =>
@@ -256,7 +256,7 @@ theorem take_modify (f : αα) (i j) (l : List α) :
| succ i => simp [ih]
theorem drop_modify_of_lt (f : α α) (i j) (l : List α) (h : i < j) :
(modify f i l).drop j = l.drop j := by
(l.modify i f).drop j = l.drop j := by
apply ext_getElem
· simp
· intro m' h₁ h₂
@@ -265,7 +265,7 @@ theorem drop_modify_of_lt (f : αα) (i j) (l : List α) (h : i < j) :
omega
theorem drop_modify_of_ge (f : α α) (i j) (l : List α) (h : i j) :
(modify f i l).drop j = modify f (i - j) (drop j l) := by
(l.modify i f).drop j = (l.drop j).modify (i - j) f := by
apply ext_getElem
· simp
· intro m' h₁ h₂
@@ -273,7 +273,7 @@ theorem drop_modify_of_ge (f : αα) (i j) (l : List α) (h : i ≥ j) :
split <;> split <;> first | rfl | omega
theorem eraseIdx_modify_of_eq (f : α α) (i) (l : List α) :
(modify f i l).eraseIdx i = l.eraseIdx i := by
(l.modify i f).eraseIdx i = l.eraseIdx i := by
apply ext_getElem
· simp [length_eraseIdx]
· intro m h₁ h₂
@@ -281,7 +281,7 @@ theorem eraseIdx_modify_of_eq (f : αα) (i) (l : List α) :
split <;> split <;> first | rfl | omega
theorem eraseIdx_modify_of_lt (f : α α) (i j) (l : List α) (h : j < i) :
(modify f i l).eraseIdx j = (l.eraseIdx j).modify f (i - 1) := by
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify (i - 1) f := by
apply ext_getElem
· simp [length_eraseIdx]
· intro k h₁ h₂
@@ -291,7 +291,7 @@ theorem eraseIdx_modify_of_lt (f : αα) (i j) (l : List α) (h : j < i) :
all_goals (first | rfl | omega)
theorem eraseIdx_modify_of_gt (f : α α) (i j) (l : List α) (h : j > i) :
(modify f i l).eraseIdx j = (l.eraseIdx j).modify f i := by
(l.modify i f).eraseIdx j = (l.eraseIdx j).modify i f := by
apply ext_getElem
· simp [length_eraseIdx]
· intro k h₁ h₂
@@ -301,7 +301,7 @@ theorem eraseIdx_modify_of_gt (f : αα) (i j) (l : List α) (h : j > i) :
all_goals (first | rfl | omega)
theorem modify_eraseIdx_of_lt (f : α α) (i j) (l : List α) (h : j < i) :
(l.eraseIdx i).modify f j = (l.modify f j).eraseIdx i := by
(l.eraseIdx i).modify j f = (l.modify j f).eraseIdx i := by
apply ext_getElem
· simp [length_eraseIdx]
· intro k h₁ h₂
@@ -311,7 +311,7 @@ theorem modify_eraseIdx_of_lt (f : αα) (i j) (l : List α) (h : j < i) :
all_goals (first | rfl | omega)
theorem modify_eraseIdx_of_ge (f : α α) (i j) (l : List α) (h : j i) :
(l.eraseIdx i).modify f j = (l.modify f (j + 1)).eraseIdx i := by
(l.eraseIdx i).modify j f = (l.modify (j + 1) f).eraseIdx i := by
apply ext_getElem
· simp [length_eraseIdx]
· intro k h₁ h₂

View File

@@ -17,10 +17,11 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/--
`ofFn f` with `f : fin n → α` returns the list whose ith element is `f i`
```
ofFn f = [f 0, f 1, ... , f (n - 1)]
```
Creates a list by applying `f` to each potential index in order, starting at `0`.
Examples:
* `List.ofFn (n := 3) toString = ["0", "1", "2"]`
* `List.ofFn (fun i => #["red", "green", "blue"].get i.val i.isLt) = ["red", "green", "blue"]`
-/
def ofFn {n} (f : Fin n α) : List α := Fin.foldr n (f · :: ·) []

View File

@@ -19,7 +19,8 @@ The notation `~` is used for permutation equivalence.
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
-- TODO: restore after an update-stage0
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
open Nat
@@ -47,6 +48,14 @@ instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
theorem perm_comm {l₁ l₂ : List α} : l₁ ~ l₂ l₂ ~ l₁ := Perm.symm, Perm.symm
protected theorem Perm.congr_left {l₁ l₂ : List α} (h : l₁ ~ l₂) (l₃ : List α) :
l₁ ~ l₃ l₂ ~ l₃ :=
h.symm.trans, h.trans
protected theorem Perm.congr_right {l₁ l₂ : List α} (h : l₁ ~ l₂) (l₃ : List α) :
l₃ ~ l₁ l₃ ~ l₂ :=
fun h' => h'.trans h, fun h' => h'.trans h.symm
theorem Perm.swap' (x y : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : y :: x :: l₁ ~ x :: y :: l₂ :=
(swap ..).trans <| p.cons _ |>.cons _
@@ -514,7 +523,7 @@ theorem Perm.eraseP (f : α → Bool) {l₁ l₂ : List α}
exact fun h h₁ h₂ => h h₂ h₁
theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i l.length) :
insertIdx i x l ~ x :: l := by
l.insertIdx i x ~ x :: l := by
induction l generalizing i with
| nil =>
cases i with

View File

@@ -20,10 +20,14 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/--
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
Merges two lists, using `le` to select the first element of the resulting list if both are
non-empty.
This version is not tail-recursive,
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
If both input lists are sorted according to `le`, then the resulting list is also sorted according
to `le`. `O(min |l| |r|)`.
This implementation is not tail-recursive, but it is replaced at runtime by a proven-equivalent
tail-recursive merge.
-/
def merge (xs ys : List α) (le : α α Bool := by exact fun a b => a b) : List α :=
match xs, ys with
@@ -54,16 +58,16 @@ def MergeSort.Internal.splitInTwo (l : { l : List α // l.length = n }) :
open MergeSort.Internal in
set_option linter.unusedVariables false in
/--
Simplified implementation of stable merge sort.
A stable merge sort.
This function is designed for reasoning about the algorithm, and is not efficient.
(It particular it uses the non tail-recursive `merge` function,
and so can not be run on large lists, but also makes unnecessary traversals of lists.)
It is replaced at runtime in the compiler by `mergeSortTR₂` using a `@[csimp]` lemma.
This function is a simplified implementation that's designed to be easy to reason about, rather than
for efficiency. In particular, it uses the non-tail-recursive `List.merge` function and traverses
lists unnecessarily.
Because we want the sort to be stable,
it is essential that we split the list in two contiguous sublists.
It is replaced at runtime by an efficient implementation that has been proven to be equivalent.
-/
-- Because we want the sort to be stable, it is essential that we split the list in two contiguous
-- sublists.
def mergeSort : (xs : List α) (le : α α Bool := by exact fun a b => a b), List α
| [], _ => []
| [a], _ => [a]

View File

@@ -16,7 +16,8 @@ We prefer to pull `List.toArray` outwards past `Array` operations.
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
-- TODO: restore after an update-stage0
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array

View File

@@ -18,7 +18,13 @@ def List.toArrayAux : List α → Array α → Array α
| nil, xs => xs
| cons a as, xs => toArrayAux as (xs.push a)
/-- Convert a `List α` into an `Array α`. This is O(n) in the length of the list. -/
/--
Converts a `List α` into an `Array α` by repeatedly pushing elements from the list onto an empty
array. `O(|xs|)`.
Use `List.toArray` instead of calling this function directly. At runtime, this operation implements
both `List.toArray` and `Array.mk`.
-/
-- This function is exported to C, where it is called by `Array.mk`
-- (the constructor) to implement this functionality.
@[inline, match_pattern, pp_nodot, export lean_list_to_array]

View File

@@ -500,6 +500,9 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
@[simp] protected theorem add_le_add_iff_right {n : Nat} : m + n k + n m k :=
Nat.le_of_add_le_add_right, fun h => Nat.add_le_add_right h _
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m n + k m k :=
Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _
/-! ### le/lt -/
protected theorem lt_asymm {a b : Nat} (h : a < b) : ¬ b < a := Nat.not_lt.2 (Nat.le_of_lt h)
@@ -709,6 +712,16 @@ protected theorem le_of_mul_le_mul_left {a b c : Nat} (h : c * a ≤ c * b) (hc
have h' : c * b < c * a := Nat.mul_lt_mul_of_pos_left hlt hc
absurd h (Nat.not_le_of_gt h')
protected theorem le_of_mul_le_mul_right {a b c : Nat} (h : a * c b * c) (hc : 0 < c) : a b := by
rw [Nat.mul_comm a c, Nat.mul_comm b c] at h
exact Nat.le_of_mul_le_mul_left h hc
protected theorem mul_le_mul_left_iff {n m k : Nat} (w : 0 < k) : k * n k * m n m :=
fun h => Nat.le_of_mul_le_mul_left h w, fun h => mul_le_mul_left _ h
protected theorem mul_le_mul_right_iff {n m k : Nat} (w : 0 < k) : n * k m * k n m :=
fun h => Nat.le_of_mul_le_mul_right h w, fun h => mul_le_mul_right _ h
protected theorem eq_of_mul_eq_mul_left {m k n : Nat} (hn : 0 < n) (h : n * m = n * k) : m = k :=
Nat.le_antisymm (Nat.le_of_mul_le_mul_left (Nat.le_of_eq h) hn)
(Nat.le_of_mul_le_mul_left (Nat.le_of_eq h.symm) hn)

View File

@@ -74,6 +74,10 @@ theorem shiftRight_eq_div_pow (m : Nat) : ∀ n, m >>> n = m / 2 ^ n
theorem shiftRight_eq_zero (m n : Nat) (hn : m < 2^n) : m >>> n = 0 := by
simp [Nat.shiftRight_eq_div_pow, Nat.div_eq_of_lt hn]
theorem shiftRight_le (m n : Nat) : m >>> n m := by
simp only [shiftRight_eq_div_pow]
apply Nat.div_le_self
/-!
### testBit
We define an operation for testing individual bits in the binary representation

View File

@@ -143,7 +143,7 @@ theorem mod_eq_of_lt {a b : Nat} (h : a < b) : a % b = a :=
simp only [add_one_ne_zero, false_iff, ne_eq]
exact ne_of_beq_eq_false rfl
@[simp] theorem Nat.zero_eq_one_mod_iff {n : Nat} : 0 = 1 % n n = 1 := by
@[simp] theorem zero_eq_one_mod_iff {n : Nat} : 0 = 1 % n n = 1 := by
rw [eq_comm]
simp

View File

@@ -14,6 +14,31 @@ import Init.Data.Nat.Simproc
namespace Nat
theorem mod_add_mod_lt (a b : Nat) {c : Nat} (h : 0 < c) : a % c + b % c < 2 * c - 1 := by
have := mod_lt a h
have := mod_lt b h
omega
theorem mod_add_mod_eq {a b c : Nat} : a % c + b % c = (a + b) % c + if a % c + b % c < c then 0 else c := by
if h : 0 < c then
rw [add_mod]
split <;> rename_i h'
· simp [mod_eq_of_lt h']
· have : (a % c + b % c) % c = a % c + b % c - c := by
rw [mod_eq_iff]
right
have := mod_lt a h
have := mod_lt b h
exact by omega, 1, by simp; omega
omega
else
replace h : c = 0 := by omega
simp [h]
theorem add_mod_eq_sub : (a + b) % c = a % c + b % c - if a % c + b % c < c then 0 else c := by
conv => rhs; congr; rw [mod_add_mod_eq]
omega
theorem lt_div_iff_mul_lt (h : 0 < k) : x < y / k x * k < y - (k - 1) := by
have t := le_div_iff_mul_le h (x := x + 1) (y := y)
rw [succ_le, add_one_mul] at t
@@ -27,8 +52,8 @@ theorem div_le_iff_le_mul (h : 0 < k) : x / k ≤ y ↔ x ≤ y * k + k - 1 := b
omega
-- TODO: reprove `div_eq_of_lt_le` in terms of this:
protected theorem div_eq_iff (h : 0 < k) : x / k = y x y * k + k - 1 y * k x := by
rw [Nat.eq_iff_le_and_ge, le_div_iff_mul_le h, Nat.div_le_iff_le_mul h]
protected theorem div_eq_iff (h : 0 < k) : x / k = y y * k x x y * k + k - 1 := by
rw [Nat.eq_iff_le_and_ge, and_comm, le_div_iff_mul_le h, Nat.div_le_iff_le_mul h]
theorem lt_of_div_eq_zero (h : 0 < k) (h' : x / k = 0) : x < k := by
rw [Nat.div_eq_iff h] at h'
@@ -98,18 +123,49 @@ theorem succ_div_of_not_dvd {a b : Nat} (h : ¬ b a + 1) :
rw [eq_comm, Nat.div_eq_iff (by simp)]
constructor
· rw [Nat.div_mul_self_eq_mod_sub_self]
have : (a + 1) % (b + 1) < b + 1 := Nat.mod_lt _ (by simp)
omega
· rw [Nat.div_mul_self_eq_mod_sub_self]
have : (a + 1) % (b + 1) < b + 1 := Nat.mod_lt _ (by simp)
omega
theorem succ_div_of_mod_ne_zero {a b : Nat} (h : (a + 1) % b 0) :
(a + 1) / b = a / b := by
rw [succ_div_of_not_dvd (by rwa [dvd_iff_mod_eq_zero])]
theorem succ_div {a b : Nat} : (a + 1) / b = a / b + if b a + 1 then 1 else 0 := by
protected theorem succ_div {a b : Nat} : (a + 1) / b = a / b + if b a + 1 then 1 else 0 := by
split <;> rename_i h
· simp [succ_div_of_dvd h]
· simp [succ_div_of_not_dvd h]
protected theorem add_div {a b c : Nat} (h : 0 < c) :
(a + b) / c = a / c + b / c + if c a % c + b % c then 1 else 0 := by
conv => lhs; rw [ Nat.div_add_mod a c]
rw [Nat.add_assoc, mul_add_div h]
conv => lhs; rw [ Nat.div_add_mod b c]
rw [Nat.add_comm (a % c), Nat.add_assoc, mul_add_div h, Nat.add_assoc, Nat.add_comm (b % c)]
congr
rw [Nat.div_eq_iff h]
constructor
· split <;> rename_i h
· simpa using h
· simp
· have := mod_lt a h
have := mod_lt b h
split <;> · simp; omega
/-- If `(a + b) % c = c - 1`, then `a % c + b % c < c`, because `a % c + b % c` can not reach `2*c - 1`. -/
theorem mod_add_mod_lt_of_add_mod_eq_sub_one (w : 0 < c) (h : (a + b) % c = c - 1) : a % c + b % c < c := by
have := mod_add_mod_lt a b w
rw [mod_add_mod_eq, h] at this
split at this
· assumption
· omega
theorem add_div_of_dvd_add_add_one (h : c a + b + 1) : (a + b) / c = a / c + b / c := by
have w : c 0 := by rintro rfl; simp at h
replace w : 0 < c := by omega
rw [Nat.add_div w, if_neg, Nat.add_zero]
have := mod_add_mod_lt_of_add_mod_eq_sub_one w ((mod_eq_sub_iff Nat.zero_lt_one w).mpr h)
omega
end Nat

View File

@@ -191,25 +191,43 @@ end Nat
namespace Prod
/--
`(start, stop).foldI f a` evaluates `f` on all the numbers
Combines an initial value with each natural number from in a range, in increasing order.
In particular, `(start, stop).foldI f init` applies `f`on all the numbers
from `start` (inclusive) to `stop` (exclusive) in increasing order:
* `(5, 8).foldI f init = init |> f 5 |> f 6 |> f 7`
Examples:
* `(5, 8).foldI (fun j _ _ xs => xs.push j) #[] = (#[] |>.push 5 |>.push 6 |>.push 7)`
* `(5, 8).foldI (fun j _ _ xs => xs.push j) #[] = #[5, 6, 7]`
* `(5, 8).foldI (fun j _ _ xs => toString j :: xs) [] = ["7", "6", "5"]`
-/
@[inline] def foldI {α : Type u} (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 α α) (a : α) : α :=
(i.2 - i.1).fold (fun j _ => f (i.1 + j) (by omega) (by omega)) a
@[inline] def foldI {α : Type u} (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 α α) (init : α) : α :=
(i.2 - i.1).fold (fun j _ => f (i.1 + j) (by omega) (by omega)) init
/--
`(start, stop).anyI f a` returns true if `f` is true for some natural number
from `start` (inclusive) to `stop` (exclusive):
* `(5, 8).anyI f = f 5 || f 6 || f 7`
Checks whether a predicate holds for any natural number in a range.
In particular, `(start, stop).allI f` returns true if `f` is true for any natural number from
`start` (inclusive) to `stop` (exclusive).
Examples:
* `(5, 8).anyI (fun j _ _ => j == 6) = (5 == 6) || (6 == 6) || (7 == 6)`
* `(5, 8).anyI (fun j _ _ => j % 2 = 0) = true`
* `(6, 6).anyI (fun j _ _ => j % 2 = 0) = false`
-/
@[inline] def anyI (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 Bool) : Bool :=
(i.2 - i.1).any (fun j _ => f (i.1 + j) (by omega) (by omega))
/--
`(start, stop).allI f a` returns true if `f` is true for all natural numbers
from `start` (inclusive) to `stop` (exclusive):
* `(5, 8).anyI f = f 5 && f 6 && f 7`
Checks whether a predicate holds for all natural numbers in a range.
In particular, `(start, stop).allI f` returns true if `f` is true for all natural numbers from
`start` (inclusive) to `stop` (exclusive).
Examples:
* `(5, 8).allI (fun j _ _ => j < 10) = (5 < 10) && (6 < 10) && (7 < 10)`
* `(5, 8).allI (fun j _ _ => j % 2 = 0) = false`
* `(6, 7).allI (fun j _ _ => j % 2 = 0) = true`
-/
@[inline] def allI (i : Nat × Nat) (f : (j : Nat) i.1 j j < i.2 Bool) : Bool :=
(i.2 - i.1).all (fun j _ => f (i.1 + j) (by omega) (by omega))

View File

@@ -7,6 +7,14 @@ prelude
import Init.Data.Nat.Gcd
import Init.Data.Nat.Lemmas
/-!
# Lemmas about `Nat.lcm`
## Future work:
Most of the material about `Nat.gcd` from `Init.Data.Nat.Gcd` has analogues for `Nat.lcm`
that should be added to this file.
-/
namespace Nat
/-- The least common multiple of `m` and `n`, defined using `gcd`. -/

View File

@@ -131,9 +131,6 @@ protected theorem eq_zero_of_add_eq_zero_right (h : n + m = 0) : n = 0 :=
@[simp] protected theorem self_eq_add_right {a b : Nat} : a = a + b b = 0 := by omega
@[simp] protected theorem self_eq_add_left {a b : Nat} : a = b + a b = 0 := by omega
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m n + k m k :=
Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _
protected theorem lt_of_add_lt_add_right : {n : Nat}, k + n < m + n k < m
| 0, h => h
| _+1, h => Nat.lt_of_add_lt_add_right (Nat.lt_of_succ_lt_succ h)
@@ -290,10 +287,6 @@ theorem succ_min_succ (x y) : min (succ x) (succ y) = succ (min x y) := by
@[simp] protected theorem min_self (a : Nat) : min a a = a := Nat.min_eq_left (Nat.le_refl _)
instance : Std.IdempotentOp (α := Nat) min := Nat.min_self
@[simp] protected theorem zero_min (a) : min 0 a = 0 := Nat.min_eq_left (Nat.zero_le _)
@[simp] protected theorem min_zero (a) : min a 0 = 0 := Nat.min_eq_right (Nat.zero_le _)
@[simp] protected theorem min_assoc : (a b c : Nat), min (min a b) c = min a (min b c)
| 0, _, _ => by rw [Nat.zero_min, Nat.zero_min, Nat.zero_min]
| _, 0, _ => by rw [Nat.zero_min, Nat.min_zero, Nat.zero_min]
@@ -307,16 +300,16 @@ instance : Std.Associative (α := Nat) min := ⟨Nat.min_assoc⟩
@[simp] protected theorem min_self_assoc' {m n : Nat} : min n (min m n) = min n m := by
rw [Nat.min_comm m n, Nat.min_assoc, Nat.min_self]
@[simp] theorem min_add_left {a b : Nat} : min a (b + a) = a := by
@[simp] theorem min_add_left_self {a b : Nat} : min a (b + a) = a := by
rw [Nat.min_def]
simp
@[simp] theorem min_add_right {a b : Nat} : min a (a + b) = a := by
@[simp] theorem min_add_right_self {a b : Nat} : min a (a + b) = a := by
rw [Nat.min_def]
simp
@[simp] theorem add_left_min {a b : Nat} : min (b + a) a = a := by
rw [Nat.min_comm, min_add_left]
@[simp] theorem add_right_min {a b : Nat} : min (a + b) a = a := by
rw [Nat.min_comm, min_add_right]
@[simp] theorem add_left_min_self {a b : Nat} : min (b + a) a = a := by
rw [Nat.min_comm, min_add_left_self]
@[simp] theorem add_right_min_self {a b : Nat} : min (a + b) a = a := by
rw [Nat.min_comm, min_add_right_self]
protected theorem sub_sub_eq_min : (a b : Nat), a - (a - b) = min a b
| 0, _ => by rw [Nat.zero_sub, Nat.zero_min]
@@ -333,55 +326,35 @@ protected theorem sub_eq_sub_min (n m : Nat) : n - m = n - min n m := by
@[simp] protected theorem sub_add_min_cancel (n m : Nat) : n - m + min n m = n := by
rw [Nat.sub_eq_sub_min, Nat.sub_add_cancel (Nat.min_le_left ..)]
protected theorem max_eq_right {a b : Nat} (h : a b) : max a b = b := if_pos h
protected theorem max_eq_left {a b : Nat} (h : b a) : max a b = a := by
rw [Nat.max_comm]; exact Nat.max_eq_right h
protected theorem succ_max_succ (x y) : max (succ x) (succ y) = succ (max x y) := by
cases Nat.le_total x y with
| inl h => rw [Nat.max_eq_right h, Nat.max_eq_right (Nat.succ_le_succ h)]
| inr h => rw [Nat.max_eq_left h, Nat.max_eq_left (Nat.succ_le_succ h)]
protected theorem max_le_of_le_of_le {a b c : Nat} : a c b c max a b c := by
intros; cases Nat.le_total a b with
| inl h => rw [Nat.max_eq_right h]; assumption
| inr h => rw [Nat.max_eq_left h]; assumption
protected theorem max_le {a b c : Nat} : max a b c a c b c :=
fun h => Nat.le_trans (Nat.le_max_left ..) h, Nat.le_trans (Nat.le_max_right ..) h,
fun h₁, h₂ => Nat.max_le_of_le_of_le h₁ h₂
protected theorem max_lt {a b c : Nat} : max a b < c a < c b < c := by
rw [ Nat.succ_le, Nat.succ_max_succ a b]; exact Nat.max_le
@[simp] protected theorem max_self (a : Nat) : max a a = a := Nat.max_eq_right (Nat.le_refl _)
instance : Std.IdempotentOp (α := Nat) max := Nat.max_self
@[simp] protected theorem zero_max (a) : max 0 a = a := Nat.max_eq_right (Nat.zero_le _)
@[simp] protected theorem max_zero (a) : max a 0 = a := Nat.max_eq_left (Nat.zero_le _)
instance : Std.LawfulIdentity (α := Nat) max 0 where
left_id := Nat.zero_max
right_id := Nat.max_zero
protected theorem max_assoc : (a b c : Nat), max (max a b) c = max a (max b c)
@[simp] protected theorem max_assoc : (a b c : Nat), max (max a b) c = max a (max b c)
| 0, _, _ => by rw [Nat.zero_max, Nat.zero_max]
| _, 0, _ => by rw [Nat.zero_max, Nat.max_zero]
| _, _, 0 => by rw [Nat.max_zero, Nat.max_zero]
| _+1, _+1, _+1 => by simp only [Nat.succ_max_succ]; exact congrArg succ <| Nat.max_assoc ..
instance : Std.Associative (α := Nat) max := Nat.max_assoc
@[simp] theorem max_add_left {a b : Nat} : max a (b + a) = b + a := by
@[simp] theorem max_add_left_self {a b : Nat} : max a (b + a) = b + a := by
rw [Nat.max_def]
simp
@[simp] theorem max_add_right {a b : Nat} : max a (a + b) = a + b := by
@[simp] theorem max_add_right_self {a b : Nat} : max a (a + b) = a + b := by
rw [Nat.max_def]
simp
@[simp] theorem add_left_max {a b : Nat} : max (b + a) a = b + a := by
rw [Nat.max_comm, max_add_left]
@[simp] theorem add_right_max {a b : Nat} : max (a + b) a = a + b := by
rw [Nat.max_comm, max_add_right]
@[simp] theorem add_left_max_self {a b : Nat} : max (b + a) a = b + a := by
rw [Nat.max_comm, max_add_left_self]
@[simp] theorem add_right_max_self {a b : Nat} : max (a + b) a = a + b := by
rw [Nat.max_comm, max_add_right_self]
protected theorem sub_add_eq_max (a b : Nat) : a - b + b = max a b := by
match Nat.le_total a b with
@@ -423,22 +396,6 @@ protected theorem min_max_distrib_right (a b c : Nat) :
repeat rw [Nat.min_comm _ c]
exact Nat.min_max_distrib_left ..
protected theorem add_max_add_right : (a b c : Nat), max (a + c) (b + c) = max a b + c
| _, _, 0 => rfl
| _, _, _+1 => Eq.trans (Nat.succ_max_succ ..) <| congrArg _ (Nat.add_max_add_right ..)
protected theorem add_min_add_right : (a b c : Nat), min (a + c) (b + c) = min a b + c
| _, _, 0 => rfl
| _, _, _+1 => Eq.trans (Nat.succ_min_succ ..) <| congrArg _ (Nat.add_min_add_right ..)
protected theorem add_max_add_left (a b c : Nat) : max (a + b) (a + c) = a + max b c := by
repeat rw [Nat.add_comm a]
exact Nat.add_max_add_right ..
protected theorem add_min_add_left (a b c : Nat) : min (a + b) (a + c) = a + min b c := by
repeat rw [Nat.add_comm a]
exact Nat.add_min_add_right ..
protected theorem pred_min_pred : (x y), min (pred x) (pred y) = pred (min x y)
| 0, _ => by simp only [Nat.pred_zero, Nat.zero_min]
| _, 0 => by simp only [Nat.pred_zero, Nat.min_zero]
@@ -463,58 +420,6 @@ protected theorem sub_min_sub_left (a b c : Nat) : min (a - b) (a - c) = a - max
protected theorem sub_max_sub_left (a b c : Nat) : max (a - b) (a - c) = a - min b c := by
omega
protected theorem mul_max_mul_right (a b c : Nat) : max (a * c) (b * c) = max a b * c := by
induction a generalizing b with
| zero => simp
| succ i ind =>
cases b <;> simp [succ_eq_add_one, Nat.succ_mul, Nat.add_max_add_right, ind]
protected theorem mul_min_mul_right (a b c : Nat) : min (a * c) (b * c) = min a b * c := by
induction a generalizing b with
| zero => simp
| succ i ind =>
cases b <;> simp [succ_eq_add_one, Nat.succ_mul, Nat.add_min_add_right, ind]
protected theorem mul_max_mul_left (a b c : Nat) : max (a * b) (a * c) = a * max b c := by
repeat rw [Nat.mul_comm a]
exact Nat.mul_max_mul_right ..
protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min b c := by
repeat rw [Nat.mul_comm a]
exact Nat.mul_min_mul_right ..
-- protected theorem sub_min_sub_left (a b c : Nat) : min (a - b) (a - c) = a - max b c := by
-- induction b, c using Nat.recDiagAux with
-- | zero_left => rw [Nat.sub_zero, Nat.zero_max]; exact Nat.min_eq_right (Nat.sub_le ..)
-- | zero_right => rw [Nat.sub_zero, Nat.max_zero]; exact Nat.min_eq_left (Nat.sub_le ..)
-- | succ_succ _ _ ih => simp only [Nat.sub_succ, Nat.succ_max_succ, Nat.pred_min_pred, ih]
-- protected theorem sub_max_sub_left (a b c : Nat) : max (a - b) (a - c) = a - min b c := by
-- induction b, c using Nat.recDiagAux with
-- | zero_left => rw [Nat.sub_zero, Nat.zero_min]; exact Nat.max_eq_left (Nat.sub_le ..)
-- | zero_right => rw [Nat.sub_zero, Nat.min_zero]; exact Nat.max_eq_right (Nat.sub_le ..)
-- | succ_succ _ _ ih => simp only [Nat.sub_succ, Nat.succ_min_succ, Nat.pred_max_pred, ih]
-- protected theorem mul_max_mul_right (a b c : Nat) : max (a * c) (b * c) = max a b * c := by
-- induction a, b using Nat.recDiagAux with
-- | zero_left => simp only [Nat.zero_mul, Nat.zero_max]
-- | zero_right => simp only [Nat.zero_mul, Nat.max_zero]
-- | succ_succ _ _ ih => simp only [Nat.succ_mul, Nat.add_max_add_right, ih]
-- protected theorem mul_min_mul_right (a b c : Nat) : min (a * c) (b * c) = min a b * c := by
-- induction a, b using Nat.recDiagAux with
-- | zero_left => simp only [Nat.zero_mul, Nat.zero_min]
-- | zero_right => simp only [Nat.zero_mul, Nat.min_zero]
-- | succ_succ _ _ ih => simp only [Nat.succ_mul, Nat.add_min_add_right, ih]
-- protected theorem mul_max_mul_left (a b c : Nat) : max (a * b) (a * c) = a * max b c := by
-- repeat rw [Nat.mul_comm a]
-- exact Nat.mul_max_mul_right ..
-- protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min b c := by
-- repeat rw [Nat.mul_comm a]
-- exact Nat.mul_min_mul_right ..
/-! ### mul -/
protected theorem mul_right_comm (n m k : Nat) : n * m * k = n * k * m := by
@@ -1029,6 +934,25 @@ theorem mod_eq_iff {a b c : Nat} :
· simp_all
· rw [mul_add_mod, mod_eq_of_lt w]
theorem mod_eq_sub_iff {a b c : Nat} (h₁ : 0 < c) (h : c b) : a % b = b - c b a + c := by
rw [Nat.mod_eq_iff]
refine ?_, ?_
· rintro (rfl, rfl|hlt, k, hk)
· simp; omega
· refine k + 1, ?_
rw [ Nat.add_sub_assoc h] at hk
rw [Nat.mul_succ, eq_comm]
apply Nat.eq_add_of_sub_eq (by omega) hk.symm
· rintro k, hk
obtain (rfl|hb) := Nat.eq_zero_or_pos b
· obtain rfl : c = 0 := by omega
refine Or.inl rfl, by simpa using hk
· have : k 0 := by rintro rfl; omega
refine Or.inr by omega, k - 1, ?_
rw [ Nat.add_sub_assoc h, eq_comm]
apply Nat.sub_eq_of_eq_add
rw [mul_sub_one, Nat.sub_add_cancel (Nat.le_mul_of_pos_right _ (by omega)), hk]
theorem succ_mod_succ_eq_zero_iff {a b : Nat} :
(a + 1) % (b + 1) = 0 a % (b + 1) = b := by
symm

View File

@@ -12,6 +12,34 @@ namespace Nat
protected theorem min_eq_min (a : Nat) : Nat.min a b = min a b := rfl
@[simp] protected theorem zero_min (a : Nat) : min 0 a = 0 := by
simp [Nat.min_def]
@[simp] protected theorem min_zero (a : Nat) : min a 0 = 0 := by
simp [Nat.min_def]
@[simp] protected theorem add_min_add_right (a b c : Nat) : min (a + c) (b + c) = min a b + c := by
rw [Nat.min_def, Nat.min_def]
simp only [Nat.add_le_add_iff_right]
split <;> simp
@[simp] protected theorem add_min_add_left (a b c : Nat) : min (a + b) (a + c) = a + min b c := by
rw [Nat.min_def, Nat.min_def]
simp only [Nat.add_le_add_iff_left]
split <;> simp
@[simp] protected theorem mul_min_mul_right (a b c : Nat) : min (a * c) (b * c) = min a b * c := by
by_cases h : 0 < c
· rw [Nat.min_def, Nat.min_def]
simp only [Nat.mul_le_mul_right_iff h]
split <;> simp
· replace h : c = 0 := by exact Nat.eq_zero_of_not_pos h
subst h
simp
@[simp] protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min b c := by
rw [Nat.mul_comm a, Nat.mul_comm a, Nat.mul_min_mul_right, Nat.mul_comm]
protected theorem min_comm (a b : Nat) : min a b = min b a := by
match Nat.lt_trichotomy a b with
| .inl h => simp [Nat.min_def, h, Nat.le_of_lt, Nat.not_le_of_lt]
@@ -20,7 +48,7 @@ protected theorem min_comm (a b : Nat) : min a b = min b a := by
instance : Std.Commutative (α := Nat) min := Nat.min_comm
protected theorem min_le_right (a b : Nat) : min a b b := by
by_cases (a <= b) <;> simp [Nat.min_def, *]
by_cases (a b) <;> simp [Nat.min_def, *]
protected theorem min_le_left (a b : Nat) : min a b a :=
Nat.min_comm .. Nat.min_le_right ..
@@ -43,6 +71,34 @@ protected theorem lt_min {a b c : Nat} : a < min b c ↔ a < b ∧ a < c := Nat.
protected theorem max_eq_max (a : Nat) : Nat.max a b = max a b := rfl
@[simp] protected theorem zero_max (a : Nat) : max 0 a = a := by
simp [Nat.max_def]
@[simp] protected theorem max_zero (a : Nat) : max a 0 = a := by
simp +contextual [Nat.max_def]
@[simp] protected theorem add_max_add_right (a b c : Nat) : max (a + c) (b + c) = max a b + c := by
rw [Nat.max_def, Nat.max_def]
simp only [Nat.add_le_add_iff_right]
split <;> simp
@[simp] protected theorem add_max_add_left (a b c : Nat) : max (a + b) (a + c) = a + max b c := by
rw [Nat.max_def, Nat.max_def]
simp only [Nat.add_le_add_iff_left]
split <;> simp
@[simp] protected theorem mul_max_mul_right (a b c : Nat) : max (a * c) (b * c) = max a b * c := by
by_cases h : 0 < c
· rw [Nat.max_def, Nat.max_def]
simp only [Nat.mul_le_mul_right_iff h]
split <;> simp
· replace h : c = 0 := by exact Nat.eq_zero_of_not_pos h
subst h
simp
@[simp] protected theorem mul_max_mul_left (a b c : Nat) : max (a * b) (a * c) = a * max b c := by
rw [Nat.mul_comm a, Nat.mul_comm a, Nat.mul_max_mul_right, Nat.mul_comm]
protected theorem max_comm (a b : Nat) : max a b = max b a := by
simp only [Nat.max_def]
by_cases h₁ : a b <;> by_cases h₂ : b a <;> simp [h₁, h₂]
@@ -50,9 +106,28 @@ protected theorem max_comm (a b : Nat) : max a b = max b a := by
· cases not_or_intro h₁ h₂ <| Nat.le_total ..
instance : Std.Commutative (α := Nat) max := Nat.max_comm
protected theorem le_max_left ( a b : Nat) : a max a b := by
by_cases (a <= b) <;> simp [Nat.max_def, *]
protected theorem le_max_left (a b : Nat) : a max a b := by
by_cases (a b) <;> simp [Nat.max_def, *]
protected theorem le_max_right (a b : Nat) : b max a b :=
Nat.max_comm .. Nat.le_max_left ..
protected theorem max_eq_right {a b : Nat} (h : a b) : max a b = b := if_pos h
protected theorem max_eq_left {a b : Nat} (h : b a) : max a b = a :=
Nat.max_comm .. Nat.max_eq_right h
protected theorem max_le_of_le_of_le {a b c : Nat} : a c b c max a b c := by
intros; cases Nat.le_total a b with
| inl h => rw [Nat.max_eq_right h]; assumption
| inr h => rw [Nat.max_eq_left h]; assumption
protected theorem max_le {a b c : Nat} : max a b c a c b c :=
fun h => Nat.le_trans (Nat.le_max_left ..) h, Nat.le_trans (Nat.le_max_right ..) h,
fun h₁, h₂ => Nat.max_le_of_le_of_le h₁ h₂
protected theorem max_lt {a b c : Nat} : max a b < c a < c b < c :=
match c with
| 0 => by simp
| c + 1 => by simpa [Nat.lt_add_one_iff] using Nat.max_le
end Nat

View File

@@ -80,9 +80,9 @@ instance : OfScientific Float32 where
def Float32.ofNat (n : Nat) : Float32 :=
OfScientific.ofScientific n false 0
def Float32.ofInt : Int Float
| Int.ofNat n => Float.ofNat n
| Int.negSucc n => Float.neg (Float.ofNat (Nat.succ n))
def Float32.ofInt : Int Float32
| Int.ofNat n => Float32.ofNat n
| Int.negSucc n => Float32.neg (Float32.ofNat (Nat.succ n))
instance : OfNat Float32 n := Float32.ofNat n

View File

@@ -18,16 +18,30 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
@[inline] private unsafe def attachWithImpl
(o : Option α) (P : α Prop) (_ : x o, P x) : Option {x // P x} := unsafeCast o
/-- "Attach" a proof `P x` that holds for the element of `o`, if present,
to produce a new option with the same element but in the type `{x // P x}`. -/
/--
“Attaches” a proof that some predicate holds for an optional value, if present, returning a subtype
that expresses this fact.
This function is primarily used to implement `Option.attach`, which allows definitions by
well-founded recursion that use iteration operators (such as `Option.map`) to prove that an optional
value drawn from a parameter is smaller than the parameter. This allows the well-founded recursion
mechanism to prove that the function terminates.
-/
@[implemented_by attachWithImpl] def attachWith
(xs : Option α) (P : α Prop) (H : x xs, P x) : Option {x // P x} :=
match xs with
| none => none
| some x => some x, H x (mem_some_self x)
/-- "Attach" the proof that the element of `xs`, if present, is in `xs`
to produce a new option with the same elements but in the type `{x // x ∈ xs}`. -/
/--
“Attaches” a proof that an optional value, if present, is indeed this value, returning a subtype
that expresses this fact.
This function is primarily used to allow definitions by well-founded recursion that use iteration
operators (such as `Option.map`) to prove that an optional value drawn from a parameter is smaller
than the parameter. This allows the well-founded recursion mechanism to prove that the function
terminates.
-/
@[inline] def attach (xs : Option α) : Option {x // x xs} := xs.attachWith _ fun _ => id
@[simp] theorem attach_none : (none : Option α).attach = none := rfl
@@ -200,11 +214,15 @@ 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 an intermediate step by lemmas such as `map_subtype`,
and is ideally subsequently simplified away by `unattach_attach`.
Remove an attached proof that the value in an `Option` is indeed that value.
If not, usually the right approach is `simp [Option.unattach, -Option.map_subtype]` to unfold.
This function is usually inserted automatically by Lean, rather than explicitly in code. It is
introduced as an intermediate step during the elaboration of definitions by well-founded recursion.
If this function is encountered in a proof state, the right approach is usually the tactic
`simp [Option.unattach, -Option.map_subtype]`.
It is a synonym for `Option.map Subtype.val`.
-/
def unattach {α : Type _} {p : α Prop} (o : Option { x // p x }) := o.map (·.val)

View File

@@ -24,7 +24,15 @@ def getM [Alternative m] : Option α → m α
@[simp] theorem isSome_none : @isSome α none = false := rfl
@[simp] theorem isSome_some : isSome (some a) = true := rfl
/-- Returns `true` on `none` and `false` on `some x`. -/
/--
Returns `true` on `none` and `false` on `some x`.
This function is more flexible than `(· == none)` because it does not require a `BEq α` instance.
Examples:
* `(none : Option Nat).isNone = true`
* `(some Nat.add).isNone = false`
-/
@[inline] def isNone : Option α Bool
| some _ => false
| none => true
@@ -33,17 +41,46 @@ def getM [Alternative m] : Option α → m α
@[simp] theorem isNone_some : isNone (some a) = false := rfl
/--
`x?.isEqSome y` is equivalent to `x? == some y`, but avoids an allocation.
Checks whether an optional value is both present and equal to some other value.
Given `x? : Option α` and `y : α`, `x?.isEqSome y` is equivalent to `x? == some y`. It is more
efficient because it avoids an allocation.
-/
@[inline] def isEqSome [BEq α] : Option α α Bool
| some a, b => a == b
| none, _ => false
/--
Sequencing of `Option` computations.
From the perspective of `Option` as computations that might fail, this function sequences
potentially-failing computations, failing if either fails. From the perspective of `Option` as a
collection with at most one element, the function is applied to the element if present, and the
final result is empty if either the initial or the resulting collections are empty.
This function is often accessed via the `>>=` operator from the `Bind (Option α)` instance, or
implicitly via `do`-notation, but it is also idiomatic to call it using [generalized field
notation](lean-manual://section/generalized-field-notation).
Examples:
* `none.bind (fun x => some x) = none`
* `(some 4).bind (fun x => some x) = some 4`
* `none.bind (Option.guard (· > 2)) = none`
* `(some 2).bind (Option.guard (· > 2)) = none`
* `(some 4).bind (Option.guard (· > 2)) = some 4`
-/
@[inline] protected def bind : Option α (α Option β) Option β
| none, _ => none
| some a, f => f a
/-- Runs `f` on `o`'s value, if any, and returns its result, or else returns `none`. -/
/--
Runs the monadic action `f` on `o`'s value, if any, and returns the result, or `none` if there is
no value.
From the perspective of `Option` as a collection with at most one element, the monadic the function
is applied to the element if present, and the final result is empty if either the initial or the
resulting collections are empty.
-/
@[inline] protected def bindM [Monad m] (f : α m (Option β)) (o : Option α) : m (Option β) := do
if let some a := o then
return ( f a)
@@ -51,8 +88,13 @@ def getM [Alternative m] : Option α → m α
return none
/--
Runs a monadic function `f` on an optional value.
If the optional value is `none` the function is not called.
Runs a monadic function `f` on an optional value, returning the result. If the optional value is
`none`, the function is not called and the result is also `none`.
From the perspective of `Option` as a container with at most one element, this is analogous to
`List.mapM`, returning the result of running the monadic function on all elements of the container.
`Option.mapA` is the corresponding operation for applicative functors.
-/
@[inline] protected def mapM [Monad m] (f : α m β) (o : Option α) : m (Option β) := do
if let some a := o then
@@ -63,25 +105,51 @@ If the optional value is `none` the function is not called.
theorem map_id : (Option.map id : Option α Option α) = id :=
funext (fun o => match o with | none => rfl | some _ => rfl)
/-- Keeps an optional value only if it satisfies the predicate `p`. -/
/--
Keeps an optional value only if it satisfies a Boolean predicate.
If `Option` is thought of as a collection that contains at most one element, then `Option.filter` is
analogous to `List.filter` or `Array.filter`.
Examples:
* `(some 5).filter (· % 2 == 0) = none`
* `(some 4).filter (· % 2 == 0) = some 4`
* `none.filter (fun x : Nat => x % 2 == 0) = none`
* `none.filter (fun x : Nat => true) = none`
-/
@[always_inline, inline] protected def filter (p : α Bool) : Option α Option α
| some a => if p a then some a else none
| none => none
/-- Checks that an optional value satisfies a predicate `p` or is `none`. -/
/--
Checks whether an optional value either satisfies a Boolean predicate or is `none`.
Examples:
* `(some 33).all (· % 2 == 0) = false
* `(some 22).all (· % 2 == 0) = true
* `none.all (fun x : Nat => x % 2 == 0) = true
-/
@[always_inline, inline] protected def all (p : α Bool) : Option α Bool
| some a => p a
| none => true
/-- Checks that an optional value is not `none` and the value satisfies a predicate `p`. -/
/--
Checks whether an optional value is not `none` and satisfies a Boolean predicate.
Examples:
* `(some 33).any (· % 2 == 0) = false
* `(some 22).any (· % 2 == 0) = true
* `none.any (fun x : Nat => true) = false
-/
@[always_inline, inline] protected def any (p : α Bool) : Option α Bool
| some a => p a
| none => false
/--
Implementation of `OrElse`'s `<|>` syntax for `Option`. If the first argument is `some a`, returns
`some a`, otherwise evaluates and returns the second argument. See also `or` for a version that is
strict in the second argument.
`some a`, otherwise evaluates and returns the second argument.
See also `or` for a version that is strict in the second argument.
-/
@[always_inline, macro_inline] protected def orElse : Option α (Unit Option α) Option α
| some a, _ => some a
@@ -90,16 +158,42 @@ strict in the second argument.
instance : OrElse (Option α) where
orElse := Option.orElse
/-- If the first argument is `some a`, returns `some a`, otherwise returns the second argument.
This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
/--
Returns the first of its arguments that is `some`, or `none` if neither is `some`.
This is similar to the `<|>` operator, also known as `OrElse.orElse`, but both arguments are always
evaluated without short-circuiting.
-/
@[always_inline, macro_inline] def or : Option α Option α Option α
| some a, _ => some a
| none, b => b
/--
Lifts an ordering relation to `Option`, such that `none` is the least element.
It can be understood as adding a distinguished least element, represented by `none`, to both `α` and
`β`.
This definition is part of the implementation of the `LT (Option α)` instance. However, because it
can be used with heterogeneous relations, it is sometimes useful on its own.
Examples:
* `Option.lt (fun n k : Nat => n < k) none none = False`
* `Option.lt (fun n k : Nat => n < k) none (some 3) = True`
* `Option.lt (fun n k : Nat => n < k) (some 3) none = False`
* `Option.lt (fun n k : Nat => n < k) (some 4) (some 5) = True`
* `Option.lt (fun n k : Nat => n < k) (some 4) (some 4) = False`
-/
@[inline] protected def lt (r : α β Prop) : Option α Option β Prop
| none, some _ => True
| some x, some y => r x y
| _, _ => False
| none, some _ => True
| some x, some y => r x y
| _, _ => False
@[inline] protected def le (r : α β Prop) : Option α Option β Prop
| none, some _ => True
| none, none => True
| some _, none => False
| some x, some y => r x y
instance (r : α β Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
| none, some _ => isTrue trivial
@@ -107,8 +201,20 @@ instance (r : α → β → Prop) [s : DecidableRel r] : DecidableRel (Option.lt
| some _, none => isFalse not_false
| none, none => isFalse not_false
/-- Take a pair of options and if they are both `some`, apply the given fn to produce an output.
Otherwise act like `orElse`. -/
/--
Applies a function to a two optional values if both are present. Otherwise, if one value is present,
it is returned and the function is not used.
The value is `some (fn a b)` if the inputs are `some a` and `some b`. Otherwise, the behavior is
equivalent to `Option.orElse`: if only one input is `some x`, then the value is `some x`, and if
both are `none`, then the value is `none`.
Examples:
* `Option.merge (· + ·) none (some 3) = some 3`
* `Option.merge (· + ·) (some 2) (some 3) = some 5`
* `Option.merge (· + ·) (some 2) none = some 2`
* `Option.merge (· + ·) none none = none`
-/
def merge (fn : α α α) : Option α Option α Option α
| none , none => none
| some x, none => some x
@@ -125,12 +231,26 @@ def merge (fn : ααα) : Option α → Option α → Option α
@[simp] theorem some_bind (a) (f : α Option β) : (some a).bind f = f a := rfl
/-- An elimination principle for `Option`. It is a nondependent version of `Option.recOn`. -/
/--
A case analysis function for `Option`.
Given a value for `none` and a function to apply to the contents of `some`, `Option.elim` checks
which constructor a given `Option` consists of, and uses the appropriate argument.
`Option.elim` is an elimination principle for `Option`. In particular, it is a non-dependent version
of `Option.recOn`. It can also be seen as a combination of `Option.map` and `Option.getD`.
Examples:
* `(some "hello").elim 0 String.length = 5`
* `none.elim 0 String.length = 0`
-/
@[inline] protected def elim : Option α β (α β) β
| some x, _, f => f x
| none, y, _ => y
/-- Extracts the value `a` from an option that is known to be `some a` for some `a`. -/
/--
Extracts the value from an option that can be proven to be `some`.
-/
@[inline] def get {α : Type u} : (o : Option α) isSome o α
| some x, _ => x
@@ -138,27 +258,54 @@ def merge (fn : ααα) : Option α → Option α → Option α
| some _, _ => rfl
@[simp] theorem get_some (x : α) (h : isSome (some x)) : (some x).get h = x := rfl
/-- `guard p a` returns `some a` if `p a` holds, otherwise `none`. -/
/--
Returns `none` if a value doesn't satisfy a predicate, or the value itself otherwise.
From the perspective of `Option` as computations that might fail, this function is a run-time
assertion operator in the `Option` monad.
Examples:
* `Option.guard (· > 2) 1 = none`
* `Option.guard (· > 2) 5 = some 5`
-/
@[inline] def guard (p : α Prop) [DecidablePred p] (a : α) : Option α :=
if p a then some a else none
/--
Cast of `Option` to `List`. Returns `[a]` if the input is `some a`, and `[]` if it is `none`.
Converts an optional value to a list with zero or one element.
Examples:
* `(some "value").toList = ["value"]`
* `none.toList = []`
-/
@[inline] def toList : Option α List α
| none => .nil
| some a => .cons a .nil
/--
Cast of `Option` to `Array`. Returns `#[a]` if the input is `some a`, and `#[]` if it is `none`.
Converts an optional value to an array with zero or one element.
Examples:
* `(some "value").toArray = #["value"]`
* `none.toArray = #[]`
-/
@[inline] def toArray : Option α Array α
| none => List.toArray .nil
| some a => List.toArray (.cons a .nil)
/--
Two arguments failsafe function. Returns `f a b` if the inputs are `some a` and `some b`, and
"does nothing" otherwise.
Applies a function to a two optional values if both are present. Otherwise, if one value is present,
it is returned and the function is not used.
The value is `some (f a b)` if the inputs are `some a` and `some b`. Otherwise, the behavior is
equivalent to `Option.orElse`. If only one input is `some x`, then the value is `some x`. If both
are `none`, then the value is `none`.
Examples:
* `Option.liftOrGet (· + ·) none (some 3) = some 3`
* `Option.liftOrGet (· + ·) (some 2) (some 3) = some 5`
* `Option.liftOrGet (· + ·) (some 2) none = some 2`
* `Option.liftOrGet (· + ·) none none = none`
-/
def liftOrGet (f : α α α) : Option α Option α Option α
| none, none => none
@@ -174,28 +321,69 @@ inductive Rel (r : α → β → Prop) : Option α → Option β → Prop
/-- `none ~ none` -/
| none : Rel r none none
/-- Flatten an `Option` of `Option`, a specialization of `joinM`. -/
/--
Flattens nested optional values, preserving any value found.
This is analogous to `List.flatten`.
Examples:
* `none.join = none`
* `(some none).join = none`
* `(some (some v)).join = some v`
-/
@[simp, inline] def join (x : Option (Option α)) : Option α := x.bind id
/-- Like `Option.mapM` but for applicative functors. -/
/--
Applies a function in some applicative functor to an optional value, returning `none` with no
effects if the value is missing.
This is analogous to `Option.mapM` for monads.
-/
@[inline] protected def mapA [Applicative m] {α β} (f : α m β) : Option α m (Option β)
| none => pure none
| some x => some <$> f x
/--
If you maybe have a monadic computation in a `[Monad m]` which produces a term of type `α`, then
there is a naturally associated way to always perform a computation in `m` which maybe produces a
result.
Converts an optional monadic computation into a monadic computation of an optional value.
Example:
```lean example
#eval show IO (Option String) from
Option.sequence <| some do
IO.println "hello"
return "world"
```
```output
hello
```
```output
some "world"
```
-/
@[inline] def sequence [Monad m] {α : Type u} : Option (m α) m (Option α)
| none => pure none
| some fn => some <$> fn
/-- A monadic analogue of `Option.elim`. -/
/--
A monadic case analysis function for `Option`.
Given a fallback computation for `none` and a monadic operation to apply to the contents of `some`,
`Option.elimM` checks which constructor a given `Option` consists of, and uses the appropriate
argument.
`Option.elimM` can also be seen as a combination of `Option.mapM` and `Option.getDM`. It is a
monadic analogue of `Option.elim`.
-/
@[inline] def elimM [Monad m] (x : m (Option α)) (y : m β) (z : α m β) : m β :=
do ( x).elim y z
/-- A monadic analogue of `Option.getD`. -/
/--
Gets the value in an option, monadically computing a default value on `none`.
This is the monadic analogue of `Option.getD`.
-/
@[inline] def getDM [Monad m] (x : Option α) (y : m α) : m α :=
match x with
| some a => pure a
@@ -217,21 +405,45 @@ instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
@[simp] theorem any_none : Option.any p none = false := rfl
@[simp] theorem any_some : Option.any p (some x) = p x := rfl
/-- The minimum of two optional values. -/
/--
The minimum of two optional values, with `none` treated as the least element. This function is
usually accessed through the `Min (Option α)` instance, rather than directly.
Prior to `nightly-2025-02-27`, `none` was treated as the greatest element, so
`min none (some x) = min (some x) none = some x`.
Examples:
* `Option.min (some 2) (some 5) = some 2`
* `Option.min (some 5) (some 2) = some 2`
* `Option.min (some 2) none = none`
* `Option.min none (some 5) = none`
* `Option.min none none = none`
-/
protected def min [Min α] : Option α Option α Option α
| some x, some y => some (Min.min x y)
| some x, none => some x
| none, some y => some y
| some _, none => none
| none, some _ => none
| none, none => none
instance [Min α] : Min (Option α) where min := Option.min
@[simp] theorem min_some_some [Min α] {a b : α} : min (some a) (some b) = some (min a b) := rfl
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = some a := rfl
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = some b := rfl
@[simp] theorem min_some_none [Min α] {a : α} : min (some a) none = none := rfl
@[simp] theorem min_none_some [Min α] {b : α} : min none (some b) = none := rfl
@[simp] theorem min_none_none [Min α] : min (none : Option α) none = none := rfl
/-- The maximum of two optional values. -/
/--
The maximum of two optional values.
This function is usually accessed through the `Max (Option α)` instance, rather than directly.
Examples:
* `Option.max (some 2) (some 5) = some 5`
* `Option.max (some 5) (some 2) = some 5`
* `Option.max (some 2) none = some 2`
* `Option.max none (some 5) = some 5`
* `Option.max none none = none`
-/
protected def max [Max α] : Option α Option α Option α
| some x, some y => some (Max.max x y)
| some x, none => some x
@@ -251,6 +463,9 @@ end Option
instance [LT α] : LT (Option α) where
lt := Option.lt (· < ·)
instance [LE α] : LE (Option α) where
le := Option.le (· ·)
@[always_inline]
instance : Functor Option where
map := Option.map
@@ -269,6 +484,15 @@ def liftOption [Alternative m] : Option α → m α
| some a => pure a
| none => failure
/--
Recover from failing `Option` computations with a handler function.
This function is usually accessed through the `MonadExceptOf Unit Option` instance.
Examples:
* `Option.tryCatch none (fun () => some "handled") = some "handled"`
* `Option.tryCatch (some "succeeded") (fun () => some "handled") = some "succeeded"`
-/
@[always_inline, inline] protected def Option.tryCatch (x : Option α) (handle : Unit Option α) : Option α :=
match x with
| some _ => x

View File

@@ -11,6 +11,9 @@ universe u
namespace Option
/--
Extracts the value from an `Option`, panicking on `none`.
-/
@[inline] def get! {α : Type u} [Inhabited α] : Option α α
| some x => x
| none => panic! "value is none"

View File

@@ -32,9 +32,12 @@ instance [DecidableEq α] (j : α) (o : Option α) : Decidable (j ∈ o) :=
theorem some_inj {a b : α} : some a = some b a = b := by simp; rfl
/--
`o = none` is decidable even if the wrapped type does not have decidable equality.
This is not an instance because it is not definitionally equal to `instance : DecidableEq Option`.
Try to use `o.isNone` or `o.isSome` instead.
Equality with `none` is decidable even if the wrapped type does not have decidable equality.
This is not an instance because it is not definitionally equal to the standard instance of
`DecidableEq (Option α)`, which can cause problems. It can be locally bound if needed.
Try to use the Boolean comparisons `Option.isNone` or `Option.isSome` instead.
-/
@[inline] def decidable_eq_none {o : Option α} : Decidable (o = none) :=
decidable_of_decidable_of_iff isNone_iff_eq_none
@@ -50,34 +53,115 @@ instance {p : α → Prop} [DecidablePred p] : ∀ o : Option α, Decidable (Exi
| some a => if h : p a then isTrue _, rfl, h else isFalse fun _, rfl, hn => h hn
/--
Partial bind. If for some `x : Option α`, `f : Π (a : α), a ∈ x → Option β` is a
partial function defined on `a : α` giving an `Option β`, where `some a = x`,
then `pbind x f h` is essentially the same as `bind x f`
but is defined only when all `x = some a`, using the proof to apply `f`.
Given an optional value and a function that can be applied when the value is `some`, returns the
result of applying the function if this is possible.
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
behavior is equivalent to that of `Option.bind`.
Examples:
```lean example
def attach (v : Option α) : Option { y : α // y ∈ v } :=
v.pbind fun x h => some ⟨x, h⟩
```
```lean example
#reduce attach (some 3)
```
```output
some ⟨3, ⋯⟩
```
```lean example
#reduce attach none
```
```output
none
```
-/
@[inline]
def pbind : x : Option α, ( a : α, a x Option β) Option β
def pbind : (o : Option α) (f : (a : α) a o Option β) Option β
| none, _ => none
| some a, f => f a rfl
/--
Partial map. If `f : Π a, p a → β` is a partial function defined on `a : α` satisfying `p`,
then `pmap f x h` is essentially the same as `map f x` but is defined only when all members of `x`
satisfy `p`, using the proof to apply `f`.
Given a function from the elements of `α` that satisfy `p` to `β` and a proof that an optional value
satisfies `p` if it's present, applies the function to the value.
Examples:
```lean example
def attach (v : Option α) : Option { y : α // y ∈ v } :=
v.pmap (fun a (h : a ∈ v) => ⟨_, h⟩) (fun _ h => h)
```
```lean example
#reduce attach (some 3)
```
```output
some ⟨3, ⋯⟩
```
```lean example
#reduce attach none
```
```output
none
```
-/
@[inline] def pmap {p : α Prop} (f : a : α, p a β) :
x : Option α, ( a, a x p a) Option β
@[inline] def pmap {p : α Prop}
(f : a : α, p a β) :
(o : Option α) ( a, a o p a) Option β
| none, _ => none
| some a, H => f a (H a rfl)
/-- Partial elimination. If `o : Option α` and `f : (a : α) → a ∈ o → β`, then `o.pelim b f` is
the same as `o.elim b f` but `f` is passed the proof that `a ∈ o`. -/
/--
Given an optional value and a function that can be applied when the value is `some`, returns the
result of applying the function if this is possible, or a fallback value otherwise.
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
behavior is equivalent to that of `Option.elim`.
Examples:
```lean example
def attach (v : Option α) : Option { y : α // y ∈ v } :=
v.pelim none fun x h => some ⟨x, h⟩
```
```lean example
#reduce attach (some 3)
```
```output
some ⟨3, ⋯⟩
```
```lean example
#reduce attach none
```
```output
none
```
-/
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) a o β) : β :=
match o with
| none => b
| some a => f a rfl
/-- Map a monadic function which returns `Unit` over an `Option`. -/
/--
Executes a monadic action on an optional value if it is present, or does nothing if there is no
value.
Examples:
```lean example
#eval ((some 5).forM set : StateM Nat Unit).run 0
```
```output
((), 5)
```
```lean example
#eval (none.forM (fun x : Nat => set x) : StateM Nat Unit).run 0
```
```output
((), 0)
```
-/
@[inline] protected def forM [Pure m] : Option α (α m PUnit) m PUnit
| none , _ => pure
| some a, f => f a

View File

@@ -352,7 +352,12 @@ section choice
attribute [local instance] Classical.propDecidable
/-- An arbitrary `some a` with `a : α` if `α` is nonempty, and otherwise `none`. -/
/--
An optional arbitrary element of a given type.
If `α` is non-empty, then there exists some `v : α` and this arbitrary element is `some v`.
Otherwise, it is `none`.
-/
noncomputable def choice (α : Type _) : Option α :=
if h : Nonempty α then some (Classical.choice h) else none
@@ -673,4 +678,80 @@ theorem pmap_map (o : Option α) (f : α → β) {p : β → Prop} (g : ∀ b, p
o.pelim g (fun a h => g' (f a (H a h))) := by
cases o <;> simp
/-! ### LT and LE -/
@[simp] theorem not_lt_none [LT α] {a : Option α} : ¬ a < none := by cases a <;> simp [LT.lt, Option.lt]
@[simp] theorem none_lt_some [LT α] {a : α} : none < some a := by simp [LT.lt, Option.lt]
@[simp] theorem some_lt_some [LT α] {a b : α} : some a < some b a < b := by simp [LT.lt, Option.lt]
@[simp] theorem none_le [LE α] {a : Option α} : none a := by cases a <;> simp [LE.le, Option.le]
@[simp] theorem not_some_le_none [LE α] {a : α} : ¬ some a none := by simp [LE.le, Option.le]
@[simp] theorem some_le_some [LE α] {a b : α} : some a some b a b := by simp [LE.le, Option.le]
/-! ### min and max -/
theorem min_eq_left [LE α] [Min α] (min_eq_left : x y : α, x y min x y = x)
{a b : Option α} (h : a b) : min a b = a := by
cases a <;> cases b <;> simp_all
theorem min_eq_right [LE α] [Min α] (min_eq_right : x y : α, y x min x y = y)
{a b : Option α} (h : b a) : min a b = b := by
cases a <;> cases b <;> simp_all
theorem min_eq_left_of_lt [LT α] [Min α] (min_eq_left : x y : α, x < y min x y = x)
{a b : Option α} (h : a < b) : min a b = a := by
cases a <;> cases b <;> simp_all
theorem min_eq_right_of_lt [LT α] [Min α] (min_eq_right : x y : α, y < x min x y = y)
{a b : Option α} (h : b < a) : min a b = b := by
cases a <;> cases b <;> simp_all
theorem min_eq_or [LE α] [Min α] (min_eq_or : x y : α, min x y = x min x y = y)
{a b : Option α} : min a b = a min a b = b := by
cases a <;> cases b <;> simp_all
theorem min_le_left [LE α] [Min α] (min_le_left : x y : α, min x y x)
{a b : Option α} : min a b a := by
cases a <;> cases b <;> simp_all
theorem min_le_right [LE α] [Min α] (min_le_right : x y : α, min x y y)
{a b : Option α} : min a b b := by
cases a <;> cases b <;> simp_all
theorem le_min [LE α] [Min α] (le_min : x y z : α, x min y z x y x z)
{a b c : Option α} : a min b c a b a c := by
cases a <;> cases b <;> cases c <;> simp_all
theorem max_eq_left [LE α] [Max α] (max_eq_left : x y : α, x y max x y = y)
{a b : Option α} (h : a b) : max a b = b := by
cases a <;> cases b <;> simp_all
theorem max_eq_right [LE α] [Max α] (max_eq_right : x y : α, y x max x y = x)
{a b : Option α} (h : b a) : max a b = a := by
cases a <;> cases b <;> simp_all
theorem max_eq_left_of_lt [LT α] [Max α] (max_eq_left : x y : α, x < y max x y = y)
{a b : Option α} (h : a < b) : max a b = b := by
cases a <;> cases b <;> simp_all
theorem max_eq_right_of_lt [LT α] [Max α] (max_eq_right : x y : α, y < x max x y = x)
{a b : Option α} (h : b < a) : max a b = a := by
cases a <;> cases b <;> simp_all
theorem max_eq_or [LE α] [Max α] (max_eq_or : x y : α, max x y = x max x y = y)
{a b : Option α} : max a b = a max a b = b := by
cases a <;> cases b <;> simp_all
theorem left_le_max [LE α] [Max α] (le_refl : x : α, x x) (left_le_max : x y : α, x max x y)
{a b : Option α} : a max a b := by
cases a <;> cases b <;> simp_all
theorem right_le_max [LE α] [Max α] (le_refl : x : α, x x) (right_le_max : x y : α, y max x y)
{a b : Option α} : b max a b := by
cases a <;> cases b <;> simp_all
theorem max_le [LE α] [Max α] (max_le : x y z : α, max x y z x z y z)
{a b c : Option α} : max a b c a c b c := by
cases a <;> cases b <;> cases c <;> simp_all
end Option

View File

@@ -46,7 +46,7 @@ theorem forIn'_eq_pelim [Monad m] [LawfulMonad m]
o.pelim (pure b) (fun a h => g a h b <$> f a h b) := by
cases o <;> simp
theorem forIn'_pure_yield_eq_pelim [Monad m] [LawfulMonad m]
@[simp] theorem forIn'_pure_yield_eq_pelim [Monad m] [LawfulMonad m]
(o : Option α) (f : (a : α) a o β β) (b : β) :
forIn' o b (fun a m b => pure (.yield (f a m b))) =
pure (f := m) (o.pelim b (fun a h => f a h b)) := by
@@ -75,7 +75,7 @@ theorem forIn_eq_elim [Monad m] [LawfulMonad m]
o.elim (pure b) (fun a => g a b <$> f a b) := by
cases o <;> simp
theorem forIn_pure_yield_eq_elim [Monad m] [LawfulMonad m]
@[simp] theorem forIn_pure_yield_eq_elim [Monad m] [LawfulMonad m]
(o : Option α) (f : (a : α) β β) (b : β) :
forIn o b (fun a b => pure (.yield (f a b))) =
pure (f := m) (o.elim b (fun a => f a b)) := by

View File

@@ -8,35 +8,81 @@ prelude
import Init.Data.String
import Init.Data.Array.Basic
/--
The result of a comparison according to a total order.
The relationship between the compared items may be:
* `Ordering.lt`: less than
* `Ordering.eq`: equal
* `Ordering.gt`: greater than
-/
inductive Ordering where
| lt | eq | gt
| /-- Less than. -/
lt
| /-- Equal. -/
eq
| /-- Greater than. -/
gt
deriving Inhabited, DecidableEq
namespace Ordering
/-- Swaps less and greater ordering results -/
/--
Swaps less-than and greater-than ordering results.
Examples:
* `Ordering.lt.swap = Ordering.gt`
* `Ordering.eq.swap = Ordering.eq`
* `Ordering.gt.swap = Ordering.lt`
-/
def swap : Ordering Ordering
| .lt => .gt
| .eq => .eq
| .gt => .lt
/--
If `o₁` and `o₂` are `Ordering`, then `o₁.then o₂` returns `o₁` unless it is `.eq`,
in which case it returns `o₂`. Additionally, it has "short-circuiting" semantics similar to
boolean `x && y`: if `o₁` is not `.eq` then the expression for `o₂` is not evaluated.
This is a useful primitive for constructing lexicographic comparator functions:
```
If `a` and `b` are `Ordering`, then `a.then b` returns `a` unless it is `.eq`, in which case it
returns `b`. Additionally, it has short-circuiting” behavior similar to boolean `&&`: if `a` is not
`.eq` then the expression for `b` is not evaluated.
This is a useful primitive for constructing lexicographic comparator functions. The `deriving Ord`
syntax on a structure uses the `Ord` instance to compare each field in order, combining the results
equivalently to `Ordering.then`.
Use `compareLex` to lexicographically combine two comparison functions.
Examples:
```lean example
structure Person where
name : String
age : Nat
-- Sort people first by name (in ascending order), and people with the same name by age (in
-- descending order)
instance : Ord Person where
compare a b := (compare a.name b.name).then (compare b.age a.age)
```
This example will sort people first by name (in ascending order) and will sort people with
the same name by age (in descending order). (If all fields are sorted ascending and in the same
order as they are listed in the structure, you can also use `deriving Ord` on the structure
definition for the same effect.)
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Dana", 50⟩
```
```output
Ordering.gt
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 50⟩
```
```output
Ordering.gt
```
```lean example
#eval Ord.compare (⟨"Gert", 33⟩ : Person) ⟨"Gert", 20⟩
```
```output
Ordering.lt
```
-/
@[macro_inline] def «then» (a b : Ordering) : Ordering :=
match a with
@@ -44,42 +90,42 @@ definition for the same effect.)
| a => a
/--
Check whether the ordering is 'equal'.
Checks whether the ordering is `eq`.
-/
def isEq : Ordering Bool
| eq => true
| _ => false
/--
Check whether the ordering is 'not equal'.
Checks whether the ordering is not `eq`.
-/
def isNe : Ordering Bool
| eq => false
| _ => true
/--
Check whether the ordering is 'less than or equal to'.
Checks whether the ordering is `lt` or `eq`.
-/
def isLE : Ordering Bool
| gt => false
| _ => true
/--
Check whether the ordering is 'less than'.
Checks whether the ordering is `lt`.
-/
def isLT : Ordering Bool
| lt => true
| _ => false
/--
Check whether the ordering is 'greater than'.
Checks whether the ordering is `gt`.
-/
def isGT : Ordering Bool
| gt => true
| _ => false
/--
Check whether the ordering is 'greater than or equal'.
Checks whether the ordering is `gt` or `eq`.
-/
def isGE : Ordering Bool
| lt => false
@@ -263,8 +309,12 @@ end Lemmas
end Ordering
/--
Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` and
`x = y` corresponds to `Ordering.eq`.
Uses decidable less-than and equality relations to find an `Ordering`.
In particular, if `x < y` then the result is `Ordering.lt`. If `x = y` then the result is
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
`compareOfLessAndBEq` uses `BEq` instead of `DecidableEq`.
-/
@[inline] def compareOfLessAndEq {α} (x y : α) [LT α] [Decidable (x < y)] [DecidableEq α] : Ordering :=
if x < y then Ordering.lt
@@ -272,8 +322,12 @@ Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` a
else Ordering.gt
/--
Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` and
`x == y` corresponds to `Ordering.eq`.
Uses a decidable less-than relation and Boolean equality to find an `Ordering`.
In particular, if `x < y` then the result is `Ordering.lt`. If `x == y` then the result is
`Ordering.eq`. Otherwise, it is `Ordering.gt`.
`compareOfLessAndEq` uses `DecidableEq` instead of `BEq`.
-/
@[inline] def compareOfLessAndBEq {α} (x y : α) [LT α] [Decidable (x < y)] [BEq α] : Ordering :=
if x < y then .lt
@@ -281,9 +335,12 @@ Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` a
else .gt
/--
Compare `a` and `b` lexicographically by `cmp₁` and `cmp₂`. `a` and `b` are
first compared by `cmp₁`. If this returns 'equal', `a` and `b` are compared
Compares `a` and `b` lexicographically by `cmp₁` and `cmp₂`.
`a` and `b` are first compared by `cmp₁`. If this returns `Ordering.eq`, `a` and `b` are compared
by `cmp₂` to break the tie.
To lexicographically combine two `Ordering`s, use `Ordering.then`.
-/
@[inline] def compareLex (cmp₁ cmp₂ : α β Ordering) (a : α) (b : β) : Ordering :=
(cmp₁ a b).then (cmp₂ a b)
@@ -306,7 +363,14 @@ export Ord (compare)
set_option linter.unusedVariables false in -- allow specifying `ord` explicitly
/--
Compare `x` and `y` by comparing `f x` and `f y`.
Compares two values by comparing the results of applying a function.
In particular, `x` is compared to `y` by comparing `f x` and `f y`.
Examples:
* `compareOn (·.length) "apple" "banana" = .lt`
* `compareOn (· % 3) 5 6 = .gt`
* `compareOn (·.foldl max 0) [1, 2, 3] [3, 2, 1] = .eq`
-/
@[inline] def compareOn [ord : Ord β] (f : α β) (x y : α) : Ordering :=
compare (f x) (f y)
@@ -358,12 +422,20 @@ instance [Ord α] : Ord (Option α) where
def lexOrd [Ord α] [Ord β] : Ord (α × β) where
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare` is
`Ordering.lt`.
-/
def ltOfOrd [Ord α] : LT α where
lt a b := compare a b = Ordering.lt
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) :=
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
/--
Constructs an `LT` instance from an `Ord` instance that asserts that the result of `compare`
satisfies `Ordering.isLE`.
-/
def leOfOrd [Ord α] : LE α where
le a b := (compare a b).isLE
@@ -373,56 +445,70 @@ instance [Ord α] : DecidableRel (@LE.le α leOfOrd) :=
namespace Ord
/--
Derive a `BEq` instance from an `Ord` instance.
Constructs a `BEq` instance from an `Ord` instance.
-/
protected def toBEq (ord : Ord α) : BEq α where
beq x y := ord.compare x y == .eq
/--
Derive an `LT` instance from an `Ord` instance.
Constructs an `LT` instance from an `Ord` instance.
-/
protected def toLT (_ : Ord α) : LT α :=
protected def toLT (ord : Ord α) : LT α :=
ltOfOrd
instance [i : Ord α] : DecidableRel (@LT.lt _ (Ord.toLT i)) :=
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
/--
Derive an `LE` instance from an `Ord` instance.
Constructs an `LE` instance from an `Ord` instance.
-/
protected def toLE (_ : Ord α) : LE α :=
protected def toLE (ord : Ord α) : LE α :=
leOfOrd
instance [i : Ord α] : DecidableRel (@LE.le _ (Ord.toLE i)) :=
inferInstanceAs (DecidableRel (fun a b => (compare a b).isLE))
/--
Invert the order of an `Ord` instance.
Inverts the order of an `Ord` instance.
The result is an `Ord α` instance that returns `Ordering.lt` when `ord` would return `Ordering.gt`
and that returns `Ordering.gt` when `ord` would return `Ordering.lt`.
-/
protected def opposite (ord : Ord α) : Ord α where
compare x y := ord.compare y x
/--
`ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
Constructs an `Ord` instance that compares values according to the results of `f`.
In particular, `ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
The function `compareOn` can be used to perform this comparison without constructing an intermediate
`Ord` instance.
-/
protected def on (_ : Ord β) (f : α β) : Ord α where
compare := compareOn f
/--
Derive the lexicographic order on products `α × β` from orders for `α` and `β`.
Constructs the lexicographic order on products `α × β` from orders for `α` and `β`.
-/
protected def lex (_ : Ord α) (_ : Ord β) : Ord (α × β) :=
lexOrd
/--
Create an order which compares elements first by `ord₁` and then, if this
returns 'equal', by `ord₂`.
Constructs an `Ord` instance from two existing instances by combining them lexicographically.
The resulting instance compares elements first by `ord₁` and then, if this returns `Ordering.eq`, by
`ord₂`.
The function `compareLex` can be used to perform this comparison without constructing an
intermediate `Ord` instance. `Ordering.then` can be used to lexicographically combine the results of
comparisons.
-/
protected def lex' (ord₁ ord₂ : Ord α) : Ord α where
compare := compareLex ord₁.compare ord₂.compare
/--
Creates an order which compares elements of an `Array` in lexicographic order.
Constructs an order which compares elements of an `Array` in lexicographic order.
-/
protected def arrayOrd [a : Ord α] : Ord (Array α) where
compare x y :=

View File

@@ -43,7 +43,13 @@ theorem map_map (f : α → β) (f' : γ → δ) (g : β → ε) (g' : δ → ζ
Prod.map g g' (Prod.map f f' x) = Prod.map (g f) (g' f') x :=
rfl
/-- Swap the factors of a product. `swap (a, b) = (b, a)` -/
/--
Swaps the elements in a pair.
Examples:
* `(1, 2).swap = (2, 1)`
* `("orange", -87).swap = (-87, "orange")`
-/
def swap : α × β β × α := fun p => (p.2, p.1)
@[simp]

View File

@@ -79,6 +79,12 @@ instance [Repr α] : Repr (ULift.{v} α) where
instance : Repr Unit where
reprPrec _ _ := "()"
/--
Returns a representation of an optional value that should be able to be parsed as an equivalent
optional value.
This function is typically accessed through the `Repr (Option α)` instance.
-/
protected def Option.repr [Repr α] : Option α Nat Format
| none, _ => "none"
| some a, prec => Repr.addAppParen ("some " ++ reprArg a) prec
@@ -251,6 +257,14 @@ where
let d1 := n % 16;
hexDigitRepr d2 ++ hexDigitRepr d1
/--
Quotes the character to its representation as a character literal, surrounded by single quotes and
escaped as necessary.
Examples:
* `'L'.quote = "'L'"`
* `'"'.quote = "'\\\"'"`
-/
def Char.quote (c : Char) : String :=
"'" ++ Char.quoteCore c ++ "'"

View File

@@ -8,6 +8,7 @@ import Init.Data.SInt.Basic
import Init.Data.SInt.Float
import Init.Data.SInt.Float32
import Init.Data.SInt.Lemmas
import Init.Data.SInt.Bitwise
/-!
This module contains the definitions and basic theory about signed fixed width integer types.

View File

@@ -77,6 +77,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int8
-/
@[inline] def Int8.toBitVec (x : Int8) : BitVec 8 := x.toUInt8.toBitVec
theorem Int8.toBitVec.inj : {x y : Int8} x.toBitVec = y.toBitVec x = y
| _, _, rfl => rfl
/-- Obtains the `Int8` that is 2's complement equivalent to the `UInt8`. -/
@[inline] def UInt8.toInt8 (i : UInt8) : Int8 := Int8.ofUInt8 i
@[inline, deprecated UInt8.toInt8 (since := "2025-02-13"), inherit_doc UInt8.toInt8]
@@ -110,8 +113,8 @@ instance : ReprAtom Int8 := ⟨⟩
instance : Hashable Int8 where
hash i := i.toUInt8.toUInt64
instance : OfNat Int8 n := Int8.ofNat n
instance : Neg Int8 where
instance Int8.instOfNat : OfNat Int8 n := Int8.ofNat n
instance Int8.instNeg : Neg Int8 where
neg := Int8.neg
/-- The maximum value an `Int8` may attain, that is, `2^7 - 1 = 127`. -/
@@ -189,6 +192,9 @@ instance : ShiftLeft Int8 := ⟨Int8.shiftLeft⟩
instance : ShiftRight Int8 := Int8.shiftRight
instance : DecidableEq Int8 := Int8.decEq
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_int8"]
def Bool.toInt8 (b : Bool) : Int8 := if b then 1 else 0
@@ -213,6 +219,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int1
-/
@[inline] def Int16.toBitVec (x : Int16) : BitVec 16 := x.toUInt16.toBitVec
theorem Int16.toBitVec.inj : {x y : Int16} x.toBitVec = y.toBitVec x = y
| _, _, rfl => rfl
/-- Obtains the `Int16` that is 2's complement equivalent to the `UInt16`. -/
@[inline] def UInt16.toInt16 (i : UInt16) : Int16 := Int16.ofUInt16 i
@[inline, deprecated UInt16.toInt16 (since := "2025-02-13"), inherit_doc UInt16.toInt16]
@@ -250,8 +259,8 @@ instance : ReprAtom Int16 := ⟨⟩
instance : Hashable Int16 where
hash i := i.toUInt16.toUInt64
instance : OfNat Int16 n := Int16.ofNat n
instance : Neg Int16 where
instance Int16.instOfNat : OfNat Int16 n := Int16.ofNat n
instance Int16.instNeg : Neg Int16 where
neg := Int16.neg
/-- The maximum value an `Int16` may attain, that is, `2^15 - 1 = 32767`. -/
@@ -329,6 +338,9 @@ instance : ShiftLeft Int16 := ⟨Int16.shiftLeft⟩
instance : ShiftRight Int16 := Int16.shiftRight
instance : DecidableEq Int16 := Int16.decEq
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_int16"]
def Bool.toInt16 (b : Bool) : Int16 := if b then 1 else 0
@@ -353,6 +365,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int3
-/
@[inline] def Int32.toBitVec (x : Int32) : BitVec 32 := x.toUInt32.toBitVec
theorem Int32.toBitVec.inj : {x y : Int32} x.toBitVec = y.toBitVec x = y
| _, _, rfl => rfl
/-- Obtains the `Int32` that is 2's complement equivalent to the `UInt32`. -/
@[inline] def UInt32.toInt32 (i : UInt32) : Int32 := Int32.ofUInt32 i
@[inline, deprecated UInt32.toInt32 (since := "2025-02-13"), inherit_doc UInt32.toInt32]
@@ -387,15 +402,15 @@ def Int32.neg (i : Int32) : Int32 := ⟨⟨-i.toBitVec⟩⟩
instance : ToString Int32 where
toString i := toString i.toInt
instance : Repr Int16 where
instance : Repr Int32 where
reprPrec i prec := reprPrec i.toInt prec
instance : ReprAtom Int16 :=
instance : ReprAtom Int32 :=
instance : Hashable Int32 where
hash i := i.toUInt32.toUInt64
instance : OfNat Int32 n := Int32.ofNat n
instance : Neg Int32 where
instance Int32.instOfNat : OfNat Int32 n := Int32.ofNat n
instance Int32.instNeg : Neg Int32 where
neg := Int32.neg
/-- The maximum value an `Int32` may attain, that is, `2^31 - 1 = 2147483647`. -/
@@ -473,6 +488,9 @@ instance : ShiftLeft Int32 := ⟨Int32.shiftLeft⟩
instance : ShiftRight Int32 := Int32.shiftRight
instance : DecidableEq Int32 := Int32.decEq
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_int32"]
def Bool.toInt32 (b : Bool) : Int32 := if b then 1 else 0
@@ -497,6 +515,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `Int6
-/
@[inline] def Int64.toBitVec (x : Int64) : BitVec 64 := x.toUInt64.toBitVec
theorem Int64.toBitVec.inj : {x y : Int64} x.toBitVec = y.toBitVec x = y
| _, _, rfl => rfl
/-- Obtains the `Int64` that is 2's complement equivalent to the `UInt64`. -/
@[inline] def UInt64.toInt64 (i : UInt64) : Int64 := Int64.ofUInt64 i
@[inline, deprecated UInt64.toInt64 (since := "2025-02-13"), inherit_doc UInt64.toInt64]
@@ -542,8 +563,8 @@ instance : ReprAtom Int64 := ⟨⟩
instance : Hashable Int64 where
hash i := i.toUInt64
instance : OfNat Int64 n := Int64.ofNat n
instance : Neg Int64 where
instance Int64.instOfNat : OfNat Int64 n := Int64.ofNat n
instance Int64.instNeg : Neg Int64 where
neg := Int64.neg
/-- The maximum value an `Int64` may attain, that is, `2^63 - 1 = 9223372036854775807`. -/
@@ -621,6 +642,9 @@ instance : ShiftLeft Int64 := ⟨Int64.shiftLeft⟩
instance : ShiftRight Int64 := Int64.shiftRight
instance : DecidableEq Int64 := Int64.decEq
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_int64"]
def Bool.toInt64 (b : Bool) : Int64 := if b then 1 else 0
@@ -645,6 +669,9 @@ Obtain the `BitVec` that contains the 2's complement representation of the `ISiz
-/
@[inline] def ISize.toBitVec (x : ISize) : BitVec System.Platform.numBits := x.toUSize.toBitVec
theorem ISize.toBitVec.inj : {x y : ISize} x.toBitVec = y.toBitVec x = y
| _, _, rfl => rfl
/-- Obtains the `ISize` that is 2's complement equivalent to the `USize`. -/
@[inline] def USize.toISize (i : USize) : ISize := ISize.ofUSize i
@[inline, deprecated USize.toISize (since := "2025-02-13"), inherit_doc USize.toISize]
@@ -700,8 +727,8 @@ instance : ReprAtom ISize := ⟨⟩
instance : Hashable ISize where
hash i := i.toUSize.toUInt64
instance : OfNat ISize n := ISize.ofNat n
instance : Neg ISize where
instance ISize.instOfNat : OfNat ISize n := ISize.ofNat n
instance ISize.instNeg : Neg ISize where
neg := ISize.neg
/-- The maximum value an `ISize` may attain, that is, `2^(System.Platform.numBits - 1) - 1`. -/
@@ -780,6 +807,9 @@ instance : ShiftLeft ISize := ⟨ISize.shiftLeft⟩
instance : ShiftRight ISize := ISize.shiftRight
instance : DecidableEq ISize := ISize.decEq
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_isize"]
def Bool.toISize (b : Bool) : ISize := if b then 1 else 0

View File

@@ -0,0 +1,57 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.SInt.Lemmas
set_option hygiene false in
macro "declare_bitwise_int_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.sdiv b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_mod {a b : $typeName} : (a % b).toBitVec = a.toBitVec.srem 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
@[simp, int_toBitVec] protected theorem toBitVec_xor (a b : $typeName) : (a ^^^ b).toBitVec = a.toBitVec ^^^ b.toBitVec := rfl
@[simp, int_toBitVec] protected theorem toBitVec_shiftLeft (a b : $typeName) : (a <<< b).toBitVec = a.toBitVec <<< (b.toBitVec.smod $bits) := rfl
@[simp, int_toBitVec] protected theorem toBitVec_shiftRight (a b : $typeName) : (a >>> b).toBitVec = a.toBitVec.sshiftRight' (b.toBitVec.smod $bits) := rfl
@[simp, int_toBitVec] protected theorem toBitVec_abs (a : $typeName) : a.abs.toBitVec = a.toBitVec.abs := rfl
end $typeName
)
declare_bitwise_int_theorems Int8 8
declare_bitwise_int_theorems Int16 16
declare_bitwise_int_theorems Int32 32
declare_bitwise_int_theorems Int64 64
declare_bitwise_int_theorems ISize System.Platform.numBits
@[simp, int_toBitVec]
theorem Bool.toBitVec_toInt8 {b : Bool} : b.toInt8.toBitVec = (BitVec.ofBool b).setWidth 8 := by
cases b <;> simp [toInt8]
@[simp, int_toBitVec]
theorem Bool.toBitVec_toInt16 {b : Bool} : b.toInt16.toBitVec = (BitVec.ofBool b).setWidth 16 := by
cases b <;> simp [toInt16]
@[simp, int_toBitVec]
theorem Bool.toBitVec_toInt32 {b : Bool} : b.toInt32.toBitVec = (BitVec.ofBool b).setWidth 32 := by
cases b <;> simp [toInt32]
@[simp, int_toBitVec]
theorem Bool.toBitVec_toInt64 {b : Bool} : b.toInt64.toBitVec = (BitVec.ofBool b).setWidth 64 := by
cases b <;> simp [toInt64]
@[simp, int_toBitVec]
theorem Bool.toBitVec_toISize {b : Bool} :
b.toISize.toBitVec = (BitVec.ofBool b).setWidth System.Platform.numBits := by
cases b
· simp [toISize]
· apply BitVec.eq_of_toNat_eq
simp [toISize]

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,14 @@ import Init.Data.Char.Basic
universe u
/--
Creates a string that contains the characters in a list, in order.
Examples:
* `['L', '∃', '∀', 'N'].asString = "L∃∀N"`
* `[].asString = ""`
* `['a', 'a', 'a'].asString = "aaa"`
-/
def List.asString (s : List Char) : String :=
s
@@ -1169,6 +1177,13 @@ end String
namespace Char
/--
Constructs a singleton string that contains only the provided character.
Examples:
* `'L'.toString = "L"`
* `'"'.toString = "\""`
-/
@[inline] protected def toString (c : Char) : String :=
String.singleton c

View File

@@ -47,30 +47,30 @@ deriving instance BEq for Sum
section get
/-- Check if a sum is `inl`. -/
/-- Checks whether a sum is the left injection `inl`. -/
def isLeft : α β Bool
| inl _ => true
| inr _ => false
/-- Check if a sum is `inr`. -/
/-- Checks whether a sum is the right injection `inr`. -/
def isRight : α β Bool
| inl _ => false
| inr _ => true
/-- Retrieve the contents from a sum known to be `inl`.-/
/-- Retrieves the contents from a sum known to be `inl`.-/
def getLeft : (ab : α β) ab.isLeft α
| inl a, _ => a
/-- Retrieve the contents from a sum known to be `inr`.-/
/-- Retrieves the contents from a sum known to be `inr`.-/
def getRight : (ab : α β) ab.isRight β
| inr b, _ => b
/-- Check if a sum is `inl` and if so, retrieve its contents. -/
/-- Checks whether a sum is the left injection `inl` and, if so, retrieves its contents. -/
def getLeft? : α β Option α
| inl a => some a
| inr _ => none
/-- Check if a sum is `inr` and if so, retrieve its contents. -/
/-- Checks whether a sum is the right injection `inr` and, if so, retrieves its contents. -/
def getRight? : α β Option β
| inr b => some b
| inl _ => none
@@ -90,7 +90,10 @@ def getRight? : α ⊕ β → Option β
end get
/-- Define a function on `α ⊕ β` by giving separate definitions on `α` and `β`. -/
/--
Case analysis for sums that applies the appropriate function `f` or `g` after checking which
constructor is present.
-/
protected def elim {α β γ} (f : α γ) (g : β γ) : α β γ :=
fun x => Sum.casesOn x f g
@@ -100,7 +103,11 @@ protected def elim {α β γ} (f : αγ) (g : β → γ) : α ⊕ β → γ
@[simp] theorem elim_inr (f : α γ) (g : β γ) (x : β) :
Sum.elim f g (inr x) = g x := rfl
/-- Map `α ⊕ β` to `α' ⊕ β'` sending `α` to `α'` and `β` to `β'`. -/
/--
Transforms a sum according to functions on each type.
This function maps `α ⊕ β` to `α' ⊕ β'`, sending `α` to `α'` and `β` to `β'`.
-/
protected def map (f : α α') (g : β β') : α β α' β' :=
Sum.elim (inl f) (inr g)
@@ -108,7 +115,11 @@ protected def map (f : αα') (g : β → β') : α ⊕ β → α' ⊕ β'
@[simp] theorem map_inr (f : α α') (g : β β') (x : β) : (inr x).map f g = inr (g x) := rfl
/-- Swap the factors of a sum type -/
/--
Swaps the factors of a sum type.
The constructor `Sum.inl` is replaced with `Sum.inr`, and vice versa.
-/
def swap : α β β α := Sum.elim inr inl
@[simp] theorem swap_inl : swap (inl x : α β) = inr x := rfl

View File

@@ -40,6 +40,20 @@ instance {p : Prop} : ToString (Decidable p) := ⟨fun h =>
| Decidable.isTrue _ => "true"
| Decidable.isFalse _ => "false"
/--
Converts a list into a string, using `ToString.toString` to convert its elements.
The resulting string resembles list literal syntax, with the elements separated by `", "` and
enclosed in square brackets.
The resulting string may not be valid Lean syntax, because there's no such expectation for
`ToString` instances.
Examples:
* `[1, 2, 3].toString = "[1, 2, 3]"`
* `["cat", "dog"].toString = "[cat, dog]"`
* `["cat", "dog", ""].toString = "[cat, dog, ]"`
-/
protected def List.toString [ToString α] : List α String
| [] => "[]"
| [x] => "[" ++ toString x ++ "]"

View File

@@ -65,6 +65,9 @@ instance : Xor UInt8 := ⟨UInt8.xor⟩
instance : ShiftLeft UInt8 := UInt8.shiftLeft
instance : ShiftRight UInt8 := UInt8.shiftRight
/--
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
@@ -137,6 +140,9 @@ instance : Xor UInt16 := ⟨UInt16.xor⟩
instance : ShiftLeft UInt16 := UInt16.shiftLeft
instance : ShiftRight UInt16 := UInt16.shiftRight
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_uint16"]
def Bool.toUInt16 (b : Bool) : UInt16 := if b then 1 else 0
@@ -211,6 +217,9 @@ instance : Xor UInt32 := ⟨UInt32.xor⟩
instance : ShiftLeft UInt32 := UInt32.shiftLeft
instance : ShiftRight UInt32 := UInt32.shiftRight
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_uint32"]
def Bool.toUInt32 (b : Bool) : UInt32 := if b then 1 else 0
@@ -270,6 +279,9 @@ instance : Xor UInt64 := ⟨UInt64.xor⟩
instance : ShiftLeft UInt64 := UInt64.shiftLeft
instance : ShiftRight UInt64 := UInt64.shiftRight
/--
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
@@ -376,6 +388,9 @@ instance : Xor USize := ⟨USize.xor⟩
instance : ShiftLeft USize := USize.shiftLeft
instance : ShiftRight USize := USize.shiftRight
/--
Converts `true` to `1` and `false` to `0`.
-/
@[extern "lean_bool_to_usize"]
def Bool.toUSize (b : Bool) : USize := if b then 1 else 0

View File

@@ -1,7 +1,7 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone
Authors: Leonardo de Moura, François G. Dorais, Mario Carneiro, Mac Malone, Markus Himmel
-/
prelude
import Init.Data.UInt.Basic
@@ -27,7 +27,10 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
@[deprecated toNat_ofBitVec (since := "2025-02-12")]
theorem toNat_mk : (ofBitVec a).toNat = a.toNat := rfl
@[simp] theorem toNat_ofNat {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
@[simp] theorem toNat_ofNat' {n : Nat} : (ofNat n).toNat = n % 2 ^ $bits := BitVec.toNat_ofNat ..
-- Not `simp` because we have simprocs which will avoid the modulo.
theorem toNat_ofNat {n : Nat} : toNat (no_index (OfNat.ofNat n)) = n % 2 ^ $bits := toNat_ofNat'
@[simp] theorem toNat_ofNatLT {n : Nat} {h : n < size} : (ofNatLT n h).toNat = n := BitVec.toNat_ofNatLT ..
@@ -55,11 +58,16 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
theorem mk_toBitVec_eq : (a : $typeName), ofBitVec a.toBitVec = a
| _, _ => rfl
@[deprecated "Use `toNat_toBitVec` and `toNat_ofNat_of_lt`." (since := "2025-03-05")]
theorem toBitVec_eq_of_lt {a : Nat} : a < size (ofNat a).toBitVec.toNat = a :=
Nat.mod_eq_of_lt
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, toBitVec_eq_of_lt h]
theorem toBitVec_ofNat' (n : Nat) : (ofNat n).toBitVec = BitVec.ofNat _ n := rfl
theorem toNat_ofNat_of_lt' {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, toBitVec_ofNat', BitVec.toNat_ofNat, Nat.mod_eq_of_lt h]
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : toNat (OfNat.ofNat n) = n :=
toNat_ofNat_of_lt' h
@[int_toBitVec] theorem le_def {a b : $typeName} : a b a.toBitVec b.toBitVec := .rfl
@@ -151,10 +159,10 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.toBitVec.isLt
open $typeName (toNat_mod toNat_lt_size) in
protected theorem toNat_mod_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % ofNat m) < m := by
protected theorem toNat_mod_lt {m : Nat} : (u : $typeName), 0 < m toNat (u % ofNat m) < m := by
intro u h1
by_cases h2 : m < size
· rw [toNat_mod, toNat_ofNat_of_lt h2]
· rw [toNat_mod, toNat_ofNat_of_lt' h2]
apply Nat.mod_lt _ h1
· apply Nat.lt_of_lt_of_le
· apply toNat_lt_size
@@ -258,16 +266,20 @@ theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat
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
simp [-toNat_toBitVec, lt_def, BitVec.lt_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt32.lt_toNat_of_lt {n : UInt32} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
simp [-toNat_toBitVec, lt_def, BitVec.lt_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [lt_def, BitVec.lt_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt32.toNat_le_of_le {n : UInt32} {m : Nat} (h : m < size) : n ofNat m n.toNat m := by
simp [-toNat_toBitVec, le_def, BitVec.le_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
theorem UInt32.le_toNat_of_le {n : UInt32} {m : Nat} (h : m < size) : ofNat m n m n.toNat := by
simp [-toNat_toBitVec, le_def, BitVec.le_def, UInt32.toNat, toBitVec_eq_of_lt h]
rw [le_def, BitVec.le_def, toNat_toBitVec, toNat_toBitVec, toNat_ofNat_of_lt' h]
exact id
@[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
@@ -287,6 +299,8 @@ theorem UInt32.size_le_usizeSize : UInt32.size ≤ USize.size := by
theorem USize.size_eq_two_pow : USize.size = 2 ^ System.Platform.numBits := rfl
theorem USize.toNat_lt_two_pow_numBits (n : USize) : n.toNat < 2 ^ System.Platform.numBits := n.toFin.isLt
@[simp] theorem USize.toNat_lt (n : USize) : n.toNat < 2 ^ 64 := Nat.lt_of_lt_of_le n.toFin.isLt size_le
theorem USize.size_le_uint64Size : USize.size UInt64.size := by
cases USize.size_eq <;> simp_all +decide
theorem UInt8.toNat_lt_usizeSize (n : UInt8) : n.toNat < USize.size :=
Nat.lt_of_lt_of_le n.toNat_lt (by cases USize.size_eq <;> simp_all)
@@ -309,6 +323,15 @@ theorem USize.size_dvd_uInt64Size : USize.size UInt64.size := by cases USize
@[simp] theorem mod_uInt64Size_uSizeSize (n : Nat) : n % UInt64.size % USize.size = n % USize.size :=
Nat.mod_mod_of_dvd _ USize.size_dvd_uInt64Size
@[simp] theorem USize.size_sub_one_mod_uint8Size : (USize.size - 1) % UInt8.size = UInt8.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem USize.size_sub_one_mod_uint16Size : (USize.size - 1) % UInt16.size = UInt16.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem USize.size_sub_one_mod_uint32Size : (USize.size - 1) % UInt32.size = UInt32.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem UInt64.size_sub_one_mod_uSizeSize : 18446744073709551615 % USize.size = USize.size - 1 := by
cases USize.size_eq <;> simp_all +decide
@[simp] theorem UInt8.toNat_mod_size (n : UInt8) : n.toNat % UInt8.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
@[simp] theorem UInt8.toNat_mod_uInt16Size (n : UInt8) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@[simp] theorem UInt8.toNat_mod_uInt32Size (n : UInt8) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@@ -373,7 +396,7 @@ theorem USize.size_dvd_uInt64Size : USize.size UInt64.size := by cases USize
@[simp] theorem UInt32.toFin_toUSize (n : UInt32) :
n.toUSize.toFin = n.toFin.castLE size_le_usizeSize := rfl
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_usizeSize := rfl
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_uint64Size := rfl
@[simp] theorem UInt16.toBitVec_toUInt8 (n : UInt16) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
@[simp] theorem UInt32.toBitVec_toUInt8 (n : UInt32) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := rfl
@@ -783,3 +806,541 @@ theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
-- @[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
@[simp] theorem UInt32.toNat_ofFin (x : Fin UInt32.size) : (UInt32.ofFin x).toNat = x.val := rfl
@[simp] theorem UInt64.toNat_ofFin (x : Fin UInt64.size) : (UInt64.ofFin x).toNat = x.val := rfl
@[simp] theorem USize.toNat_ofFin (x : Fin USize.size) : (USize.ofFin x).toNat = x.val := rfl
theorem UInt8.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toNat = n := by rw [UInt8.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt16.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toNat = n := by rw [UInt16.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt32.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toNat = n := by rw [UInt32.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt64.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toNat = n := by rw [UInt64.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem USize.toNat_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toNat = n := by rw [USize.ofNatTruncate, dif_pos hn, toNat_ofNatLT]
theorem UInt8.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toNat = UInt8.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem UInt16.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toNat = UInt16.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem UInt32.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toNat = UInt32.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem UInt64.toNat_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toNat = UInt64.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
theorem USize.toNat_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toNat = USize.size - 1 := by rw [ofNatTruncate, dif_neg (by omega), toNat_ofNatLT]
@[simp] theorem UInt8.toFin_ofNatLT {n : Nat} (hn) : (UInt8.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt16.toFin_ofNatLT {n : Nat} (hn) : (UInt16.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt32.toFin_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt64.toFin_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem USize.toFin_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toFin = n, hn := rfl
@[simp] theorem UInt8.toFin_ofNat' {n : Nat} : (UInt8.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt16.toFin_ofNat' {n : Nat} : (UInt16.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt32.toFin_ofNat' {n : Nat} : (UInt32.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt64.toFin_ofNat' {n : Nat} : (UInt64.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem USize.toFin_ofNat' {n : Nat} : (USize.ofNat n).toFin = Fin.ofNat' _ n := rfl
@[simp] theorem UInt8.toFin_ofBitVec {b} : (UInt8.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem UInt16.toFin_ofBitVec {b} : (UInt16.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem UInt32.toFin_ofBitVec {b} : (UInt32.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem UInt64.toFin_ofBitVec {b} : (UInt64.ofBitVec b).toFin = b.toFin := rfl
@[simp] theorem USize.toFin_ofBitVec {b} : (USize.ofBitVec b).toFin = b.toFin := rfl
theorem UInt8.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt64.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem USize.toFin_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toFin = n, hn :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toFin = UInt8.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toFin = UInt16.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toFin = UInt32.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toFin_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toFin = UInt64.size - 1, by decide :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toFin_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toFin = USize.size - 1, by cases USize.size_eq <;> simp_all :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt8.toBitVec_ofNatLT {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt16.toBitVec_ofNatLT {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt32.toBitVec_ofNatLT {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt64.toBitVec_ofNatLT {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem USize.toBitVec_ofNatLT {n : Nat} (hn : n < USize.size) :
(USize.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := rfl
@[simp] theorem UInt8.toBitVec_ofFin (n : Fin UInt8.size) : (UInt8.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt16.toBitVec_ofFin (n : Fin UInt16.size) : (UInt16.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt32.toBitVec_ofFin (n : Fin UInt32.size) : (UInt32.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt64.toBitVec_ofFin (n : Fin UInt64.size) : (UInt64.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem USize.toBitVec_ofFin (n : Fin USize.size) : (USize.ofFin n).toBitVec = BitVec.ofFin n := rfl
@[simp] theorem UInt8.toBitVec_ofBitVec (n) : (UInt8.ofBitVec n).toBitVec = n := rfl
@[simp] theorem UInt16.toBitVec_ofBitVec (n) : (UInt16.ofBitVec n).toBitVec = n := rfl
@[simp] theorem UInt32.toBitVec_ofBitVec (n) : (UInt32.ofBitVec n).toBitVec = n := rfl
@[simp] theorem UInt64.toBitVec_ofBitVec (n) : (UInt64.ofBitVec n).toBitVec = n := rfl
@[simp] theorem USize.toBitVec_ofBitVec (n) : (USize.ofBitVec n).toBitVec = n := rfl
theorem UInt8.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt64.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem USize.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt8.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt16.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt32.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toBitVec = BitVec.ofNatLT (UInt64.size - 1) (by decide) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toBitVec_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toBitVec = BitVec.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all) :=
BitVec.eq_of_toNat_eq (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt16.toUInt8_ofNatLT {n : Nat} (hn) : (UInt16.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem UInt32.toUInt8_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem UInt64.toUInt8_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem USize.toUInt8_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem UInt16.toUInt8_ofFin (n) : (UInt16.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem UInt32.toUInt8_ofFin (n) : (UInt32.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem UInt64.toUInt8_ofFin (n) : (UInt64.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem USize.toUInt8_ofFin (n) : (USize.ofFin n).toUInt8 = UInt8.ofNat n.val := rfl
@[simp] theorem UInt16.toUInt8_ofBitVec (b) : (UInt16.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt32.toUInt8_ofBitVec (b) : (UInt32.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt64.toUInt8_ofBitVec (b) : (UInt64.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) := rfl
@[simp] theorem USize.toUInt8_ofBitVec (b) : (USize.ofBitVec b).toUInt8 = UInt8.ofBitVec (b.setWidth _) :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt8_ofNat' (n : Nat) : (UInt16.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt8_ofNat' (n : Nat) : (UInt32.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt8_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem USize.toUInt8_ofNat' (n : Nat) : (USize.ofNat n).toUInt8 = UInt8.ofNat n := UInt8.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
@[simp] theorem UInt32.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
@[simp] theorem UInt64.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
@[simp] theorem USize.toUInt8_ofNat {n : Nat} : toUInt8 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := toUInt8_ofNat' _
theorem UInt16.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem UInt32.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem UInt64.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem USize.toUInt8_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt8 = UInt8.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt8_ofNatLT]
theorem UInt16.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt8_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt8 = UInt8.ofNatLT (UInt8.size - 1) (by decide) :=
UInt8.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt32.toUInt16_ofNatLT {n : Nat} (hn) : (UInt32.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
@[simp] theorem UInt64.toUInt16_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
@[simp] theorem USize.toUInt16_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt16 = UInt16.ofNat n := rfl
@[simp] theorem UInt32.toUInt16_ofFin (n) : (UInt32.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
@[simp] theorem UInt64.toUInt16_ofFin (n) : (UInt64.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
@[simp] theorem USize.toUInt16_ofFin (n) : (USize.ofFin n).toUInt16 = UInt16.ofNat n.val := rfl
@[simp] theorem UInt32.toUInt16_ofBitVec (b) : (UInt32.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt64.toUInt16_ofBitVec (b) : (UInt64.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
@[simp] theorem USize.toUInt16_ofBitVec (b) : (USize.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt16_ofNat' (n : Nat) : (UInt32.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt16_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
@[simp] theorem USize.toUInt16_ofNat' (n : Nat) : (USize.ofNat n).toUInt16 = UInt16.ofNat n := UInt16.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt32.toUInt16_ofNat' _
@[simp] theorem UInt64.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUInt16_ofNat' _
@[simp] theorem USize.toUInt16_ofNat {n : Nat} : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := USize.toUInt16_ofNat' _
theorem UInt32.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
theorem UInt64.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
theorem USize.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt16 = UInt16.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt16_ofNatLT]
theorem UInt32.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt64.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt16.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt64.toUInt32_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUInt32 = UInt32.ofNat n := rfl
@[simp] theorem USize.toUInt32_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toUInt32 = UInt32.ofNat n := rfl
@[simp] theorem UInt64.toUInt32_ofFin (n) : (UInt64.ofFin n).toUInt32 = UInt32.ofNat n.val := rfl
@[simp] theorem USize.toUInt32_ofFin (n) : (USize.ofFin n).toUInt32 = UInt32.ofNat n.val := rfl
@[simp] theorem UInt64.toUInt32_ofBitVec (b) : (UInt64.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
@[simp] theorem USize.toUInt32_ofBitVec (b) : (USize.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_ofNat' (n : Nat) : (UInt64.ofNat n).toUInt32 = UInt32.ofNat n := UInt32.toNat.inj (by simp)
@[simp] theorem USize.toUInt32_ofNat' (n : Nat) : (USize.ofNat n).toUInt32 = UInt32.ofNat n := UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_ofNat {n : Nat} : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUInt32_ofNat' _
@[simp] theorem USize.toUInt32_ofNat {n : Nat} : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n := USize.toUInt32_ofNat' _
theorem UInt64.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUInt32 = UInt32.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt32_ofNatLT]
theorem USize.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt32 = UInt32.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUInt32_ofNatLT]
theorem UInt64.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt32.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt32.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt64.toUSize_ofNatLT {n : Nat} (hn) : (UInt64.ofNatLT n hn).toUSize = USize.ofNat n := rfl
@[simp] theorem UInt64.toUSize_ofFin (n) : (UInt64.ofFin n).toUSize = USize.ofNat n.val := rfl
@[simp] theorem UInt64.toUSize_ofBitVec (b) : (UInt64.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toNat.inj (by simp)
@[simp] theorem UInt64.toUSize_ofNat' (n : Nat) : (UInt64.ofNat n).toUSize = USize.ofNat n := USize.toNat.inj (by simp)
@[simp] theorem UInt64.toUSize_ofNat {n : Nat} : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n := UInt64.toUSize_ofNat' _
theorem UInt64.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatTruncate n).toUSize = USize.ofNat n := by rw [ofNatTruncate, dif_pos hn, toUSize_ofNatLT]
theorem UInt64.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt64.size n) :
(UInt64.ofNatTruncate n).toUSize = USize.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUInt16_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUInt16 = UInt16.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt8.toUInt32_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt8.toUInt64_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt8.toUSize_ofNatLT {n : Nat} (h) :
(UInt8.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
theorem UInt8.toUInt16_ofFin {n} :
(UInt8.ofFin n).toUInt16 = UInt16.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt8.toUInt32_ofFin {n} :
(UInt8.ofFin n).toUInt32 = UInt32.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt8.toUInt64_ofFin {n} :
(UInt8.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt8.toUSize_ofFin {n} :
(UInt8.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
@[simp] theorem UInt8.toUInt16_ofBitVec {b} : (UInt8.ofBitVec b).toUInt16 = UInt16.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt8.toUInt32_ofBitVec {b} : (UInt8.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt8.toUInt64_ofBitVec {b} : (UInt8.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt8.toUSize_ofBitVec {b} : (UInt8.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toBitVec_inj.1 (by simp)
theorem UInt8.toUInt16_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUInt16 = UInt16.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt8.toUInt16_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUInt16 = UInt16.ofNatLT (UInt8.size - 1) (by decide) :=
UInt16.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt8.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt8.size - 1) (by decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt8.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt8.size n) :
(UInt8.ofNatTruncate n).toUSize = USize.ofNatLT (UInt8.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toUInt32_ofNatLT {n : Nat} (h) :
(UInt16.ofNatLT n h).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt16.toUInt64_ofNatLT {n : Nat} (h) :
(UInt16.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt16.toUSize_ofNatLT {n : Nat} (h) :
(UInt16.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
theorem UInt16.toUInt32_ofFin {n} :
(UInt16.ofFin n).toUInt32 = UInt32.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt16.toUInt64_ofFin {n} :
(UInt16.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt16.toUSize_ofFin {n} :
(UInt16.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
@[simp] theorem UInt16.toUInt32_ofBitVec {b} : (UInt16.ofBitVec b).toUInt32 = UInt32.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt16.toUInt64_ofBitVec {b} : (UInt16.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt16.toUSize_ofBitVec {b} : (UInt16.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toBitVec_inj.1 (by simp)
theorem UInt16.toUInt32_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUInt32 = UInt32.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt16.toUInt32_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUInt32 = UInt32.ofNatLT (UInt16.size - 1) (by decide) :=
UInt32.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt16.size - 1) (by decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt16.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt16.size n) :
(UInt16.ofNatTruncate n).toUSize = USize.ofNatLT (UInt16.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toUInt64_ofNatLT {n : Nat} (h) :
(UInt32.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h (by decide)) := rfl
theorem UInt32.toUSize_ofNatLT {n : Nat} (h) :
(UInt32.ofNatLT n h).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le h size_le_usizeSize) := rfl
theorem UInt32.toUInt64_ofFin {n} :
(UInt32.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt (by decide)) := rfl
theorem UInt32.toUSize_ofFin {n} :
(UInt32.ofFin n).toUSize = USize.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_usizeSize) := rfl
@[simp] theorem UInt32.toUInt64_ofBitVec {b} : (UInt32.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) := rfl
@[simp] theorem UInt32.toUSize_ofBitVec {b} : (UInt32.ofBitVec b).toUSize = USize.ofBitVec (b.setWidth _) :=
USize.toBitVec_inj.1 (by simp)
theorem UInt32.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn (by decide)) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toUSize_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatTruncate n).toUSize = USize.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem UInt32.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (UInt32.size - 1) (by decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem UInt32.toUSize_ofNatTruncate_of_le {n : Nat} (hn : UInt32.size n) :
(UInt32.ofNatTruncate n).toUSize = USize.ofNatLT (UInt32.size - 1) (Nat.lt_of_lt_of_le (by decide) size_le_usizeSize) :=
USize.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
theorem USize.toUInt64_ofNatLT {n : Nat} (h) :
(USize.ofNatLT n h).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le h size_le_uint64Size) := rfl
theorem USize.toUInt64_ofFin {n} :
(USize.ofFin n).toUInt64 = UInt64.ofNatLT n.val (Nat.lt_of_lt_of_le n.isLt size_le_uint64Size) := rfl
@[simp] theorem USize.toUInt64_ofBitVec {b} : (USize.ofBitVec b).toUInt64 = UInt64.ofBitVec (b.setWidth _) :=
UInt64.toBitVec_inj.1 (by simp)
theorem USize.toUInt64_ofNatTruncate_of_lt {n : Nat} (hn : n < USize.size) :
(USize.ofNatTruncate n).toUInt64 = UInt64.ofNatLT n (Nat.lt_of_lt_of_le hn size_le_uint64Size) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_lt hn])
theorem USize.toUInt64_ofNatTruncate_of_le {n : Nat} (hn : USize.size n) :
(USize.ofNatTruncate n).toUInt64 = UInt64.ofNatLT (USize.size - 1) (by cases USize.size_eq <;> simp_all +decide) :=
UInt64.toNat.inj (by simp [toNat_ofNatTruncate_of_le hn])
@[simp] theorem UInt8.toUInt16_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUInt16 = UInt16.ofNat n := by
rw [ UInt8.ofNatLT_eq_ofNat (h := hn), toUInt16_ofNatLT, UInt16.ofNatLT_eq_ofNat]
@[simp] theorem UInt8.toUInt32_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUInt32 = UInt32.ofNat n := by
rw [ UInt8.ofNatLT_eq_ofNat (h := hn), toUInt32_ofNatLT, UInt32.ofNatLT_eq_ofNat]
@[simp] theorem UInt8.toUInt64_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUInt64 = UInt64.ofNat n := by
rw [ UInt8.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
@[simp] theorem UInt8.toUSize_ofNat' {n : Nat} (hn : n < UInt8.size) : (UInt8.ofNat n).toUSize = USize.ofNat n := by
rw [ UInt8.ofNatLT_eq_ofNat (h := hn), toUSize_ofNatLT, USize.ofNatLT_eq_ofNat]
@[simp] theorem UInt16.toUInt32_ofNat' {n : Nat} (hn : n < UInt16.size) : (UInt16.ofNat n).toUInt32 = UInt32.ofNat n := by
rw [ UInt16.ofNatLT_eq_ofNat (h := hn), toUInt32_ofNatLT, UInt32.ofNatLT_eq_ofNat]
@[simp] theorem UInt16.toUInt64_ofNat' {n : Nat} (hn : n < UInt16.size) : (UInt16.ofNat n).toUInt64 = UInt64.ofNat n := by
rw [ UInt16.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
@[simp] theorem UInt16.toUSize_ofNat' {n : Nat} (hn : n < UInt16.size) : (UInt16.ofNat n).toUSize = USize.ofNat n := by
rw [ UInt16.ofNatLT_eq_ofNat (h := hn), toUSize_ofNatLT, USize.ofNatLT_eq_ofNat]
@[simp] theorem UInt32.toUInt64_ofNat' {n : Nat} (hn : n < UInt32.size) : (UInt32.ofNat n).toUInt64 = UInt64.ofNat n := by
rw [ UInt32.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
@[simp] theorem UInt32.toUSize_ofNat' {n : Nat} (hn : n < UInt32.size) : (UInt32.ofNat n).toUSize = USize.ofNat n := by
rw [ UInt32.ofNatLT_eq_ofNat (h := hn), toUSize_ofNatLT, USize.ofNatLT_eq_ofNat]
@[simp] theorem USize.toUInt64_ofNat' {n : Nat} (hn : n < USize.size) : (USize.ofNat n).toUInt64 = UInt64.ofNat n := by
rw [ USize.ofNatLT_eq_ofNat (h := hn), toUInt64_ofNatLT, UInt64.ofNatLT_eq_ofNat]
@[simp] theorem UInt8.toUInt16_ofNat {n : Nat} (hn : n < 256) : toUInt16 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt8.toUInt16_ofNat' hn
@[simp] theorem UInt8.toUInt32_ofNat {n : Nat} (hn : n < 256) : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt8.toUInt32_ofNat' hn
@[simp] theorem UInt8.toUInt64_ofNat {n : Nat} (hn : n < 256) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt8.toUInt64_ofNat' hn
@[simp] theorem UInt8.toUSize_ofNat {n : Nat} (hn : n < 256) : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt8.toUSize_ofNat' hn
@[simp] theorem UInt16.toUInt32_ofNat {n : Nat} (hn : n < 65536) : toUInt32 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt16.toUInt32_ofNat' hn
@[simp] theorem UInt16.toUInt64_ofNat {n : Nat} (hn : n < 65536) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt16.toUInt64_ofNat' hn
@[simp] theorem UInt16.toUSize_ofNat {n : Nat} (hn : n < 65536) : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt16.toUSize_ofNat' hn
@[simp] theorem UInt32.toUInt64_ofNat {n : Nat} (hn : n < 4294967296) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt32.toUInt64_ofNat' hn
@[simp] theorem UInt32.toUSize_ofNat {n : Nat} (hn : n < 4294967296) : toUSize (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
UInt32.toUSize_ofNat' hn
@[simp] theorem USize.toUInt64_ofNat {n : Nat} (hn : n < 4294967296) : toUInt64 (no_index (OfNat.ofNat n)) = OfNat.ofNat n :=
USize.toUInt64_ofNat' (Nat.lt_of_lt_of_le hn UInt32.size_le_usizeSize)
@[simp] theorem UInt8.ofNatLT_finVal (n : Fin UInt8.size) : UInt8.ofNatLT n.val n.isLt = UInt8.ofFin n := rfl
@[simp] theorem UInt16.ofNatLT_finVal (n : Fin UInt16.size) : UInt16.ofNatLT n.val n.isLt = UInt16.ofFin n := rfl
@[simp] theorem UInt32.ofNatLT_finVal (n : Fin UInt32.size) : UInt32.ofNatLT n.val n.isLt = UInt32.ofFin n := rfl
@[simp] theorem UInt64.ofNatLT_finVal (n : Fin UInt64.size) : UInt64.ofNatLT n.val n.isLt = UInt64.ofFin n := rfl
@[simp] theorem USize.ofNatLT_finVal (n : Fin USize.size) : USize.ofNatLT n.val n.isLt = USize.ofFin n := rfl
@[simp] theorem UInt8.ofNatLT_bitVecToNat (n : BitVec 8) : UInt8.ofNatLT n.toNat n.isLt = UInt8.ofBitVec n := rfl
@[simp] theorem UInt16.ofNatLT_bitVecToNat (n : BitVec 16) : UInt16.ofNatLT n.toNat n.isLt = UInt16.ofBitVec n := rfl
@[simp] theorem UInt32.ofNatLT_bitVecToNat (n : BitVec 32) : UInt32.ofNatLT n.toNat n.isLt = UInt32.ofBitVec n := rfl
@[simp] theorem UInt64.ofNatLT_bitVecToNat (n : BitVec 64) : UInt64.ofNatLT n.toNat n.isLt = UInt64.ofBitVec n := rfl
@[simp] theorem USize.ofNatLT_bitVecToNat (n : BitVec System.Platform.numBits) : USize.ofNatLT n.toNat n.isLt = USize.ofBitVec n := rfl
@[simp] theorem UInt8.ofNat_finVal (n : Fin UInt8.size) : UInt8.ofNat n.val = UInt8.ofFin n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
@[simp] theorem UInt16.ofNat_finVal (n : Fin UInt16.size) : UInt16.ofNat n.val = UInt16.ofFin n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
@[simp] theorem UInt32.ofNat_finVal (n : Fin UInt32.size) : UInt32.ofNat n.val = UInt32.ofFin n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
@[simp] theorem UInt64.ofNat_finVal (n : Fin UInt64.size) : UInt64.ofNat n.val = UInt64.ofFin n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
@[simp] theorem USize.ofNat_finVal (n : Fin USize.size) : USize.ofNat n.val = USize.ofFin n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_finVal]
@[simp] theorem UInt8.ofNat_bitVecToNat (n : BitVec 8) : UInt8.ofNat n.toNat = UInt8.ofBitVec n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
@[simp] theorem UInt16.ofNat_bitVecToNat (n : BitVec 16) : UInt16.ofNat n.toNat = UInt16.ofBitVec n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
@[simp] theorem UInt32.ofNat_bitVecToNat (n : BitVec 32) : UInt32.ofNat n.toNat = UInt32.ofBitVec n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
@[simp] theorem UInt64.ofNat_bitVecToNat (n : BitVec 64) : UInt64.ofNat n.toNat = UInt64.ofBitVec n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
@[simp] theorem USize.ofNat_bitVecToNat (n : BitVec System.Platform.numBits) : USize.ofNat n.toNat = USize.ofBitVec n := by
rw [ ofNatLT_eq_ofNat (h := n.isLt), ofNatLT_bitVecToNat]
@[simp] theorem UInt8.ofNatTruncate_finVal (n : Fin UInt8.size) : UInt8.ofNatTruncate n.val = UInt8.ofFin n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt8.ofNat_finVal]
@[simp] theorem UInt16.ofNatTruncate_finVal (n : Fin UInt16.size) : UInt16.ofNatTruncate n.val = UInt16.ofFin n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt16.ofNat_finVal]
@[simp] theorem UInt32.ofNatTruncate_finVal (n : Fin UInt32.size) : UInt32.ofNatTruncate n.val = UInt32.ofFin n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt32.ofNat_finVal]
@[simp] theorem UInt64.ofNatTruncate_finVal (n : Fin UInt64.size) : UInt64.ofNatTruncate n.val = UInt64.ofFin n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, UInt64.ofNat_finVal]
@[simp] theorem USize.ofNatTruncate_finVal (n : Fin USize.size) : USize.ofNatTruncate n.val = USize.ofFin n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, USize.ofNat_finVal]
@[simp] theorem UInt8.ofNatTruncate_bitVecToNat (n : BitVec 8) : UInt8.ofNatTruncate n.toNat = UInt8.ofBitVec n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
@[simp] theorem UInt16.ofNatTruncate_bitVecToNat (n : BitVec 16) : UInt16.ofNatTruncate n.toNat = UInt16.ofBitVec n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
@[simp] theorem UInt32.ofNatTruncate_bitVecToNat (n : BitVec 32) : UInt32.ofNatTruncate n.toNat = UInt32.ofBitVec n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
@[simp] theorem UInt64.ofNatTruncate_bitVecToNat (n : BitVec 64) : UInt64.ofNatTruncate n.toNat = UInt64.ofBitVec n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
@[simp] theorem USize.ofNatTruncate_bitVecToNat (n : BitVec System.Platform.numBits) : USize.ofNatTruncate n.toNat = USize.ofBitVec n := by
rw [ofNatTruncate_eq_ofNat _ n.isLt, ofNat_bitVecToNat]
@[simp] theorem UInt8.ofFin_mk {n : Nat} (hn) : UInt8.ofFin (Fin.mk n hn) = UInt8.ofNatLT n hn := rfl
@[simp] theorem UInt16.ofFin_mk {n : Nat} (hn) : UInt16.ofFin (Fin.mk n hn) = UInt16.ofNatLT n hn := rfl
@[simp] theorem UInt32.ofFin_mk {n : Nat} (hn) : UInt32.ofFin (Fin.mk n hn) = UInt32.ofNatLT n hn := rfl
@[simp] theorem UInt64.ofFin_mk {n : Nat} (hn) : UInt64.ofFin (Fin.mk n hn) = UInt64.ofNatLT n hn := rfl
@[simp] theorem USize.ofFin_mk {n : Nat} (hn) : USize.ofFin (Fin.mk n hn) = USize.ofNatLT n hn := rfl
@[simp] theorem UInt8.ofFin_bitVecToFin (n : BitVec 8) : UInt8.ofFin n.toFin = UInt8.ofBitVec n := rfl
@[simp] theorem UInt16.ofFin_bitVecToFin (n : BitVec 16) : UInt16.ofFin n.toFin = UInt16.ofBitVec n := rfl
@[simp] theorem UInt32.ofFin_bitVecToFin (n : BitVec 32) : UInt32.ofFin n.toFin = UInt32.ofBitVec n := rfl
@[simp] theorem UInt64.ofFin_bitVecToFin (n : BitVec 64) : UInt64.ofFin n.toFin = UInt64.ofBitVec n := rfl
@[simp] theorem USize.ofFin_bitVecToFin (n : BitVec System.Platform.numBits) : USize.ofFin n.toFin = USize.ofBitVec n := rfl
@[simp] theorem UInt8.ofBitVec_ofNatLT {n : Nat} (hn) : UInt8.ofBitVec (BitVec.ofNatLT n hn) = UInt8.ofNatLT n hn := rfl
@[simp] theorem UInt16.ofBitVec_ofNatLT {n : Nat} (hn) : UInt16.ofBitVec (BitVec.ofNatLT n hn) = UInt16.ofNatLT n hn := rfl
@[simp] theorem UInt32.ofBitVec_ofNatLT {n : Nat} (hn) : UInt32.ofBitVec (BitVec.ofNatLT n hn) = UInt32.ofNatLT n hn := rfl
@[simp] theorem UInt64.ofBitVec_ofNatLT {n : Nat} (hn) : UInt64.ofBitVec (BitVec.ofNatLT n hn) = UInt64.ofNatLT n hn := rfl
@[simp] theorem USize.ofBitVec_ofNatLT {n : Nat} (hn) : USize.ofBitVec (BitVec.ofNatLT n hn) = USize.ofNatLT n hn := rfl
@[simp] theorem UInt8.ofBitVec_ofFin (n) : UInt8.ofBitVec (BitVec.ofFin n) = UInt8.ofFin n := rfl
@[simp] theorem UInt16.ofBitVec_ofFin (n) : UInt16.ofBitVec (BitVec.ofFin n) = UInt16.ofFin n := rfl
@[simp] theorem UInt32.ofBitVec_ofFin (n) : UInt32.ofBitVec (BitVec.ofFin n) = UInt32.ofFin n := rfl
@[simp] theorem UInt64.ofBitVec_ofFin (n) : UInt64.ofBitVec (BitVec.ofFin n) = UInt64.ofFin n := rfl
@[simp] theorem USize.ofBitVec_ofFin (n) : USize.ofBitVec (BitVec.ofFin n) = USize.ofFin n := rfl
@[simp] theorem BitVec.ofNat_uInt8ToNat (n : UInt8) : BitVec.ofNat 8 n.toNat = n.toBitVec :=
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem BitVec.ofNat_uInt16ToNat (n : UInt16) : BitVec.ofNat 16 n.toNat = n.toBitVec :=
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem BitVec.ofNat_uInt32ToNat (n : UInt32) : BitVec.ofNat 32 n.toNat = n.toBitVec :=
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem BitVec.ofNat_uInt64ToNat (n : UInt64) : BitVec.ofNat 64 n.toNat = n.toBitVec :=
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)

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