Compare commits

...

81 Commits

Author SHA1 Message Date
Kim Morrison
22ef07e67c fix test 2025-03-04 21:18:04 +11:00
Kim Morrison
45af89a986 stopping point for tonight 2025-03-04 20:45:59 +11:00
Kim Morrison
f39d152f6f more 2025-03-04 18:23:11 +11:00
Kim Morrison
eb2e200c8f . 2025-03-04 16:56:19 +11:00
Kim Morrison
669c55f438 continuing alignment of ediv/fdiv/tdiv 2025-03-04 15:51:31 +11: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
Sebastian Ullrich
2e66341f69 feat: Environment.realizeConst (#7076)
This PR introduces the central parallelism API for ensuring that helper
declarations can be generated lazily without duplicating work or
creating conflicts across threads.
2025-02-26 19:32:21 +00:00
Mac Malone
2e44585ce9 fix: set CP_UTF8 on Windows (#7213)
This PR adds `SetConsoleOutputCP(CP_UTF8)` during runtime initialization
to properly display Unicode on the Windows console. This effects both
the Lean executable itself and user executables (including Lake).

Closes #4291.
2025-02-26 18:36:32 +00:00
Leonardo de Moura
e2f0e14b04 feat: disequalities in cutsat (#7244)
This PR adds support for disequalities in the cutsat procedure used in
`grind`.
2025-02-26 17:26:59 +00:00
Henrik Böving
e801dc96ca chore: cleanup non terminal simps in LRAT (#7243)
This PR cleans up non terminal simps in the LRAT checking module.
2025-02-26 15:02:57 +00:00
Henrik Böving
56a3ac1814 feat: bv_decide structure projections and if (#7242)
This PR makes sure bv_decide can work with projections applied to `ite`
and `cond` in its structures pass.
2025-02-26 14:47:44 +00:00
Paul Reichert
6c62f720c8 feat: tree map lemmas for getThenInsertIfNew? (#7229)
This PR provides lemmas for the tree map function `getThenInsertIfNew?`.

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-26 10:29:51 +00:00
Eric Wieser
a57efd0a88 fix: free memory from lib_uv requests (#7151)
This PR fixes a memory leak in `IO.FS.createTempFile`
2025-02-26 07:52:34 +00:00
Paul Reichert
7e2d6e2254 feat: tree map lemmas for the getKey variants and insertIfNew functions (#7221)
This PR provides lemmas about the tree map functions `getKey?`,
`getKey`, `getKey!`, `getKeyD` and `insertIfNew` and their interaction
with other functions for which lemmas already exist.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-26 07:36:28 +00:00
Kim Morrison
4603e1a6ad feat: add Array/Vector.replace (#7235)
This PR adds `Array.replace` and `Vector.replace`, proves the
correspondences with `List.replace`, and reproduces the basic API. In
order to do so, it fills in some gaps in the `List.findX` APIs.
2025-02-26 06:03:45 +00:00
Mac Malone
550d2918b8 feat: Lake plugin w/ USE_LAKE (#7233)
This PR uses the Lake plugin when Lake is built with Lake via
`USE_LAKE`.
2025-02-26 04:05:15 +00:00
Leonardo de Moura
eb5ad2c03a feat: disequality propagation from grind core module to cutsat (#7234)
This PR implements dIsequality propagation from `grind` core module to
cutsat.
2025-02-26 03:34:39 +00:00
Leonardo de Moura
769fe4ebf6 feat: add Grind.mkDiseqProof? (#7231)
This PR implements functions for constructing disequality proofs in
`grind`.
2025-02-25 23:40:07 +00:00
Joachim Breitner
8130fdc474 feat: induction tactic to err on extra targets (#7224)
This PR make `induction … using` and `cases … using` complain if more
targets were given than expected by that eliminator.
2025-02-25 20:53:16 +00:00
Markus Himmel
41bba59868 feat: UIntX conversion lemmas (part 2/2) (#7210)
This PR adds the remaining lemmas about iterated conversions between
finite types starting with something of type `UIntX`.

In the near future, we will add similar lemmas when starting with
something of type `IntX`, `Nat`, `Int`, `BitVec` or `Fin`.
2025-02-25 18:52:17 +00:00
Eric Wieser
115f06c32a fix: missing indents in Try this message (#7191)
This PR fixes the indentation of "Try this" suggestions in widget-less
multiline messages, as they appear in `#guard_msgs` outputs.
2025-02-25 16:55:50 +00:00
Sebastian Ullrich
1e1e17cb35 fix: be consistent in not reporting newlines between trace nodes to info view (#7143)
This PR makes the server consistently not report newlines between trace
nodes to the info view, enabling it to render them on dedicates lines
without extraneous spacing between them in all circumstances.

The info view code will separately need to be adjusted to this new
behavior, until then this change will make adjacent trace node leafs
consistently be rendered *on the same line* if there is sufficient
space. The cmdline should be unaffected in any case.
2025-02-25 16:16:35 +00:00
Paul Reichert
831e8d768b feat: tree map lemmas for get, get! and getD (#7207)
This PR provides lemmas for the tree map functions `get`, `get!` and
`getD` in relation to the other operations for which lemmas already
exist.

Internally, the `simp_to_model` tactic was provided two new simp lemmas
to eliminate some common complications that require `rw`'ing before
using `simp_to_model`. However, it is still necessary to sometimes
`revert` some hypotheses.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-25 15:26:50 +00:00
jrr6
b4b878b2d0 fix: prevent exact? and apply? from suggesting invalid tactics (#7192)
This PR prevents `exact?` and `apply?` from suggesting tactics that
correspond to correct proofs but do not elaborate, and it allows these
tactics to suggest `expose_names` when needed.

These tactics now indicate that a non-compiling term was generated but
do not suggest that that term be inserted. `exact?` also no longer
suggests that the user try `apply?` if no partial suggestions were
found.

This addresses part of #5407 but does not achieve the exact expected
behavior therein (due to #6122).
2025-02-25 15:24:09 +00:00
Paul Reichert
2377f35426 fix: replace the compare_self simp lemma with a less generic one (#7222)
This PR removes the `simp` attribute from `ReflCmp.compare_self` because
it matches arbitrary function applications. Instead, a new `simp` lemma
`ReflOrd.compare_self` is introduced, which only matches applications of
`compare`.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-02-25 10:08:23 +00:00
Lean stage0 autoupdater
c7f706baeb chore: update stage0 2025-02-25 08:57:53 +00:00
666 changed files with 14468 additions and 3284 deletions

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

View File

@@ -25,7 +25,10 @@ cp llvm/lib/clang/*/include/{std*,__std*,limits}.h stage1/include/clang
echo '
// https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode
#define SEM_FAILCRITICALERRORS 0x0001
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);' > stage1/include/clang/windows.h
__declspec(dllimport) __stdcall unsigned int SetErrorMode(unsigned int uMode);
// https://docs.microsoft.com/en-us/windows/console/setconsoleoutputcp
#define CP_UTF8 65001
__declspec(dllimport) __stdcall int SetConsoleOutputCP(unsigned int wCodePageID);' > stage1/include/clang/windows.h
# COFF dependencies
cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
# runtime

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))

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

@@ -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

@@ -144,6 +144,8 @@ end List
namespace Array
theorem size_eq_length_toList (xs : Array α) : xs.size = xs.toList.length := rfl
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
@@ -1090,6 +1092,11 @@ def split (as : Array α) (p : α → Bool) : Array α × Array α :=
as.foldl (init := (#[], #[])) fun (as, bs) a =>
if p a then (as.push a, bs) else (as, bs.push a)
def replace [BEq α] (xs : Array α) (a b : α) : Array α :=
match xs.finIdxOf? a with
| none => xs
| some i => xs.set i b
/-! ### Lexicographic ordering -/
instance instLT [LT α] : LT (Array α) := fun as bs => as.toList < bs.toList

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

View File

@@ -299,24 +299,6 @@ theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
rcases xs with xs
simp [List.find?_eq_some_iff_getElem]
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := rfl
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
theorem findFinIdx?_congr {p : α Bool} {xs ys : Array α} (w : xs = ys) :
findFinIdx? p xs = (findFinIdx? p ys).map (fun i => i.cast (by simp [w])) := by
subst w
simp
@[simp] theorem findFinIdx?_subtype {p : α Prop} {xs : Array { x // p x }}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
xs.findFinIdx? f = (xs.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
cases xs
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
rw [findFinIdx?_congr List.unattach_toArray]
simp [Function.comp_def]
/-! ### findIdx -/
theorem findIdx_of_getElem?_eq_some {xs : Array α} (w : xs[xs.findIdx p]? = some y) : p y := by
@@ -542,6 +524,47 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
cases xs
simp
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := rfl
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
theorem findFinIdx?_congr {p : α Bool} {xs ys : Array α} (w : xs = ys) :
findFinIdx? p xs = (findFinIdx? p ys).map (fun i => i.cast (by simp [w])) := by
subst w
simp
theorem findFinIdx?_eq_pmap_findIdx? {xs : Array α} {p : α Bool} :
xs.findFinIdx? p =
(xs.findIdx? p).pmap
(fun i m => by simp [findIdx?_eq_some_iff_getElem] at m; exact i, m.choose)
(fun i h => h) := by
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
@[simp] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
xs.findFinIdx? p = none x, x xs ¬ p x := by
simp [findFinIdx?_eq_pmap_findIdx?]
@[simp]
theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α Bool} {i : Fin xs.size} :
xs.findFinIdx? p = some i
p xs[i] j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
constructor
· rintro a, h, w₁, w₂, rfl
exact w₁, fun j hji => by simpa using w₂ j hji
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[simp] theorem findFinIdx?_subtype {p : α Prop} {xs : Array { x // p x }}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
xs.findFinIdx? f = (xs.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
cases xs
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
rw [findFinIdx?_congr List.unattach_toArray]
simp [Function.comp_def]
/-! ### idxOf
The verification API for `idxOf` is still incomplete.
@@ -579,10 +602,26 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
rcases xs with xs
simp [List.idxOf?_eq_none_iff]
/-! ### finIdxOf? -/
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findFinIdx?` (and proved using them).
-/
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := rfl
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.finIdxOf? a = none a xs := by
rcases xs with xs
simp [List.finIdxOf?_eq_none_iff]
@[simp] theorem finIdxOf?_eq_some_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} {i : Fin xs.size} :
xs.finIdxOf? a = some i xs[i] = a j (_ : j < i), ¬xs[j] = a := by
rcases xs with xs
simp [List.finIdxOf?_eq_some_iff]
end Array

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,9 @@ open Nat
/-! ### mapM -/
@[simp] theorem mapM_id {xs : Array α} {f : α Id β} : xs.mapM f = xs.map f := by
induction xs; simp_all
@[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

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

@@ -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

@@ -13,6 +13,7 @@ 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
set_option linter.missingDocs true
@@ -569,6 +570,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
@@ -1467,6 +1473,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
@@ -2693,6 +2709,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
@@ -4109,9 +4128,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

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

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`.
-/
/--

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,25 +21,25 @@ 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`
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`.
This is the function powering the `/` notation on integers.
@@ -71,7 +71,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`.
@@ -229,7 +229,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 +268,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
@@ -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,12 +105,17 @@ 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]
@@ -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,7 +178,7 @@ 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), _ =>
@@ -178,6 +186,9 @@ theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b ≥ 0 ↔ a ≥ 0
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 -/
theorem emod_nonneg : (a : Int) {b : Int}, b 0 0 a % b
@@ -189,16 +200,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 +307,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

View File

@@ -40,6 +40,17 @@ protected theorem dvd_add_left {a b c : Int} (H : a c) : a b + c ↔ a
protected theorem dvd_add_right {a b c : Int} (H : a b) : a b + c a c := by
rw [Int.add_comm, Int.dvd_add_left H]
@[simp] protected theorem dvd_add_mul_self {a b c : Int} : a b + c * a a b := by
rw [Int.dvd_add_left (Int.dvd_mul_left c a)]
@[simp] protected theorem dvd_add_self_mul {a b c : Int} : a b + a * c a b := by
rw [Int.mul_comm, Int.dvd_add_mul_self]
@[simp] protected theorem dvd_mul_self_add {a b c : Int} : a b * a + c a c := by
rw [Int.add_comm, Int.dvd_add_mul_self]
@[simp] protected theorem dvd_self_mul_add {a b c : Int} : a a * b + c a c := by
rw [Int.mul_comm, Int.dvd_mul_self_add]
protected theorem dvd_iff_dvd_of_dvd_sub {a b c : Int} (H : a b - c) : a b a c :=
fun h => Int.sub_sub_self b c Int.dvd_sub h H,
fun h => Int.sub_add_cancel b c Int.dvd_add H h
@@ -94,6 +105,14 @@ theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 ≤ b) (H' : a * b = 1) : b
instance decidableDvd : DecidableRel (α := Int) (· ·) := fun _ _ =>
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
protected theorem mul_dvd_mul_iff_left {a b c : Int} (h : a 0) : (a * b) (a * c) b c :=
by rintro d, h'; exact d, by rw [Int.mul_assoc] at h'; exact (mul_eq_mul_left_iff h).mp h',
by rintro d, rfl; exact d, by simp [Int.mul_assoc]
protected theorem mul_dvd_mul_iff_right {a b c : Int} (h : a 0) : (b * a) (c * a) b c := by
rw [Int.mul_comm b a, Int.mul_comm c a]
exact Int.mul_dvd_mul_iff_left h
/-! ### *div zero -/
@[simp] protected theorem zero_tdiv : b : Int, tdiv 0 b = 0
@@ -234,6 +253,13 @@ theorem tdiv_eq_fdiv {a b : Int} :
rw [fdiv_eq_tdiv]
omega
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.tdiv b = a / b := by
simp [tdiv_eq_ediv, h]
theorem fdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.fdiv b = a / b := by
simp [fdiv_eq_ediv, h]
/-! ### mod zero -/
@[simp] theorem zero_tmod (b : Int) : tmod 0 b = 0 := by cases b <;> simp [tmod]
@@ -251,9 +277,6 @@ theorem tdiv_eq_fdiv {a b : Int} :
/-! ### mod definitions -/
theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
rw [Int.mul_comm]; exact ediv_add_emod ..
theorem tmod_add_tdiv : a b : Int, tmod a b + b * (a.tdiv b) = a
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
| ofNat m, -[n+1] => by
@@ -274,9 +297,11 @@ theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
theorem tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
rw [Int.add_comm]; apply tmod_add_tdiv ..
/-- Variant of `tmod_add_tdiv` with the multiplication written the other way around. -/
theorem tmod_add_tdiv' (m k : Int) : tmod m k + m.tdiv k * k = m := by
rw [Int.mul_comm]; apply tmod_add_tdiv
/-- Variant of `tdiv_add_tmod` with the multiplication written the other way around. -/
theorem tdiv_add_tmod' (m k : Int) : m.tdiv k * k + tmod m k = m := by
rw [Int.mul_comm]; apply tdiv_add_tmod
@@ -300,9 +325,17 @@ theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
show -((succ m % succ n) : Int) + -(succ n * (succ m / succ n)) = -(succ m)
rw [ Int.neg_add]; exact congrArg (-ofNat ·) <| Nat.mod_add_div ..
/-- Variant of `fmod_add_fdiv` with the multiplication written the other way around. -/
theorem fmod_add_fdiv' (a b : Int) : a.fmod b + (a.fdiv b) * b = a := by
rw [Int.mul_comm]; exact fmod_add_fdiv ..
theorem fdiv_add_fmod (a b : Int) : b * a.fdiv b + a.fmod b = a := by
rw [Int.add_comm]; exact fmod_add_fdiv ..
/-- Variant of `fdiv_add_fmod` with the multiplication written the other way around. -/
theorem fdiv_add_fmod' (a b : Int) : (a.fdiv b) * b + a.fmod b = a := by
rw [Int.mul_comm]; exact fdiv_add_fmod ..
theorem fmod_def (a b : Int) : a.fmod b = a - b * a.fdiv b := by
rw [ Int.add_sub_cancel (a.fmod b), fmod_add_fdiv]
@@ -368,10 +401,13 @@ theorem tmod_eq_fmod {a b : Int} :
/-! ### `/` ediv -/
theorem ediv_neg' {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
theorem ediv_neg_of_neg_of_pos {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
match a, b, eq_negSucc_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
| _, _, _, rfl, _, rfl => negSucc_lt_zero _
@[deprecated ediv_neg_of_neg_of_pos (since := "2025-03-04")]
abbrev ediv_neg' := @ediv_neg_of_neg_of_pos
theorem negSucc_ediv (m : Nat) {b : Int} (H : 0 < b) : -[m+1] / b = -(ediv m b + 1) :=
match b, eq_succ_of_zero_lt H with
| _, _, rfl => rfl
@@ -396,17 +432,21 @@ theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0
rw [Int.div_def, ediv]
exact le_add_one (ediv_nonneg (ofNat_zero_le a) (Int.le_trans (ofNat_zero_le b) (le.intro 1 rfl)))
theorem ediv_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a / b 0 :=
theorem ediv_pos_of_neg_of_neg {a b : Int} (ha : a < 0) (hb : b < 0) : 0 < a / b := by
rw [Int.div_def]
match a, b, ha, hb with
| .negSucc a, .negSucc b, _, _ => apply ofNat_succ_pos
theorem ediv_nonpos_of_nonneg_of_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a / b 0 :=
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
@[deprecated ediv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev ediv_nonpos := @ediv_nonpos_of_nonneg_of_nonpos
theorem ediv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a / b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
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
@[simp] theorem mul_ediv_mul_of_pos {a : Int}
(b c : Int) (H : 0 < a) : (a * b) / (a * c) = b / c :=
suffices (m k : Nat) (b : Int), (m.succ * b) / (m.succ * k) = b / k from
@@ -446,6 +486,19 @@ protected theorem ediv_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a / b = c :=
Int.ediv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
protected theorem eq_ediv_of_mul_eq_left {a b c : Int}
(H1 : b 0) (H2 : a * b = c) : a = c / b :=
(Int.ediv_eq_of_eq_mul_left H1 H2.symm).symm
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp] protected theorem neg_ediv_self (a : Int) (h : a 0) : (-a) / a = -1 := by
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
-- There are no theorems `neg_ediv : ∀ {a b : Int}, (-a) / b = - (a / b)` or
-- `neg_ediv_neg: ∀ {a b : Int}, (-a) / (-b) = a / b` because these are false.
/-! ### emod -/
theorem mod_def' (m n : Int) : m % n = emod m n := rfl
@@ -576,12 +629,6 @@ theorem dvd_emod_sub_self {x : Int} {m : Nat} : (m : Int) x % m - x := by
@[simp] theorem emod_one (a : Int) : a % 1 = 0 := by
simp [emod_def, Int.one_mul, Int.sub_self]
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp] protected theorem neg_ediv_self (a : Int) (h : a 0) : (-a) / a = -1 := by
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
@[simp]
theorem emod_sub_cancel (x y : Int): (x - y) % y = x % y := by
by_cases h : y = 0
@@ -715,9 +762,7 @@ theorem ediv_eq_ediv_of_mul_eq_mul {a b c d : Int}
/-! ### tdiv -/
@[simp] protected theorem tdiv_one : a : Int, a.tdiv 1 = a
| (n:Nat) => congrArg ofNat (Nat.div_one _)
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
-- `tdiv` analogues of `ediv` lemmas from `Bootstrap.lean`
unseal Nat.div in
@[simp] protected theorem tdiv_neg : a b : Int, a.tdiv (-b) = -(a.tdiv b)
@@ -725,6 +770,102 @@ unseal Nat.div in
| ofNat _, -[_+1] | -[_+1], succ _ => (Int.neg_neg _).symm
| ofNat _, succ _ | -[_+1], 0 | -[_+1], -[_+1] => rfl
/-!
There are no lemmas
* `add_mul_tdiv_right : c ≠ 0 → (a + b * c).tdiv c = a.tdiv c + b`
* `add_mul_tdiv_left : b ≠ 0 → (a + b * c).tdiv b = a.tdiv b + c`
* `add_tdiv_of_dvd_right : c b → (a + b).tdiv c = a.tdiv c + b.tdiv c`
* `add_tdiv_of_dvd_left : c a → (a + b).tdiv c = a.tdiv c + b.tdiv c`
because these statements are all incorrect, and require awkward conditional off-by-one corrections.
-/
@[simp] theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b 0) : (a * b).tdiv b = a := by
rw [tdiv_eq_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ H]
@[simp] theorem mul_tdiv_cancel_left (b : Int) (H : a 0) : (a * b).tdiv a = b :=
Int.mul_comm .. Int.mul_tdiv_cancel _ H
-- `tdiv` analogues of `ediv` lemmas given above
-- There are no lemmas `tdiv_nonneg_iff_of_pos`, `tdiv_neg_of_neg_of_pos`, or `negSucc_tdiv`
-- corresponding to `ediv_nonneg_iff_of_pos`, `ediv_neg_of_neg_of_pos`, or `negSucc_ediv` as they require awkward corrections.
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.tdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_zero_le _
theorem tdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a.tdiv b := by
rw [tdiv_eq_ediv]
split <;> rename_i h
· simpa using ediv_nonneg_of_nonpos_of_nonpos Ha Hb
· simp at h
by_cases h' : b = 0
· subst h'
simp
· replace h' : b < 0 := by omega
rw [sign_eq_neg_one_of_neg h']
have : 0 < a / b := by
by_cases h'' : a = 0
· subst h''
simp at h
· replace h'' : a < 0 := by omega
exact ediv_pos_of_neg_of_neg h'' h'
omega
protected theorem tdiv_nonpos_of_nonneg_of_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a.tdiv b 0 :=
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
@[deprecated Int.tdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev tdiv_nonpos := @Int.tdiv_nonpos_of_nonneg_of_nonpos
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.tdiv b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
@[simp] theorem mul_tdiv_mul_of_pos {a : Int}
(b c : Int) (H : 0 < a) : (a * b).tdiv (a * c) = b.tdiv c := by
rw [tdiv_eq_ediv, mul_ediv_mul_of_pos _ _ H, tdiv_eq_ediv]
simp only [sign_mul]
by_cases h : 0 b
· rw [if_pos, if_pos (.inl h)]
left
exact Int.mul_nonneg (Int.le_of_lt H) h
· have H' : a 0 := by omega
simp only [Int.mul_dvd_mul_iff_left H']
by_cases h' : c b
· simp [h']
· rw [if_neg, if_neg]
· simp [sign_eq_one_of_pos H]
· simp [h']; omega
· simp_all only [Int.not_le, ne_eq, or_false]
exact Int.mul_neg_of_pos_of_neg H h
@[simp] theorem mul_tdiv_mul_of_pos_left
(a : Int) {b : Int} (c : Int) (H : 0 < b) : (a * b).tdiv (c * b) = a.tdiv c := by
rw [Int.mul_comm, Int.mul_comm c, mul_tdiv_mul_of_pos _ _ H]
@[simp] protected theorem tdiv_one : a : Int, a.tdiv 1 = a
| (n:Nat) => congrArg ofNat (Nat.div_one _)
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
(H1 : b 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
(H1 : a 0) (H2 : a * b = c) : b = c.tdiv a :=
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a.tdiv b = c :=
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
protected theorem eq_tdiv_of_mul_eq_left {a b c : Int}
(H1 : b 0) (H2 : a * b = c) : a = c.tdiv b :=
(Int.tdiv_eq_of_eq_mul_left H1 H2.symm).symm
@[simp] protected theorem tdiv_self {a : Int} (H : a 0) : a.tdiv a = 1 := by
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
unseal Nat.div in
@[simp] protected theorem neg_tdiv : a b : Int, (-a).tdiv b = -(a.tdiv b)
| 0, n => by simp [Int.neg_zero]
@@ -734,35 +875,117 @@ unseal Nat.div in
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
simp [Int.tdiv_neg, Int.neg_tdiv, Int.neg_neg]
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.tdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_zero_le _
@[simp] theorem natAbs_tdiv (a b : Int) : natAbs (a.tdiv b) = (natAbs a).div (natAbs b) :=
match a, b, eq_nat_or_neg a, eq_nat_or_neg b with
| _, _, _, .inl rfl, _, .inl rfl => rfl
| _, _, _, .inl rfl, _, .inr rfl => by rw [Int.tdiv_neg, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inl rfl => by rw [Int.neg_tdiv, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inr rfl => by rw [Int.neg_tdiv_neg, natAbs_neg, natAbs_neg]; rfl
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a.tdiv b 0 :=
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
/-! ### tmod -/
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.tdiv b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
-- `tmod` analogues of `emod` lemmas from `Bootstrap.lean`
@[simp] protected theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b 0) : (a * b).tdiv b = a :=
have : {a b : Nat}, (b : Int) 0 (tdiv (a * b) b : Int) = a := fun H => by
rw [ ofNat_mul, ofNat_tdiv,
Nat.mul_div_cancel _ <| Nat.pos_of_ne_zero <| Int.ofNat_ne_zero.1 H]
match a, b, a.eq_nat_or_neg, b.eq_nat_or_neg with
| _, _, a, .inl rfl, b, .inl rfl => this H
| _, _, a, .inl rfl, b, .inr rfl => by
rw [Int.mul_neg, Int.neg_tdiv, Int.tdiv_neg, Int.neg_neg,
this (Int.neg_ne_zero.1 H)]
| _, _, a, .inr rfl, b, .inl rfl => by rw [Int.neg_mul, Int.neg_tdiv, this H]
| _, _, a, .inr rfl, b, .inr rfl => by
rw [Int.neg_mul_neg, Int.tdiv_neg, this (Int.neg_ne_zero.1 H)]
theorem ofNat_tmod (m n : Nat) : ((m % n) : Int) = tmod m n := rfl
@[simp] protected theorem mul_tdiv_cancel_left (b : Int) (H : a 0) : (a * b).tdiv a = b :=
Int.mul_comm .. Int.mul_tdiv_cancel _ H
@[simp] theorem tmod_one (a : Int) : tmod a 1 = 0 := by
simp [tmod_def, Int.tdiv_one, Int.one_mul, Int.sub_self]
@[simp] protected theorem tdiv_self {a : Int} (H : a 0) : a.tdiv a = 1 := by
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
theorem tmod_nonneg : {a : Int} (b : Int), 0 a 0 tmod a b
| ofNat _, -[_+1], _ | ofNat _, ofNat _, _ => ofNat_nonneg _
theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
match a, b, eq_succ_of_zero_lt H with
| ofNat _, _, n, rfl => ofNat_lt.2 <| Nat.mod_lt _ n.succ_pos
| -[_+1], _, n, rfl => Int.lt_of_le_of_lt
(Int.neg_nonpos_of_nonneg <| Int.ofNat_nonneg _) (ofNat_pos.2 n.succ_pos)
@[simp] theorem tmod_neg (a b : Int) : tmod a (-b) = tmod a b := by
rw [tmod_def, tmod_def, Int.tdiv_neg, Int.neg_mul_neg]
@[simp] theorem neg_tmod (a b : Int) : tmod (-a) b = -tmod a b := by
rw [tmod_def, Int.neg_tdiv, Int.mul_neg, tmod_def]
omega
-- The following statements for `tmod` are false:
-- `add_mul_tmod_self {a b c : Int} : (a + b * c).tmod c = a.tmod c`
-- `add_mul_tmod_self_left (a b c : Int) : (a + b * c).tmod b = a.tmod b`
-- `tmod_add_tmod (m n k : Int) : (m.tmod n + k).tmod n = (m + k).tmod n`
-- `add_tmod_tmod (m n k : Int) : (m + n.tmod k).tmod k = (m + n).tmod k`
-- `add_tmod (a b n : Int) : (a + b).tmod n = (a.tmod n + b.tmod n).tmod n`
-- `add_tmod_eq_add_tmod_right {m n k : Int} (i : Int) : (m.tmod n = k.tmod n) → (m + i).tmod n = (k + i).tmod n`
-- `tmod_add_cancel_right {m n k : Int} (i) : (m + i).tmod n = (k + i).tmod n ↔ m.tmod n = k.tmod n`
-- `sub_tmod (a b n : Int) : (a - b).tmod n = (a.tmod n - b.tmod n).tmod n`
@[simp] theorem mul_tmod_left (a b : Int) : (a * b).tmod b = 0 :=
if h : b = 0 then by simp [h, Int.mul_zero] else by
rw [Int.tmod_def, Int.mul_tdiv_cancel _ h, Int.mul_comm, Int.sub_self]
@[simp] theorem mul_tmod_right (a b : Int) : (a * b).tmod a = 0 := by
rw [Int.mul_comm, mul_tmod_left]
/--
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 : a, P a P (-a)) (w : n : Nat, P n) (a : Int) : P a := by
cases a with
| ofNat n => exact w n
| negSucc n =>
rw [negSucc_eq, inv, ofNat_succ]
apply w
attribute [local simp] Int.neg_inj
theorem mul_tmod (a b n : Int) : (a * b).tmod n = (a.tmod n * b.tmod n).tmod n := by
induction a using wlog_sign
case inv => simp
induction b using wlog_sign
case inv => simp
induction n using wlog_sign
case inv => simp
simp only [ Int.natCast_mul, ofNat_tmod]
rw [Nat.mul_mod]
@[simp] theorem tmod_self {a : Int} : a.tmod a = 0 := by
have := mul_tmod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem tmod_tmod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n.tmod k).tmod m = n.tmod m := by
induction n using wlog_sign
case inv => simp
induction k using wlog_sign
case inv => simp [Int.dvd_neg]
induction m using wlog_sign
case inv => simp
simp only [ Int.natCast_mul, ofNat_tmod]
norm_cast at h
rw [Nat.mod_mod_of_dvd _ h]
@[simp] theorem tmod_tmod (a b : Int) : (a.tmod b).tmod b = a.tmod b :=
tmod_tmod_of_dvd a (Int.dvd_refl b)
theorem tmod_eq_zero_of_dvd : {a b : Int}, a b tmod b a = 0
| _, _, _, rfl => mul_tmod_right ..
-- `tmod` analogues of `emod` lemmas from above
theorem tmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : tmod a b = a := by
rw [tmod_eq_emod_of_nonneg H1, emod_eq_of_lt H1 H2]
-- lemmas about `tmod` without `emod` analogues
theorem tdiv_sign : a b, a.tdiv (sign b) = a * sign b
| _, succ _ => by simp [sign, Int.mul_one]
| _, 0 => by simp [sign, Int.mul_zero]
| _, -[_+1] => by simp [sign, Int.mul_neg, Int.mul_one]
protected theorem sign_eq_tdiv_abs (a : Int) : sign a = a.tdiv (natAbs a) :=
if az : a = 0 then by simp [az] else
(Int.tdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(sign_mul_natAbs _).symm).symm
/-! properties of `tdiv` and `tmod` -/
theorem mul_tdiv_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : b * (a.tdiv b) = a := by
have := tmod_add_tdiv a b; rwa [H, Int.zero_add] at this
@@ -773,68 +996,6 @@ theorem tdiv_mul_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : a.tdiv
theorem dvd_of_tmod_eq_zero {a b : Int} (H : tmod b a = 0) : a b :=
b.tdiv a, (mul_tdiv_cancel_of_tmod_eq_zero H).symm
protected theorem mul_tdiv_assoc (a : Int) : {b c : Int}, c b (a * b).tdiv c = a * (b.tdiv c)
| _, c, d, rfl =>
if cz : c = 0 then by simp [cz, Int.mul_zero] else by
rw [Int.mul_left_comm, Int.mul_tdiv_cancel_left _ cz, Int.mul_tdiv_cancel_left _ cz]
protected theorem mul_tdiv_assoc' (b : Int) {a c : Int} (h : c a) :
(a * b).tdiv c = a.tdiv c * b := by
rw [Int.mul_comm, Int.mul_tdiv_assoc _ h, Int.mul_comm]
theorem tdiv_dvd_tdiv : {a b c : Int}, a b b c b.tdiv a c.tdiv a
| a, _, _, b, rfl, c, rfl => by
by_cases az : a = 0
· simp [az]
· rw [Int.mul_tdiv_cancel_left _ az, Int.mul_assoc, Int.mul_tdiv_cancel_left _ az]
apply Int.dvd_mul_right
@[simp] theorem natAbs_tdiv (a b : Int) : natAbs (a.tdiv b) = (natAbs a).div (natAbs b) :=
match a, b, eq_nat_or_neg a, eq_nat_or_neg b with
| _, _, _, .inl rfl, _, .inl rfl => rfl
| _, _, _, .inl rfl, _, .inr rfl => by rw [Int.tdiv_neg, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inl rfl => by rw [Int.neg_tdiv, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inr rfl => by rw [Int.neg_tdiv_neg, natAbs_neg, natAbs_neg]; rfl
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
(H1 : b 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
(H1 : a 0) (H2 : a * b = c) : b = c.tdiv a :=
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
/-! ### (t-)mod -/
theorem ofNat_tmod (m n : Nat) : ((m % n) : Int) = tmod m n := rfl
@[simp] theorem tmod_one (a : Int) : tmod a 1 = 0 := by
simp [tmod_def, Int.tdiv_one, Int.one_mul, Int.sub_self]
theorem tmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : tmod a b = a := by
rw [tmod_eq_emod_of_nonneg H1, emod_eq_of_lt H1 H2]
theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
match a, b, eq_succ_of_zero_lt H with
| ofNat _, _, n, rfl => ofNat_lt.2 <| Nat.mod_lt _ n.succ_pos
| -[_+1], _, n, rfl => Int.lt_of_le_of_lt
(Int.neg_nonpos_of_nonneg <| Int.ofNat_nonneg _) (ofNat_pos.2 n.succ_pos)
theorem tmod_nonneg : {a : Int} (b : Int), 0 a 0 tmod a b
| ofNat _, -[_+1], _ | ofNat _, ofNat _, _ => ofNat_nonneg _
@[simp] theorem tmod_neg (a b : Int) : tmod a (-b) = tmod a b := by
rw [tmod_def, tmod_def, Int.tdiv_neg, Int.neg_mul_neg]
@[simp] theorem mul_tmod_left (a b : Int) : (a * b).tmod b = 0 :=
if h : b = 0 then by simp [h, Int.mul_zero] else by
rw [Int.tmod_def, Int.mul_tdiv_cancel _ h, Int.mul_comm, Int.sub_self]
@[simp] theorem mul_tmod_right (a b : Int) : (a * b).tmod a = 0 := by
rw [Int.mul_comm, mul_tmod_left]
theorem tmod_eq_zero_of_dvd : {a b : Int}, a b tmod b a = 0
| _, _, _, rfl => mul_tmod_right ..
theorem dvd_iff_tmod_eq_zero {a b : Int} : a b tmod b a = 0 :=
tmod_eq_zero_of_dvd, dvd_of_tmod_eq_zero
@@ -855,9 +1016,6 @@ protected theorem mul_tdiv_cancel' {a b : Int} (H : a b) : a * b.tdiv a = b
protected theorem eq_mul_of_tdiv_eq_right {a b c : Int}
(H1 : b a) (H2 : a.tdiv b = c) : a = b * c := by rw [ H2, Int.mul_tdiv_cancel' H1]
@[simp] theorem tmod_self {a : Int} : a.tmod a = 0 := by
have := mul_tmod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem neg_tmod_self (a : Int) : (-a).tmod a = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_refl a
@@ -878,10 +1036,6 @@ protected theorem eq_mul_of_tdiv_eq_left {a b c : Int}
(H1 : b a) (H2 : a.tdiv b = c) : a = c * b := by
rw [Int.mul_comm, Int.eq_mul_of_tdiv_eq_right H1 H2]
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a.tdiv b = c :=
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d n) (H : n.tdiv d = 0) : n = 0 := by
rw [ Int.mul_tdiv_cancel' h, H, Int.mul_zero]
@@ -890,33 +1044,50 @@ protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d n) (H : n.tdiv
refine fun h => ?_, congrArg (tdiv · d)
rw [ Int.mul_tdiv_cancel' hda, Int.mul_tdiv_cancel' hdb, h]
theorem tdiv_sign : a b, a.tdiv (sign b) = a * sign b
| _, succ _ => by simp [sign, Int.mul_one]
| _, 0 => by simp [sign, Int.mul_zero]
| _, -[_+1] => by simp [sign, Int.mul_neg, Int.mul_one]
protected theorem mul_tdiv_assoc (a : Int) : {b c : Int}, c b (a * b).tdiv c = a * (b.tdiv c)
| _, c, d, rfl =>
if cz : c = 0 then by simp [cz, Int.mul_zero] else by
rw [Int.mul_left_comm, Int.mul_tdiv_cancel_left _ cz, Int.mul_tdiv_cancel_left _ cz]
protected theorem sign_eq_tdiv_abs (a : Int) : sign a = a.tdiv (natAbs a) :=
if az : a = 0 then by simp [az] else
(Int.tdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(sign_mul_natAbs _).symm).symm
protected theorem mul_tdiv_assoc' (b : Int) {a c : Int} (h : c a) :
(a * b).tdiv c = a.tdiv c * b := by
rw [Int.mul_comm, Int.mul_tdiv_assoc _ h, Int.mul_comm]
theorem tdiv_dvd_tdiv : {a b c : Int}, a b b c b.tdiv a c.tdiv a
| a, _, _, b, rfl, c, rfl => by
by_cases az : a = 0
· simp [az]
· rw [Int.mul_tdiv_cancel_left _ az, Int.mul_assoc, Int.mul_tdiv_cancel_left _ az]
apply Int.dvd_mul_right
/-! ### `tdiv` and ordering -/
-- Theorems about `tdiv` and ordering, whose `ediv` analogues are in `Bootstrap.lean`.
theorem mul_tdiv_self_le {x k : Int} (h : 0 x) : k * (x.tdiv k) x := by
by_cases w : k = 0
· simp [w, h]
· rw [tdiv_eq_ediv_of_nonneg h]
apply mul_ediv_self_le w
theorem lt_mul_tdiv_self_add {x k : Int} (h : 0 < k) : x < k * (x.tdiv k) + k := by
rw [tdiv_eq_ediv, sign_eq_one_of_pos h]
have := lt_mul_ediv_self_add (x := x) h
split <;> simp [Int.mul_add] <;> omega
/-! ### fdiv -/
theorem fdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.fdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_fdiv .. ofNat_zero_le _
-- There is no theorem `fdiv_neg : ∀ a b : Int, a.fdiv (-b) = -(a.fdiv b)`
-- because this is false, for example at `a = 2`, `b = 3`, as `-1 ≠ 0`.
unseal Nat.div in
theorem fdiv_nonpos : {a b : Int}, 0 a b 0 a.fdiv b 0
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => _
theorem add_mul_fdiv_right (a b : Int) {c : Int} (H : c 0) : (a + b * c).fdiv c = a.fdiv c + b := by
rw [fdiv_eq_ediv, add_mul_ediv_right _ _ H, fdiv_eq_ediv]
simp only [Int.dvd_add_left (Int.dvd_mul_left _ _)]
split <;> omega
theorem fdiv_neg' : {a b : Int}, a < 0 0 < b a.fdiv b < 0
| -[_+1], succ _, _, _ => negSucc_lt_zero _
@[simp] theorem fdiv_one : a : Int, a.fdiv 1 = a
| 0 => rfl
| succ _ => congrArg Nat.cast (Nat.div_one _)
| -[_+1] => congrArg negSucc (Nat.div_one _)
theorem add_mul_fdiv_left (a : Int) {b : Int}
(c : Int) (H : b 0) : (a + b * c).fdiv b = a.fdiv b + c := by
rw [Int.mul_comm, Int.add_mul_fdiv_right _ _ H]
@[simp] theorem mul_fdiv_cancel (a : Int) {b : Int} (H : b 0) : fdiv (a * b) b = a :=
if b0 : 0 b then by
@@ -932,32 +1103,174 @@ theorem fdiv_neg' : ∀ {a b : Int}, a < 0 → 0 < b → a.fdiv b < 0
@[simp] theorem mul_fdiv_cancel_left (b : Int) (H : a 0) : fdiv (a * b) a = b :=
Int.mul_comm .. Int.mul_fdiv_cancel _ H
theorem add_fdiv_of_dvd_right {a b c : Int} (H : c b) : (a + b).fdiv c = a.fdiv c + b.fdiv c := by
by_cases h : c = 0
· simp [h]
· obtain d, rfl := H
rw [add_mul_fdiv_left _ _ h]
simp [h]
theorem add_fdiv_of_dvd_left {a b c : Int} (H : c a) : (a + b).fdiv c = a.fdiv c + b.fdiv c := by
rw [Int.add_comm, Int.add_fdiv_of_dvd_right H, Int.add_comm]
theorem fdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.fdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_fdiv .. ofNat_zero_le _
theorem fdiv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a.fdiv b := by
rw [fdiv_eq_ediv]
by_cases ha : a = 0
· simp [ha]
· by_cases hb : b = 0
· simp [hb]
· have : 0 < a / b := ediv_pos_of_neg_of_neg (by omega) (by omega)
split <;> omega
unseal Nat.div in
theorem fdiv_nonpos_of_nonneg_of_nonpos : {a b : Int}, 0 a b 0 a.fdiv b 0
| 0, 0, _, _ | 0, -[_+1], _, _ | succ _, 0, _, _ | succ _, -[_+1], _, _ => _
@[deprecated fdiv_nonpos_of_nonneg_of_nonpos (since := "2025-03-04")]
abbrev fdiv_nonpos := @fdiv_nonpos_of_nonneg_of_nonpos
theorem fdiv_neg_of_neg_of_pos : {a b : Int}, a < 0 0 < b a.fdiv b < 0
| -[_+1], succ _, _, _ => negSucc_lt_zero _
@[deprecated fdiv_neg_of_neg_of_pos (since := "2025-03-04")]
abbrev fdiv_neg := @fdiv_neg_of_neg_of_pos
theorem fdiv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.fdiv b = 0 := by
rw [fdiv_eq_ediv, if_pos, Int.sub_zero]
· apply ediv_eq_zero_of_lt (by omega) (by omega)
· left; omega
@[simp] theorem mul_fdiv_mul_of_pos {a : Int}
(b c : Int) (H : 0 < a) : (a * b).fdiv (a * c) = b.fdiv c := by
rw [fdiv_eq_ediv, mul_ediv_mul_of_pos _ _ H, fdiv_eq_ediv]
congr 2
simp [Int.mul_dvd_mul_iff_left (Int.ne_of_gt H)]
constructor
· rintro (h | h)
· exact .inl (Int.nonneg_of_mul_nonneg_right h H)
· exact .inr h
· rintro (h | h)
· exact .inl (Int.mul_nonneg (by omega) h)
· exact .inr h
@[simp] theorem mul_fdiv_mul_of_pos_left
(a : Int) {b : Int} (c : Int) (H : 0 < b) : (a * b).fdiv (c * b) = a.fdiv c := by
rw [Int.mul_comm a b, Int.mul_comm c b, Int.mul_fdiv_mul_of_pos _ _ H]
@[simp] theorem fdiv_one : a : Int, a.fdiv 1 = a
| 0 => rfl
| succ _ => congrArg Nat.cast (Nat.div_one _)
| -[_+1] => congrArg negSucc (Nat.div_one _)
protected theorem fdiv_eq_of_eq_mul_right {a b c : Int}
(H1 : b 0) (H2 : a = b * c) : a.fdiv b = c := by rw [H2, Int.mul_fdiv_cancel_left _ H1]
protected theorem eq_fdiv_of_mul_eq_right {a b c : Int}
(H1 : a 0) (H2 : a * b = c) : b = c.tdiv a :=
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
protected theorem fdiv_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a.fdiv b = c :=
Int.fdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
protected theorem eq_fdiv_of_mul_eq_left {a b c : Int}
(H1 : b 0) (H2 : a * b = c) : a = c.fdiv b :=
(Int.fdiv_eq_of_eq_mul_left H1 H2.symm).symm
@[simp] protected theorem fdiv_self {a : Int} (H : a 0) : a.fdiv a = 1 := by
have := Int.mul_fdiv_cancel 1 H; rwa [Int.one_mul] at this
theorem lt_fdiv_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.fdiv b + 1) * b :=
Int.fdiv_eq_ediv_of_nonneg _ (Int.le_of_lt H) lt_ediv_add_one_mul_self a H
-- `neg_fdiv : ∀ a b : Int, (-a).fdiv b = -(a.fdiv b)` is untrue.
protected theorem neg_fdiv_neg (a b : Int) : (-a).fdiv (-b) = a.fdiv b := by
match a, b with
| 0, 0 => rfl
| 0, ofNat b => simp
| 0, -[b+1] => simp
| ofNat (a + 1), 0 => simp
| ofNat (a + 1), ofNat (b + 1) =>
unfold fdiv
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
rw [ negSucc_eq, negSucc_eq]
| ofNat (a + 1), -[b+1] =>
unfold fdiv
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
rw [ negSucc_eq, neg_negSucc]
| -[a+1], 0 => simp
| -[a+1], ofNat (b + 1) =>
unfold fdiv
simp only [ofNat_eq_coe, natCast_add, Nat.cast_ofNat_Int, Nat.succ_eq_add_one]
rw [neg_negSucc, negSucc_eq]
| -[a+1], -[b+1] =>
unfold fdiv
simp only [ofNat_eq_coe, ofNat_ediv, Nat.succ_eq_add_one, natCast_add, Nat.cast_ofNat_Int]
rw [neg_negSucc, neg_negSucc]
simp
-- `natAbs_fdiv (a b : Int) : natAbs (a.fdiv b) = (natAbs a).div (natAbs b)` is untrue.
/-! ### fmod -/
-- `fmod` analogues of `emod` lemmas from `Bootstrap.lean`
theorem ofNat_fmod (m n : Nat) : (m % n) = fmod m n := by
cases m <;> simp [fmod, Nat.succ_eq_add_one]
@[simp] theorem fmod_one (a : Int) : a.fmod 1 = 0 := by
simp [fmod_def, Int.one_mul, Int.sub_self]
theorem fmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.fmod b = a := by
rw [fmod_eq_emod_of_nonneg _ (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
theorem fmod_nonneg {a b : Int} (ha : 0 a) (hb : 0 b) : 0 a.fmod b :=
fmod_eq_tmod_of_nonneg ha hb tmod_nonneg _ ha
theorem fmod_nonneg' (a : Int) {b : Int} (hb : 0 < b) : 0 a.fmod b :=
theorem fmod_nonneg_of_pos (a : Int) {b : Int} (hb : 0 < b) : 0 a.fmod b :=
fmod_eq_emod_of_nonneg _ (Int.le_of_lt hb) emod_nonneg _ (Int.ne_of_lt hb).symm
@[deprecated fmod_nonneg_of_pos (since := "2025-03-04")]
abbrev fmod_nonneg' := @fmod_nonneg_of_pos
theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
fmod_eq_emod_of_nonneg _ (Int.le_of_lt H) emod_lt_of_pos a H
-- There is no `fmod_neg : ∀ {a b : Int}, a.fmod (-b) = -a.fmod b` as this is false.
@[simp] theorem add_mul_fmod_self {a b c : Int} : (a + b * c).fmod c = a.fmod c := by
rw [fmod_eq_emod, add_mul_emod_self, fmod_eq_emod]
simp
@[simp] theorem add_mul_fmod_self_left (a b c : Int) : (a + b * c).fmod b = a.fmod b := by
rw [Int.mul_comm, Int.add_mul_fmod_self]
@[simp] theorem fmod_add_fmod (m n k : Int) : (m.fmod n + k).fmod n = (m + k).fmod n := by
by_cases h : n = 0
· simp [h]
rw [fmod_def, fmod_def]
conv => rhs; rw [fmod_def]
have : m - n * m.fdiv n + k = m + k + n * (- m.fdiv n) := by simp [Int.mul_neg]; omega
rw [this, add_fdiv_of_dvd_right (Int.dvd_mul_right ..), Int.mul_add, mul_fdiv_cancel_left _ h]
omega
@[simp] theorem add_fmod_fmod (m n k : Int) : (m + n.fmod k).fmod k = (m + n).fmod k := by
rw [Int.add_comm, Int.fmod_add_fmod, Int.add_comm]
theorem add_fmod (a b n : Int) : (a + b).fmod n = (a.fmod n + b.fmod n).fmod n := by
simp
theorem add_fmod_eq_add_fmod_right {m n k : Int} (i : Int)
(H : m.fmod n = k.fmod n) : (m + i).fmod n = (k + i).fmod n := by
rw [add_fmod]
conv => rhs; rw [add_fmod]
rw [H]
theorem fmod_add_cancel_right {m n k : Int} (i) : (m + i).fmod n = (k + i).fmod n m.fmod n = k.fmod n :=
fun H => by
have := add_fmod_eq_add_fmod_right (-i) H
rwa [Int.add_neg_cancel_right, Int.add_neg_cancel_right] at this,
add_fmod_eq_add_fmod_right _
@[simp] theorem mul_fmod_left (a b : Int) : (a * b).fmod b = 0 :=
if h : b = 0 then by simp [h, Int.mul_zero] else by
rw [Int.fmod_def, Int.mul_fdiv_cancel _ h, Int.mul_comm, Int.sub_self]
@@ -965,21 +1278,55 @@ theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
@[simp] theorem mul_fmod_right (a b : Int) : (a * b).fmod a = 0 := by
rw [Int.mul_comm, mul_fmod_left]
theorem mul_fmod (a b n : Int) : (a * b).fmod n = (a.fmod n * b.fmod n).fmod n := by
conv => lhs; rw [
fmod_add_fdiv a n, fmod_add_fdiv' b n, Int.add_mul, Int.mul_add, Int.mul_add,
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_fmod_self_left,
Int.mul_assoc, add_mul_fmod_self]
@[simp] theorem fmod_self {a : Int} : a.fmod a = 0 := by
have := mul_fmod_left 1 a; rwa [Int.one_mul] at this
/-! ### Theorems crossing div/mod versions -/
@[simp] theorem fmod_fmod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n.fmod k).fmod m = n.fmod m := by
conv => rhs; rw [ fmod_add_fdiv n k]
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_fmod_self_left]
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.tdiv b = a / b := by
by_cases b0 : b = 0
· simp [b0]
· rw [Int.tdiv_eq_iff_eq_mul_left b0 h, Int.ediv_eq_iff_eq_mul_left b0 h]
@[simp] theorem fmod_fmod (a b : Int) : (a.fmod b).fmod b = a.fmod b :=
fmod_fmod_of_dvd _ (Int.dvd_refl b)
theorem fdiv_eq_ediv_of_dvd : {a b : Int}, b a a.fdiv b = a / b
| _, b, c, rfl => by
by_cases bz : b = 0
· simp [bz]
· rw [mul_fdiv_cancel_left _ bz, mul_ediv_cancel_left _ bz]
theorem sub_fmod (a b n : Int) : (a - b).fmod n = (a.fmod n - b.fmod n).fmod n := by
apply (fmod_add_cancel_right b).mp
rw [Int.sub_add_cancel, Int.add_fmod_fmod, Int.sub_add_cancel, fmod_fmod]
theorem fmod_eq_zero_of_dvd : {a b : Int}, a b b.fmod a = 0
| _, _, _, rfl => mul_fmod_right ..
-- `fmod` analogues of `emod` lemmas from above
theorem fmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.fmod b = a := by
rw [fmod_eq_emod_of_nonneg _ (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
-- lemmas about `fmod` without `emod` analogues
theorem fdiv_sign {a b : Int} : a.fdiv (sign b) = a * sign b := by
rw [fdiv_eq_ediv]
rcases sign_trichotomy b with h | h | h <;> simp [h]
protected theorem sign_eq_fdiv_abs (a : Int) : sign a = a.fdiv (natAbs a) :=
if az : a = 0 then by simp [az] else
(Int.fdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(sign_mul_natAbs _).symm).symm
/-! ### properties of `fdiv` and `fmod` -/
/-! ### `fdiv` and ordering -/
-- Theorems about `fdiv` and ordering, whose `ediv` analogues are in `Bootstrap.lean`.
theorem lt_fdiv_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.fdiv b + 1) * b :=
Int.fdiv_eq_ediv_of_nonneg _ (Int.le_of_lt H) lt_ediv_add_one_mul_self a H
/-! ### bmod -/

View File

@@ -46,4 +46,23 @@ 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
@[simp] theorem natCast_le_zero : {n : Nat} (n : Int) 0 n = 0 := by omega
@[simp] theorem toNat_eq_zero : {n : Int}, n.toNat = 0 n 0 := by omega
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₁)
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
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
@@ -531,8 +532,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
@@ -820,7 +822,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
@@ -846,9 +848,6 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
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
@@ -1006,6 +1005,474 @@ theorem eq_of_core (ctx : Context) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
intro; subst p₃; simp
intro h; rw [h, Int.sub_eq_add_neg, Int.sub_self]
def Poly.isUnsatDiseq (p : Poly) : Bool :=
match p with
| .num 0 => true
| _ => false
theorem diseq_norm (ctx : Context) (p₁ p₂ : Poly) (h : p₁.norm == p₂) : p₁.denote' ctx 0 p₂.denote' ctx 0 := by
simp at h
replace h := congrArg (Poly.denote ctx) h
simp at h
simp [*]
theorem diseq_coeff (ctx : Context) (p p' : Poly) (k : Int) : eq_coeff_cert p p' k p.denote' ctx 0 p'.denote' ctx 0 := by
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
def diseq_eq_subst_cert (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly) : Bool :=
let a := p₁.coeff x
let b := p₂.coeff x
a != 0 && p₃ == (p₁.mul b |>.combine (p₂.mul (-a)))
theorem eq_diseq_subst (ctx : Context) (x : Var) (p₁ : Poly) (p₂ : Poly) (p₃ : Poly)
: diseq_eq_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [diseq_eq_subst_cert]
intros _ _; subst p₃
intro h₁ h₂
simp [*]
theorem diseq_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 [ Int.sub_eq_zero] at h
rw [Int.sub_eq_add_neg]; 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
intro h₁ h₂
replace h₂ := Int.neg_le_of_neg_le h₂; simp at 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
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
@[local simp] private theorem lcm_neg_left (a b : Int) : Int.lcm (-a) b = Int.lcm a b := by simp [Int.lcm]
@[local simp] private theorem lcm_neg_right (a b : Int) : Int.lcm a (-b) = Int.lcm a b := by simp [Int.lcm]
@[local simp] private theorem gcd_neg_left (a b : Int) : Int.gcd (-a) b = Int.gcd a b := by simp [Int.gcd]
@[local simp] private theorem gcd_neg_right (a b : Int) : Int.gcd a (-b) = Int.gcd a b := by simp [Int.gcd]
@[local simp] private theorem gcd_zero (a : Int) : Int.gcd a 0 = a.natAbs := by simp [Int.gcd]
@[local simp] 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
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by

View File

@@ -133,12 +133,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
@@ -938,6 +941,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
@@ -1021,6 +1040,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

View File

@@ -662,6 +662,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 +770,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 }}

View File

@@ -212,6 +212,7 @@ def mapMono (as : List α) (f : αα) : List α :=
/-! ## 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 +222,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

@@ -514,47 +514,6 @@ private theorem findIdx?_go_eq {p : α → Bool} {xs : List α} {i : Nat} :
(x :: xs).findIdx? p = if p x then some 0 else (xs.findIdx? p).map fun i => i + 1 := by
simp [findIdx?, findIdx?_go_eq]
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_nil {p : α Bool} : findFinIdx? p [] = none := rfl
theorem findIdx?_go_eq_map_findFinIdx?_go_val {xs : List α} {p : α Bool} {i : Nat} {h} :
List.findIdx?.go p xs i =
(List.findFinIdx?.go p l xs i h).map (·.val) := by
unfold findIdx?.go
unfold findFinIdx?.go
split
· simp_all
· simp only
split
· simp
· rw [findIdx?_go_eq_map_findFinIdx?_go_val]
theorem findIdx?_eq_map_findFinIdx?_val {xs : List α} {p : α Bool} :
xs.findIdx? p = (xs.findFinIdx? p).map (·.val) := by
simp [findIdx?, findFinIdx?]
rw [findIdx?_go_eq_map_findFinIdx?_go_val]
@[simp] theorem findFinIdx?_cons {p : α Bool} {x : α} {xs : List α} :
findFinIdx? p (x :: xs) = if p x then some 0 else (findFinIdx? p xs).map Fin.succ := by
rw [ Option.map_inj_right (f := Fin.val) (fun a b => Fin.eq_of_val_eq)]
rw [ findIdx?_eq_map_findFinIdx?_val]
rw [findIdx?_cons]
split
· simp
· rw [findIdx?_eq_map_findFinIdx?_val]
simp [Function.comp_def]
@[simp] theorem findFinIdx?_subtype {p : α Prop} {l : List { x // p x }}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
l.findFinIdx? f = (l.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
unfold unattach
induction l with
| nil => simp
| cons a l ih =>
simp [hf, findFinIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### findIdx -/
theorem findIdx_cons (p : α Bool) (b : α) (l : List α) :
@@ -976,6 +935,71 @@ theorem findIdx_eq_getD_findIdx? {xs : List α} {p : α → Bool} :
simp [hf, findIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_nil {p : α Bool} : findFinIdx? p [] = none := rfl
theorem findIdx?_go_eq_map_findFinIdx?_go_val {xs : List α} {p : α Bool} {i : Nat} {h} :
List.findIdx?.go p xs i =
(List.findFinIdx?.go p l xs i h).map (·.val) := by
unfold findIdx?.go
unfold findFinIdx?.go
split
· simp_all
· simp only
split
· simp
· rw [findIdx?_go_eq_map_findFinIdx?_go_val]
theorem findIdx?_eq_map_findFinIdx?_val {xs : List α} {p : α Bool} :
xs.findIdx? p = (xs.findFinIdx? p).map (·.val) := by
simp [findIdx?, findFinIdx?]
rw [findIdx?_go_eq_map_findFinIdx?_go_val]
theorem findFinIdx?_eq_pmap_findIdx? {xs : List α} {p : α Bool} :
xs.findFinIdx? p =
(xs.findIdx? p).pmap
(fun i m => by simp [findIdx?_eq_some_iff_getElem] at m; exact i, m.choose)
(fun i h => h) := by
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
@[simp] theorem findFinIdx?_cons {p : α Bool} {x : α} {xs : List α} :
findFinIdx? p (x :: xs) = if p x then some 0 else (findFinIdx? p xs).map Fin.succ := by
rw [ Option.map_inj_right (f := Fin.val) (fun a b => Fin.eq_of_val_eq)]
rw [ findIdx?_eq_map_findFinIdx?_val]
rw [findIdx?_cons]
split
· simp
· rw [findIdx?_eq_map_findFinIdx?_val]
simp [Function.comp_def]
@[simp] theorem findFinIdx?_eq_none_iff {l : List α} {p : α Bool} :
l.findFinIdx? p = none x l, ¬ p x := by
simp [findFinIdx?_eq_pmap_findIdx?]
@[simp]
theorem findFinIdx?_eq_some_iff {xs : List α} {p : α Bool} {i : Fin xs.length} :
xs.findFinIdx? p = some i
p xs[i] j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
constructor
· rintro a, h, w₁, w₂, rfl
exact w₁, fun j hji => by simpa using w₂ j hji
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[simp] theorem findFinIdx?_subtype {p : α Prop} {l : List { x // p x }}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
l.findFinIdx? f = (l.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
unfold unattach
induction l with
| nil => simp
| cons a l ih =>
simp [hf, findFinIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### idxOf
The verification API for `idxOf` is still incomplete.
@@ -1035,6 +1059,36 @@ theorem idxOf_lt_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∈ l) : l.
@[deprecated idxOf_lt_length (since := "2025-01-29")]
abbrev indexOf_lt_length := @idxOf_lt_length
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findFinIdx?` (and proved using them).
-/
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
@[simp] theorem finIdxOf?_nil [BEq α] : ([] : List α).finIdxOf? a = none := rfl
@[simp] theorem finIdxOf?_cons [BEq α] (a : α) (xs : List α) :
(a :: xs).finIdxOf? b =
if a == b then some 0, by simp else (xs.finIdxOf? b).map (·.succ) := by
simp [finIdxOf?]
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.finIdxOf? a = none a l := by
simp only [finIdxOf?, findFinIdx?_eq_none_iff, beq_iff_eq]
constructor
· intro w m
exact w a m rfl
· rintro h a m rfl
exact h m
@[simp] theorem finIdxOf?_eq_some_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} {i : Fin l.length} :
l.finIdxOf? a = some i l[i] = a j (_ : j < i), ¬l[j] = a := by
simp only [finIdxOf?, findFinIdx?_eq_some_iff, beq_iff_eq]
/-! ### idxOf?
The verification API for `idxOf?` is still incomplete.
@@ -1060,12 +1114,6 @@ theorem idxOf?_cons [BEq α] (a : α) (xs : List α) (b : α) :
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
/-! ### finIdxOf? -/
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
/-! ### lookup -/
section lookup

View File

@@ -3086,8 +3086,12 @@ variable [BEq α]
@[simp] theorem replace_cons_self [LawfulBEq α] {a : α} : (a::as).replace a b = b::as := by
simp [replace_cons]
@[simp] theorem replace_of_not_mem {l : List α} (h : !l.elem a) : l.replace a b = l := by
induction l <;> simp_all [replace_cons]
@[simp] theorem replace_of_not_mem [LawfulBEq α] {l : List α} (h : a l) : l.replace a b = l := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [replace_cons]
split <;> simp_all
@[simp] theorem length_replace {l : List α} : (l.replace a b).length = l.length := by
induction l with
@@ -3170,7 +3174,7 @@ theorem replace_take {l : List α} {i : Nat} :
(replicate n a).replace a b = b :: replicate (n - 1) a := by
cases n <;> simp_all [replicate_succ, replace_cons]
@[simp] theorem replace_replicate_ne {a b c : α} (h : !b == a) :
@[simp] theorem replace_replicate_ne [LawfulBEq α] {a b c : α} (h : !b == a) :
(replicate n a).replace b c = replicate n a := by
rw [replace_of_not_mem]
simp_all

View File

@@ -658,6 +658,40 @@ private theorem insertIdx_loop_toArray (i : Nat) (l : List α) (j : Nat) (hj : j
· simp only [size_toArray, Nat.not_le] at h'
rw [List.insertIdx_of_length_lt (h := h')]
@[simp]
theorem replace_toArray [BEq α] [LawfulBEq α] (l : List α) (a b : α) :
l.toArray.replace a b = (l.replace a b).toArray := by
rw [Array.replace]
split <;> rename_i i h
· simp only [finIdxOf?_toArray, finIdxOf?_eq_none_iff] at h
rw [replace_of_not_mem]
simpa
· simp_all only [finIdxOf?_toArray, finIdxOf?_eq_some_iff, Fin.getElem_fin, set_toArray,
mk.injEq]
apply List.ext_getElem
· simp
· intro j h₁ h₂
rw [List.getElem_replace, List.getElem_set]
by_cases h₃ : j < i
· rw [if_neg (by omega), if_neg]
simp only [length_set] at h₁ h₃
simpa using h.2 j, by omega h₃
· by_cases h₃ : j = i
· rw [if_pos (by omega), if_pos, if_neg]
· simp only [mem_take_iff_getElem, not_exists]
intro k hk
simpa using h.2 k, by omega (by show k < i.1; omega)
· subst h₃
simpa using h.1
· rw [if_neg (by omega)]
split
· rw [if_pos]
· simp_all
· simp only [mem_take_iff_getElem]
simp only [length_set] at h₁
exact i, by omega, h.1
· rfl
@[simp] theorem leftpad_toArray (n : Nat) (a : α) (l : List α) :
Array.leftpad n a l.toArray = (leftpad n a l).toArray := by
simp [leftpad, Array.leftpad, toArray_replicate]

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

@@ -101,6 +101,12 @@ This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
| 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
| some x, some y => s x y
@@ -217,18 +223,24 @@ 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.
Note 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`.
-/
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. -/
@@ -251,6 +263,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

View File

@@ -654,6 +654,11 @@ theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (o H)
Option.map g (pmap f o H) = pmap (fun a h => g (f a h)) o H := by
cases o <;> simp
theorem pmap_map (o : Option α) (f : α β) {p : β Prop} (g : b, p b γ) (H) :
pmap g (o.map f) H =
pmap (fun a h => g (f a) h) o (fun a m => H (f a) (mem_map_of_mem f m)) := by
cases o <;> simp
/-! ### pelim -/
@[simp] theorem pelim_none : pelim none b f = b := rfl
@@ -668,4 +673,80 @@ theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (o H)
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

@@ -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`. -/
@@ -213,6 +216,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 +256,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`. -/
@@ -353,6 +359,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]
@@ -394,8 +403,8 @@ instance : ReprAtom Int16 := ⟨⟩
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`. -/
@@ -497,6 +506,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 +554,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`. -/
@@ -645,6 +657,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 +715,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`. -/

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]

View File

@@ -5,9 +5,94 @@ Authors: Markus Himmel
-/
prelude
import Init.Data.SInt.Basic
import Init.Data.BitVec.Lemmas
open Lean in
set_option hygiene false in
macro "declare_int_theorems" typeName:ident _bits:term:arg : command => do
let mut cmds Syntax.getArgs <$> `(
namespace $typeName
@[int_toBitVec] theorem le_def {a b : $typeName} : a b a.toBitVec.sle b.toBitVec := Iff.rfl
@[int_toBitVec] theorem lt_def {a b : $typeName} : a < b a.toBitVec.slt b.toBitVec := Iff.rfl
theorem toBitVec_inj {a b : $typeName} : a.toBitVec = b.toBitVec a = b :=
toBitVec.inj, (· rfl)
@[int_toBitVec] theorem eq_iff_toBitVec_eq {a b : $typeName} : a = b a.toBitVec = b.toBitVec :=
toBitVec_inj.symm
@[int_toBitVec] theorem ne_iff_toBitVec_ne {a b : $typeName} : a b a.toBitVec b.toBitVec :=
Decidable.not_iff_not.2 eq_iff_toBitVec_eq
@[simp] theorem toBitVec_ofNat {n : Nat} : toBitVec (ofNat n) = BitVec.ofNat _ n := rfl
@[simp, int_toBitVec] theorem toBitVec_ofNatOfNat {n : Nat} : toBitVec (no_index (OfNat.ofNat n)) = OfNat.ofNat n := rfl
end $typeName
)
return mkNullNode cmds
declare_int_theorems Int8 8
declare_int_theorems Int16 16
declare_int_theorems Int32 32
declare_int_theorems Int64 64
declare_int_theorems ISize System.Platform.numBits
@[simp] theorem UInt8.toBitVec_toInt8 (x : UInt8) : x.toInt8.toBitVec = x.toBitVec := rfl
@[simp] theorem UInt16.toBitVec_toInt16 (x : UInt16) : x.toInt16.toBitVec = x.toBitVec := rfl
@[simp] theorem UInt32.toBitVec_toInt32 (x : UInt32) : x.toInt32.toBitVec = x.toBitVec := rfl
@[simp] theorem UInt64.toBitVec_toInt64 (x : UInt64) : x.toInt64.toBitVec = x.toBitVec := rfl
@[simp] theorem USize.toBitVec_toISize (x : USize) : x.toISize.toBitVec = x.toBitVec := rfl
@[simp] theorem Int8.ofBitVec_uInt8ToBitVec (x : UInt8) : Int8.ofBitVec x.toBitVec = x.toInt8 := rfl
@[simp] theorem Int16.ofBitVec_uInt16ToBitVec (x : UInt16) : Int16.ofBitVec x.toBitVec = x.toInt16 := rfl
@[simp] theorem Int32.ofBitVec_uInt32ToBitVec (x : UInt32) : Int32.ofBitVec x.toBitVec = x.toInt32 := rfl
@[simp] theorem Int64.ofBitVec_uInt64ToBitVec (x : UInt64) : Int64.ofBitVec x.toBitVec = x.toInt64 := rfl
@[simp] theorem ISize.ofBitVec_uSize8ToBitVec (x : USize) : ISize.ofBitVec x.toBitVec = x.toISize := rfl
@[simp] theorem UInt8.toUInt8_toInt8 (x : UInt8) : x.toInt8.toUInt8 = x := rfl
@[simp] theorem UInt16.toUInt16_toInt16 (x : UInt16) : x.toInt16.toUInt16 = x := rfl
@[simp] theorem UInt32.toUInt32_toInt32 (x : UInt32) : x.toInt32.toUInt32 = x := rfl
@[simp] theorem UInt64.toUInt64_toInt64 (x : UInt64) : x.toInt64.toUInt64 = x := rfl
@[simp] theorem USize.toUSize_toISize (x : USize) : x.toISize.toUSize = x := rfl
@[simp] theorem ISize.toBitVec_neg (x : ISize) : (-x).toBitVec = -x.toBitVec := rfl
@[simp] theorem ISize.toBitVec_zero : (0 : ISize).toBitVec = 0 := rfl
@[simp] theorem ISize.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := rfl
@[simp] theorem Int8.neg_zero : -(0 : Int8) = 0 := rfl
@[simp] theorem Int16.neg_zero : -(0 : Int16) = 0 := rfl
@[simp] theorem Int32.neg_zero : -(0 : Int32) = 0 := rfl
@[simp] theorem Int64.neg_zero : -(0 : Int64) = 0 := rfl
@[simp] theorem ISize.neg_zero : -(0 : ISize) = 0 := ISize.toBitVec.inj (by simp)
theorem ISize.toNat_toBitVec_ofNat_of_lt {n : Nat} (h : n < 2^32) :
(ofNat n).toBitVec.toNat = n :=
Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le h (by cases USize.size_eq <;> simp_all +decide))
theorem ISize.toInt_ofInt {n : Int} (hn : -2^31 n) (hn' : n < 2^31) : toInt (ofInt n) = n := by
rw [toInt, toBitVec_ofInt, BitVec.toInt_ofInt_eq_self] <;> cases System.Platform.numBits_eq
<;> (simp_all; try omega)
theorem ISize.toNatClampNeg_ofInt_eq_zero {n : Int} (hn : -2^31 n) (hn' : n 0) :
toNatClampNeg (ofInt n) = 0 := by
rwa [toNatClampNeg, toInt_ofInt hn (by omega), Int.toNat_eq_zero]
theorem ISize.neg_ofInt {n : Int} : -ofInt n = ofInt (-n) :=
toBitVec.inj (by simp [BitVec.ofInt_neg])
theorem ISize.ofInt_eq_ofNat {n : Nat} : ofInt n = ofNat n :=
toBitVec.inj (by simp)
theorem ISize.neg_ofNat {n : Nat} : -ofNat n = ofInt (-n) := by
rw [ neg_ofInt, ofInt_eq_ofNat]
theorem ISize.toNatClampNeg_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) :
toNatClampNeg (ofNat n) = n := by
rw [toNatClampNeg, ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega), Int.toNat_ofNat]
theorem ISize.toNatClampNeg_neg_ofNat_of_le {n : Nat} (h : n 2 ^ 31) :
toNatClampNeg (-ofNat n) = 0 := by
rw [neg_ofNat, toNatClampNeg_ofInt_eq_zero (by omega) (by omega)]
theorem ISize.toInt_ofNat_of_lt {n : Nat} (h : n < 2 ^ 31) : toInt (ofNat n) = n := by
rw [ ofInt_eq_ofNat, toInt_ofInt (by omega) (by omega)]
theorem ISize.toInt_neg_ofNat_of_le {n : Nat} (h : n 2 ^ 31) : toInt (-ofNat n) = -n := by
rw [ ofInt_eq_ofNat, neg_ofInt, toInt_ofInt (by omega) (by omega)]

View File

@@ -287,6 +287,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)
@@ -295,6 +297,51 @@ theorem UInt16.toNat_lt_usizeSize (n : UInt16) : n.toNat < USize.size :=
theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
Nat.lt_of_lt_of_le n.toNat_lt (by cases USize.size_eq <;> simp_all)
theorem UInt8.size_dvd_usizeSize : UInt8.size USize.size := by cases USize.size_eq <;> simp_all +decide
theorem UInt16.size_dvd_usizeSize : UInt16.size USize.size := by cases USize.size_eq <;> simp_all +decide
theorem UInt32.size_dvd_usizeSize : UInt32.size USize.size := by cases USize.size_eq <;> simp_all +decide
theorem USize.size_dvd_uInt64Size : USize.size UInt64.size := by cases USize.size_eq <;> simp_all +decide
@[simp] theorem mod_usizeSize_uInt8Size (n : Nat) : n % USize.size % UInt8.size = n % UInt8.size :=
Nat.mod_mod_of_dvd _ UInt8.size_dvd_usizeSize
@[simp] theorem mod_usizeSize_uInt16Size (n : Nat) : n % USize.size % UInt16.size = n % UInt16.size :=
Nat.mod_mod_of_dvd _ UInt16.size_dvd_usizeSize
@[simp] theorem mod_usizeSize_uInt32Size (n : Nat) : n % USize.size % UInt32.size = n % UInt32.size :=
Nat.mod_mod_of_dvd _ UInt32.size_dvd_usizeSize
@[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 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))
@[simp] theorem UInt8.toNat_mod_uInt64Size (n : UInt8) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@[simp] theorem UInt8.toNat_mod_uSizeSize (n : UInt8) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
@[simp] theorem UInt16.toNat_mod_size (n : UInt16) : n.toNat % UInt16.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
@[simp] theorem UInt16.toNat_mod_uInt32Size (n : UInt16) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@[simp] theorem UInt16.toNat_mod_uInt64Size (n : UInt16) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@[simp] theorem UInt16.toNat_mod_uSizeSize (n : UInt16) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
@[simp] theorem UInt32.toNat_mod_size (n : UInt32) : n.toNat % UInt32.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
@[simp] theorem UInt32.toNat_mod_uInt64Size (n : UInt32) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt (Nat.lt_trans n.toNat_lt (by decide))
@[simp] theorem UInt32.toNat_mod_uSizeSize (n : UInt32) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_usizeSize
@[simp] theorem UInt64.toNat_mod_size (n : UInt64) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
@[simp] theorem USize.toNat_mod_size (n : USize) : n.toNat % USize.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt_size
@[simp] theorem USize.toNat_mod_uInt64Size (n : USize) : n.toNat % UInt64.size = n.toNat := Nat.mod_eq_of_lt n.toNat_lt
@[simp] theorem UInt8.toUInt16_mod_256 (n : UInt8) : n.toUInt16 % 256 = n.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt32_mod_256 (n : UInt8) : n.toUInt32 % 256 = n.toUInt32 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt64_mod_256 (n : UInt8) : n.toUInt64 % 256 = n.toUInt64 := UInt64.toNat.inj (by simp)
@[simp] theorem UInt8.toUSize_mod_256 (n : UInt8) : n.toUSize % 256 = n.toUSize := USize.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt32_mod_65536 (n : UInt16) : n.toUInt32 % 65536 = n.toUInt32 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt64_mod_65536 (n : UInt16) : n.toUInt64 % 65536 = n.toUInt64 := UInt64.toNat.inj (by simp)
@[simp] theorem UInt16.toUSize_mod_65536 (n : UInt16) : n.toUSize % 65536 = n.toUSize := USize.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt64_mod_4294967296 (n : UInt32) : n.toUInt64 % 4294967296 = n.toUInt64 := UInt64.toNat.inj (by simp)
@[simp] theorem Fin.mk_uInt8ToNat (n : UInt8) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
@[simp] theorem Fin.mk_uInt16ToNat (n : UInt16) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
@[simp] theorem Fin.mk_uInt32ToNat (n : UInt32) : Fin.mk n.toNat n.toFin.isLt = n.toFin := rfl
@@ -328,7 +375,7 @@ theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
@[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
@@ -349,14 +396,14 @@ theorem UInt32.toNat_lt_usizeSize (n : UInt32) : n.toNat < USize.size :=
@[simp] theorem UInt16.toBitVec_toUInt64 (n : UInt16) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := rfl
@[simp] theorem UInt32.toBitVec_toUInt64 (n : UInt32) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := rfl
@[simp] theorem USize.toBitVec_toUInt64 (n : USize) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 :=
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt (USize.toNat_lt _)])
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt8.toBitVec_toUSize (n : UInt8) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt16.toBitVec_toUSize (n : UInt16) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt32.toBitVec_toUSize (n : UInt32) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt n.toNat_lt_usizeSize])
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt64.toBitVec_toUSize (n : UInt64) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp)
@@ -420,3 +467,321 @@ theorem USize.ofNatLT_uInt64ToNat (n : UInt64) (h) : USize.ofNatLT n.toNat h = n
@[simp] theorem USize.ofFin_uint8ToFin (n : UInt8) : USize.ofFin (n.toFin.castLE UInt8.size_le_usizeSize) = n.toUSize := rfl
@[simp] theorem USize.ofFin_uint16ToFin (n : UInt16) : USize.ofFin (n.toFin.castLE UInt16.size_le_usizeSize) = n.toUSize := rfl
@[simp] theorem USize.ofFin_uint32ToFin (n : UInt32) : USize.ofFin (n.toFin.castLE UInt32.size_le_usizeSize) = n.toUSize := rfl
@[simp] theorem Nat.toUInt8_eq {n : Nat} : n.toUInt8 = UInt8.ofNat n := rfl
@[simp] theorem Nat.toUInt16_eq {n : Nat} : n.toUInt16 = UInt16.ofNat n := rfl
@[simp] theorem Nat.toUInt32_eq {n : Nat} : n.toUInt32 = UInt32.ofNat n := rfl
@[simp] theorem Nat.toUInt64_eq {n : Nat} : n.toUInt64 = UInt64.ofNat n := rfl
@[simp] theorem Nat.toUSize_eq {n : Nat} : n.toUSize = USize.ofNat n := rfl
@[simp] theorem UInt8.ofBitVec_uInt16ToBitVec (n : UInt16) :
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
@[simp] theorem UInt8.ofBitVec_uInt32ToBitVec (n : UInt32) :
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
@[simp] theorem UInt8.ofBitVec_uInt64ToBitVec (n : UInt64) :
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := rfl
@[simp] theorem UInt8.ofBitVec_uSizeToBitVec (n : USize) :
UInt8.ofBitVec (n.toBitVec.setWidth 8) = n.toUInt8 := UInt8.toNat.inj (by simp)
@[simp] theorem UInt16.ofBitVec_uInt8ToBitVec (n : UInt8) :
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
@[simp] theorem UInt16.ofBitVec_uInt32ToBitVec (n : UInt32) :
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
@[simp] theorem UInt16.ofBitVec_uInt64ToBitVec (n : UInt64) :
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := rfl
@[simp] theorem UInt16.ofBitVec_uSizeToBitVec (n : USize) :
UInt16.ofBitVec (n.toBitVec.setWidth 16) = n.toUInt16 := UInt16.toNat.inj (by simp)
@[simp] theorem UInt32.ofBitVec_uInt8ToBitVec (n : UInt8) :
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
@[simp] theorem UInt32.ofBitVec_uInt16ToBitVec (n : UInt16) :
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
@[simp] theorem UInt32.ofBitVec_uInt64ToBitVec (n : UInt64) :
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := rfl
@[simp] theorem UInt32.ofBitVec_uSizeToBitVec (n : USize) :
UInt32.ofBitVec (n.toBitVec.setWidth 32) = n.toUInt32 := UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.ofBitVec_uInt8ToBitVec (n : UInt8) :
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
@[simp] theorem UInt64.ofBitVec_uInt16ToBitVec (n : UInt16) :
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
@[simp] theorem UInt64.ofBitVec_uInt32ToBitVec (n : UInt32) :
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 := rfl
@[simp] theorem UInt64.ofBitVec_uSizeToBitVec (n : USize) :
UInt64.ofBitVec (n.toBitVec.setWidth 64) = n.toUInt64 :=
UInt64.toNat.inj (by simp)
@[simp] theorem USize.ofBitVec_uInt8ToBitVec (n : UInt8) :
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem USize.ofBitVec_uInt16ToBitVec (n : UInt16) :
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem USize.ofBitVec_uInt32ToBitVec (n : UInt32) :
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem USize.ofBitVec_uInt64ToBitVec (n : UInt64) :
USize.ofBitVec (n.toBitVec.setWidth System.Platform.numBits) = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem UInt8.ofNat_uInt16ToNat (n : UInt16) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
@[simp] theorem UInt8.ofNat_uInt32ToNat (n : UInt32) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
@[simp] theorem UInt8.ofNat_uInt64ToNat (n : UInt64) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
@[simp] theorem UInt8.ofNat_uSizeToNat (n : USize) : UInt8.ofNat n.toNat = n.toUInt8 := rfl
@[simp] theorem UInt16.ofNat_uInt8ToNat (n : UInt8) : UInt16.ofNat n.toNat = n.toUInt16 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt16.ofNat_uInt32ToNat (n : UInt32) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
@[simp] theorem UInt16.ofNat_uInt64ToNat (n : UInt64) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
@[simp] theorem UInt16.ofNat_uSizeToNat (n : USize) : UInt16.ofNat n.toNat = n.toUInt16 := rfl
@[simp] theorem UInt32.ofNat_uInt8ToNat (n : UInt8) : UInt32.ofNat n.toNat = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt32.ofNat_uInt16ToNat (n : UInt16) : UInt32.ofNat n.toNat = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt32.ofNat_uInt64ToNat (n : UInt64) : UInt32.ofNat n.toNat = n.toUInt32 := rfl
@[simp] theorem UInt32.ofNat_uSizeToNat (n : USize) : UInt32.ofNat n.toNat = n.toUInt32 := rfl
@[simp] theorem UInt64.ofNat_uInt8ToNat (n : UInt8) : UInt64.ofNat n.toNat = n.toUInt64 :=
UInt64.toNat.inj (by simp)
@[simp] theorem UInt64.ofNat_uInt16ToNat (n : UInt16) : UInt64.ofNat n.toNat = n.toUInt64 :=
UInt64.toNat.inj (by simp)
@[simp] theorem UInt64.ofNat_uInt32ToNat (n : UInt32) : UInt64.ofNat n.toNat = n.toUInt64 :=
UInt64.toNat.inj (by simp)
@[simp] theorem UInt64.ofNat_uSizeToNat (n : USize) : UInt64.ofNat n.toNat = n.toUInt64 :=
UInt64.toNat.inj (by simp)
@[simp] theorem USize.ofNat_uInt8ToNat (n : UInt8) : USize.ofNat n.toNat = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem USize.ofNat_uInt16ToNat (n : UInt16) : USize.ofNat n.toNat = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem USize.ofNat_uInt32ToNat (n : UInt32) : USize.ofNat n.toNat = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem USize.ofNat_uInt64ToNat (n : UInt64) : USize.ofNat n.toNat = n.toUSize :=
USize.toNat.inj (by simp)
theorem UInt8.ofNatLT_eq_ofNat (n : Nat) {h} : UInt8.ofNatLT n h = UInt8.ofNat n :=
UInt8.toNat.inj (by simp [Nat.mod_eq_of_lt h])
theorem UInt16.ofNatLT_eq_ofNat (n : Nat) {h} : UInt16.ofNatLT n h = UInt16.ofNat n :=
UInt16.toNat.inj (by simp [Nat.mod_eq_of_lt h])
theorem UInt32.ofNatLT_eq_ofNat (n : Nat) {h} : UInt32.ofNatLT n h = UInt32.ofNat n :=
UInt32.toNat.inj (by simp [Nat.mod_eq_of_lt h])
theorem UInt64.ofNatLT_eq_ofNat (n : Nat) {h} : UInt64.ofNatLT n h = UInt64.ofNat n :=
UInt64.toNat.inj (by simp [Nat.mod_eq_of_lt h])
theorem USize.ofNatLT_eq_ofNat (n : Nat) {h} : USize.ofNatLT n h = USize.ofNat n :=
USize.toNat.inj (by simp [Nat.mod_eq_of_lt h])
theorem UInt8.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt8.size) :
UInt8.ofNatTruncate n = UInt8.ofNat n := by
simp [ofNatTruncate, hn, UInt8.ofNatLT_eq_ofNat]
theorem UInt16.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt16.size) :
UInt16.ofNatTruncate n = UInt16.ofNat n := by
simp [ofNatTruncate, hn, UInt16.ofNatLT_eq_ofNat]
theorem UInt32.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt32.size) :
UInt32.ofNatTruncate n = UInt32.ofNat n := by
simp [ofNatTruncate, hn, UInt32.ofNatLT_eq_ofNat]
theorem UInt64.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < UInt64.size) :
UInt64.ofNatTruncate n = UInt64.ofNat n := by
simp [ofNatTruncate, hn, UInt64.ofNatLT_eq_ofNat]
theorem USize.ofNatTruncate_eq_ofNat (n : Nat) (hn : n < USize.size) :
USize.ofNatTruncate n = USize.ofNat n := by
simp [ofNatTruncate, hn, USize.ofNatLT_eq_ofNat]
@[simp] theorem UInt8.ofNatTruncate_toNat (n : UInt8) : UInt8.ofNatTruncate n.toNat = n := by
rw [UInt8.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
@[simp] theorem UInt16.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt16.ofNatTruncate n.toNat = n.toUInt16 := by
rw [UInt16.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
exact Nat.lt_trans (n.toNat_lt) (by decide)
@[simp] theorem UInt16.ofNatTruncate_toNat (n : UInt16) : UInt16.ofNatTruncate n.toNat = n := by
rw [UInt16.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
@[simp] theorem UInt32.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt32.ofNatTruncate n.toNat = n.toUInt32 := by
rw [UInt32.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
exact Nat.lt_trans (n.toNat_lt) (by decide)
@[simp] theorem UInt32.ofNatTruncate_uInt16ToNat (n : UInt16) : UInt32.ofNatTruncate n.toNat = n.toUInt32 := by
rw [UInt32.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
exact Nat.lt_trans (n.toNat_lt) (by decide)
@[simp] theorem UInt32.ofNatTruncate_toNat (n : UInt32) : UInt32.ofNatTruncate n.toNat = n := by
rw [UInt32.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
@[simp] theorem UInt64.ofNatTruncate_uInt8ToNat (n : UInt8) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
exact Nat.lt_trans (n.toNat_lt) (by decide)
@[simp] theorem UInt64.ofNatTruncate_uInt16ToNat (n : UInt16) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
exact Nat.lt_trans (n.toNat_lt) (by decide)
@[simp] theorem UInt64.ofNatTruncate_uInt32ToNat (n : UInt32) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uInt32ToNat]
exact Nat.lt_trans (n.toNat_lt) (by decide)
@[simp] theorem UInt64.ofNatTruncate_toNat (n : UInt64) : UInt64.ofNatTruncate n.toNat = n := by
rw [UInt64.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt]
@[simp] theorem UInt64.ofNatTruncate_uSizeToNat (n : USize) : UInt64.ofNatTruncate n.toNat = n.toUInt64 := by
rw [UInt64.ofNatTruncate_eq_ofNat, ofNat_uSizeToNat]
exact n.toNat_lt
@[simp] theorem USize.ofNatTruncate_uInt8ToNat (n : UInt8) : USize.ofNatTruncate n.toNat = n.toUSize := by
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt8ToNat]
exact n.toNat_lt_usizeSize
@[simp] theorem USize.ofNatTruncate_uInt16ToNat (n : UInt16) : USize.ofNatTruncate n.toNat = n.toUSize := by
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt16ToNat]
exact n.toNat_lt_usizeSize
@[simp] theorem USize.ofNatTruncate_uInt32ToNat (n : UInt32) : USize.ofNatTruncate n.toNat = n.toUSize := by
rw [USize.ofNatTruncate_eq_ofNat, ofNat_uInt32ToNat]
exact n.toNat_lt_usizeSize
@[simp] theorem USize.ofNatTruncate_toNat (n : USize) : USize.ofNatTruncate n.toNat = n := by
rw [USize.ofNatTruncate_eq_ofNat] <;> simp [n.toNat_lt_size]
@[simp] theorem UInt8.toUInt8_toUInt16 (n : UInt8) : n.toUInt16.toUInt8 = n :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt8_toUInt32 (n : UInt8) : n.toUInt32.toUInt8 = n :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt8_toUInt64 (n : UInt8) : n.toUInt64.toUInt8 = n :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt8_toUSize (n : UInt8) : n.toUSize.toUInt8 = n :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt16_toUInt32 (n : UInt8) : n.toUInt32.toUInt16 = n.toUInt16 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt16_toUInt64 (n : UInt8) : n.toUInt64.toUInt16 = n.toUInt16 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt16_toUSize (n : UInt8) : n.toUSize.toUInt16 = n.toUInt16 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt32_toUInt16 (n : UInt8) : n.toUInt16.toUInt32 = n.toUInt32 := rfl
@[simp] theorem UInt8.toUInt32_toUInt64 (n : UInt8) : n.toUInt64.toUInt32 = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt32_toUSize (n : UInt8) : n.toUSize.toUInt32 = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt8.toUInt64_toUInt16 (n : UInt8) : n.toUInt16.toUInt64 = n.toUInt64 := rfl
@[simp] theorem UInt8.toUInt64_toUInt32 (n : UInt8) : n.toUInt32.toUInt64 = n.toUInt64 := rfl
@[simp] theorem UInt8.toUInt64_toUSize (n : UInt8) : n.toUSize.toUInt64 = n.toUInt64 := rfl
@[simp] theorem UInt8.toUSize_toUInt16 (n : UInt8) : n.toUInt16.toUSize = n.toUSize := rfl
@[simp] theorem UInt8.toUSize_toUInt32 (n : UInt8) : n.toUInt32.toUSize = n.toUSize := rfl
@[simp] theorem UInt8.toUSize_toUInt64 (n : UInt8) : n.toUInt64.toUSize = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt8_toUInt32 (n : UInt16) : n.toUInt32.toUInt8 = n.toUInt8 := rfl
@[simp] theorem UInt16.toUInt8_toUInt64 (n : UInt16) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
@[simp] theorem UInt16.toUInt8_toUSize (n : UInt16) : n.toUSize.toUInt8 = n.toUInt8 := rfl
@[simp] theorem UInt16.toUInt16_toUInt8 (n : UInt16) : n.toUInt8.toUInt16 = n % 256 := rfl
@[simp] theorem UInt16.toUInt16_toUInt32 (n : UInt16) : n.toUInt32.toUInt16 = n :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt16_toUInt64 (n : UInt16) : n.toUInt64.toUInt16 = n :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt16_toUSize (n : UInt16) : n.toUSize.toUInt16 = n :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt32_toUInt8 (n : UInt16) : n.toUInt8.toUInt32 = n.toUInt32 % 256 := rfl
@[simp] theorem UInt16.toUInt32_toUInt64 (n : UInt16) : n.toUInt64.toUInt32 = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt32_toUSize (n : UInt16) : n.toUSize.toUInt32 = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt16.toUInt64_toUInt8 (n : UInt16) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
@[simp] theorem UInt16.toUInt64_toUInt32 (n : UInt16) : n.toUInt32.toUInt64 = n.toUInt64 := rfl
@[simp] theorem UInt16.toUInt64_toUSize (n : UInt16) : n.toUSize.toUInt64 = n.toUInt64 := rfl
@[simp] theorem UInt16.toUSize_toUInt8 (n : UInt16) : n.toUInt8.toUSize = n.toUSize % 256 :=
USize.toNat.inj (by simp)
@[simp] theorem UInt16.toUSize_toUInt32 (n : UInt16) : n.toUInt32.toUSize = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem UInt16.toUSize_toUInt64 (n : UInt16) : n.toUInt64.toUSize = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt8_toUInt16 (n : UInt32) : n.toUInt16.toUInt8 = n.toUInt8 :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt8_toUInt64 (n : UInt32) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
@[simp] theorem UInt32.toUInt8_toUSize (n : UInt32) : n.toUSize.toUInt8 = n.toUInt8 := rfl
@[simp] theorem UInt32.toUInt16_toUInt8 (n : UInt32) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt16_toUInt64 (n : UInt32) : n.toUInt64.toUInt16 = n.toUInt16 := rfl
@[simp] theorem UInt32.toUInt16_toUSize (n : UInt32) : n.toUSize.toUInt16 = n.toUInt16 := rfl
@[simp] theorem UInt32.toUInt32_toUInt8 (n : UInt32) : n.toUInt8.toUInt32 = n % 256 := rfl
@[simp] theorem UInt32.toUInt32_toUInt16 (n : UInt32) : n.toUInt16.toUInt32 = n % 65536 := rfl
@[simp] theorem UInt32.toUInt32_toUInt64 (n : UInt32) : n.toUInt64.toUInt32 = n :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt32_toUSize (n : UInt32) : n.toUSize.toUInt32 = n :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt32.toUInt64_toUInt8 (n : UInt32) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
@[simp] theorem UInt32.toUInt64_toUInt16 (n : UInt32) : n.toUInt16.toUInt64 = n.toUInt64 % 65536 := rfl
@[simp] theorem UInt32.toUInt64_toUSize (n : UInt32) : n.toUSize.toUInt64 = n.toUInt64 := rfl
@[simp] theorem UInt32.toUSize_toUInt8 (n : UInt32) : n.toUInt8.toUSize = n.toUSize % 256 :=
USize.toNat.inj (by simp)
@[simp] theorem UInt32.toUSize_toUInt16 (n : UInt32) : n.toUInt16.toUSize = n.toUSize % 65536 :=
USize.toNat.inj (by simp)
@[simp] theorem UInt32.toUSize_toUInt64 (n : UInt32) : n.toUInt64.toUSize = n.toUSize :=
USize.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt8_toUInt16 (n : UInt64) : n.toUInt16.toUInt8 = n.toUInt8 :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt8_toUInt32 (n : UInt64) : n.toUInt32.toUInt8 = n.toUInt8 :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt8_toUSize (n : UInt64) : n.toUSize.toUInt8 = n.toUInt8 :=
UInt8.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt16_toUInt8 (n : UInt64) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt16_toUInt32 (n : UInt64) : n.toUInt32.toUInt16 = n.toUInt16 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt16_toUSize (n : UInt64) : n.toUSize.toUInt16 = n.toUInt16 :=
UInt16.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_toUInt8 (n : UInt64) : n.toUInt8.toUInt32 = n.toUInt32 % 256 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_toUInt16 (n : UInt64) : n.toUInt16.toUInt32 = n.toUInt32 % 65536 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt32_toUSize (n : UInt64) : n.toUSize.toUInt32 = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem UInt64.toUInt64_toUInt8 (n : UInt64) : n.toUInt8.toUInt64 = n % 256 := rfl
@[simp] theorem UInt64.toUInt64_toUInt16 (n : UInt64) : n.toUInt16.toUInt64 = n % 65536 := rfl
@[simp] theorem UInt64.toUInt64_toUInt32 (n : UInt64) : n.toUInt32.toUInt64 = n % 4294967296 := rfl
@[simp] theorem UInt64.toUSize_toUInt8 (n : UInt64) : n.toUInt8.toUSize = n.toUSize % 256 :=
USize.toNat.inj (by simp)
@[simp] theorem UInt64.toUSize_toUInt16 (n : UInt64) : n.toUInt16.toUSize = n.toUSize % 65536 :=
USize.toNat.inj (by simp)
@[simp] theorem USize.toUInt8_toUInt16 (n : USize) : n.toUInt16.toUInt8 = n.toUInt8 :=
UInt8.toNat.inj (by simp)
@[simp] theorem USize.toUInt8_toUInt32 (n : USize) : n.toUInt32.toUInt8 = n.toUInt8 :=
UInt8.toNat.inj (by simp)
@[simp] theorem USize.toUInt8_toUInt64 (n : USize) : n.toUInt64.toUInt8 = n.toUInt8 := rfl
@[simp] theorem USize.toUInt16_toUInt8 (n : USize) : n.toUInt8.toUInt16 = n.toUInt16 % 256 :=
UInt16.toNat.inj (by simp)
@[simp] theorem USize.toUInt16_toUInt32 (n : USize) : n.toUInt32.toUInt16 = n.toUInt16 :=
UInt16.toNat.inj (by simp)
@[simp] theorem USize.toUInt16_toUInt64 (n : USize) : n.toUInt64.toUInt16 = n.toUInt16 := rfl
@[simp] theorem USize.toUInt64_toUInt8 (n : USize) : n.toUInt8.toUInt64 = n.toUInt64 % 256 := rfl
@[simp] theorem USize.toUInt64_toUInt16 (n : USize) : n.toUInt16.toUInt64 = n.toUInt64 % 65536 := rfl
@[simp] theorem USize.toUInt32_toUInt8 (n : USize) : n.toUInt8.toUInt32 = n.toUInt32 % 256 :=
UInt32.toNat.inj (by simp)
@[simp] theorem USize.toUInt32_toUInt16 (n : USize) : n.toUInt16.toUInt32 = n.toUInt32 % 65536 :=
UInt32.toNat.inj (by simp)
@[simp] theorem USize.toUInt32_toUInt64 (n : USize) : n.toUInt64.toUInt32 = n.toUInt32 :=
UInt32.toNat.inj (by simp)
@[simp] theorem USize.toUSize_toUInt8 (n : USize) : n.toUInt8.toUSize = n % 256 :=
USize.toNat.inj (by simp)
@[simp] theorem USize.toUSize_toUInt16 (n : USize) : n.toUInt16.toUSize = n % 65536 :=
USize.toNat.inj (by simp)
@[simp] theorem USize.toUSize_toUInt64 (n : USize) : n.toUInt64.toUSize = n :=
USize.toNat.inj (by simp)
-- Note: we are currently missing the following four results for which there does not seem to
-- be a good candidate for the RHS:
-- @[simp] theorem UInt64.toUInt64_toUSize (n : UInt64) : n.toUSize.toUInt64 = ? :=
-- @[simp] theorem UInt64.toUSize_toUInt32 (n : UInt64) : n.toUInt32.toUSize = ? :=
-- @[simp] theorem USize.toUInt64_toUInt32 (n : USize) : n.toUInt32.toUInt64 = ? :=
-- @[simp] theorem USize.toUSize_toUInt32 (n : USize) : n.toInt32.toUSize = ? :=

View File

@@ -7,8 +7,8 @@ prelude
import Init.Data.Vector.Lemmas
import Init.Data.Array.Attach
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Vector
@@ -473,6 +473,10 @@ def unattach {α : Type _} {p : α → Prop} (xs : Vector { x // p x } n) : Vect
(xs.push a).unattach = xs.unattach.push a.1 := by
simp only [unattach, Vector.map_push]
@[simp] theorem mem_unattach {p : α Prop} {xs : Vector { x // p x } n} {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 unattach_mk {p : α Prop} {xs : Array { x // p x }} {h : xs.size = n} :
(mk xs h).unattach = mk xs.unattach (by simpa using h) := by
simp [unattach]
@@ -552,6 +556,18 @@ and simplifies these to the function directly taking the value.
simp
rw [Array.find?_subtype hf]
@[simp] theorem all_subtype {p : α Prop} {xs : Vector { x // p x } n} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) :
xs.all f = xs.unattach.all g := by
rcases xs with xs, rfl
simp [hf]
@[simp] theorem any_subtype {p : α Prop} {xs : Vector { x // p x } n} {f : { x // p x } Bool} {g : α Bool}
(hf : x h, f x, h = g x) :
xs.any f = xs.unattach.any g := by
rcases xs with xs, rfl
simp [hf]
/-! ### Simp lemmas pushing `unattach` inwards. -/
@[simp] theorem unattach_reverse {p : α Prop} {xs : Vector { x // p x } n} :

View File

@@ -8,6 +8,7 @@ prelude
import Init.Data.Array.Lemmas
import Init.Data.Array.MapIdx
import Init.Data.Array.InsertIdx
import Init.Data.Array.Range
import Init.Data.Range
import Init.Data.Stream
@@ -17,8 +18,8 @@ import Init.Data.Stream
`Vector α n` is a thin wrapper around `Array α` for arrays of fixed size `n`.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
/-- `Vector α n` is an `Array α` with size `n`. -/
structure Vector (α : Type u) (n : Nat) extends Array α where
@@ -455,6 +456,9 @@ to avoid having to have the predicate live in `p : α → m (ULift Bool)`.
@[inline] def count [BEq α] (a : α) (xs : Vector α n) : Nat :=
xs.toArray.count a
@[inline] def replace [BEq α] (xs : Vector α n) (a b : α) : Vector α n :=
xs.toArray.replace a b, by simp
/--
Pad a vector on the left with a given element.

View File

@@ -15,8 +15,8 @@ import Init.Data.Array.Find
We are still missing results about `idxOf?`, `findIdx`, and `findIdx?`.
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Vector

View File

@@ -13,8 +13,8 @@ import Init.Data.Array.Find
Lemmas about `Vector α n`
-/
-- set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Array
@@ -246,6 +246,9 @@ abbrev zipWithIndex_mk := @zipIdx_mk
@[simp] theorem count_mk [BEq α] (xs : Array α) (h : xs.size = n) (a : α) :
(Vector.mk xs h).count a = xs.count a := rfl
@[simp] theorem replace_mk [BEq α] (xs : Array α) (h : xs.size = n) (a b) :
(Vector.mk xs h).replace a b = Vector.mk (xs.replace a b) (by simp [h]) := rfl
@[simp] theorem eq_mk : xs = Vector.mk as h xs.toArray = as := by
cases xs
simp
@@ -406,6 +409,9 @@ theorem toArray_mapM_go [Monad m] [LawfulMonad m] (f : α → m β) (xs : Vector
cases xs
simp
@[simp] theorem replace_toArray [BEq α] (xs : Vector α n) (a b) :
xs.toArray.replace a b = (xs.replace a b).toArray := rfl
@[simp] theorem find?_toArray (p : α Bool) (xs : Vector α n) :
xs.toArray.find? p = xs.find? p := by
cases xs
@@ -1586,9 +1592,11 @@ theorem getElem_append (xs : Vector α n) (ys : Vector α m) (i : Nat) (hi : i <
rcases ys with ys, rfl
simp [Array.getElem_append, hi]
@[simp]
theorem getElem_append_left {xs : Vector α n} {ys : Vector α m} {i : Nat} (hi : i < n) :
(xs ++ ys)[i] = xs[i] := by simp [getElem_append, hi]
@[simp]
theorem getElem_append_right {xs : Vector α n} {ys : Vector α m} {i : Nat} (h : i < n + m) (hi : n i) :
(xs ++ ys)[i] = ys[i - n] := by
rw [getElem_append, dif_neg (by omega)]
@@ -2062,6 +2070,12 @@ theorem flatMap_mkArray {β} (f : α → Vector β m) : (mkVector n a).flatMap f
rcases xs with xs, rfl
simp
theorem getElem_eq_getElem_reverse {xs : Vector α n} {i} (h : i < n) :
xs[i] = xs.reverse[n - 1 - i] := by
rw [getElem_reverse]
congr
omega
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
theorem getElem?_reverse' {xs : Vector α n} (i j) (h : i + j + 1 = n) : xs.reverse[i]? = xs[j]? := by
rcases xs with xs, rfl
@@ -2468,6 +2482,14 @@ theorem contains_iff_mem [BEq α] [LawfulBEq α] {xs : Vector α n} {a : α} :
rcases xs with xs, rfl
simp
/--
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
defeq issues in the implicit size argument.
-/
@[simp] theorem getElem_pop' (xs : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt xs.pop i h = xs[i] :=
getElem_pop h
theorem getElem?_pop (xs : Vector α n) (i : Nat) :
xs.pop[i]? = if i < n - 1 then xs[i]? else none := by
rcases xs with xs, rfl
@@ -2504,6 +2526,236 @@ theorem pop_append {xs : Vector α n} {ys : Vector α m} :
@[simp] theorem pop_mkVector (n) (a : α) : (mkVector n a).pop = mkVector (n - 1) a := by
ext <;> simp
/-! ### replace -/
section replace
variable [BEq α]
@[simp] theorem replace_cast {xs : Vector α n} {a b : α} :
(xs.cast h).replace a b = (xs.replace a b).cast (by simp [h]) := by
rcases xs with xs, rfl
simp
-- This hypothesis could probably be dropped from some of the lemmas below,
-- by proving them direct from the definition rather than going via `List`.
variable [LawfulBEq α]
@[simp] theorem replace_of_not_mem {xs : Vector α n} (h : ¬ a xs) : xs.replace a b = xs := by
rcases xs with xs, rfl
simp_all
theorem getElem?_replace {xs : Vector α n} {i : Nat} :
(xs.replace a b)[i]? = if xs[i]? == some a then if a xs.take i then some a else some b else xs[i]? := by
rcases xs with xs, rfl
simp [Array.getElem?_replace]
split <;> rename_i h
· rw (occs := [2]) [if_pos]
simpa using h
· rw [if_neg]
simpa using h
theorem getElem?_replace_of_ne {xs : Vector α n} {i : Nat} (h : xs[i]? some a) :
(xs.replace a b)[i]? = xs[i]? := by
simp_all [getElem?_replace]
theorem getElem_replace {xs : Vector α n} {i : Nat} (h : i < n) :
(xs.replace a b)[i] = if xs[i] == a then if a xs.take i then a else b else xs[i] := by
apply Option.some.inj
rw [ getElem?_eq_getElem, getElem?_replace]
split <;> split <;> simp_all
theorem getElem_replace_of_ne {xs : Vector α n} {i : Nat} {h : i < n} (h' : xs[i] a) :
(xs.replace a b)[i]'(by simpa) = xs[i]'(h) := by
rw [getElem_replace h]
simp [h']
theorem replace_append {xs : Vector α n} {ys : Vector α m} :
(xs ++ ys).replace a b = if a xs then xs.replace a b ++ ys else xs ++ ys.replace a b := by
rcases xs with xs, rfl
rcases ys with ys, rfl
simp only [mk_append_mk, replace_mk, eq_mk, Array.replace_append]
split <;> simp_all
theorem replace_append_left {xs : Vector α n} {ys : Vector α m} (h : a xs) :
(xs ++ ys).replace a b = xs.replace a b ++ ys := by
simp [replace_append, h]
theorem replace_append_right {xs : Vector α n} {ys : Vector α m} (h : ¬ a xs) :
(xs ++ ys).replace a b = xs ++ ys.replace a b := by
simp [replace_append, h]
theorem replace_extract {xs : Vector α n} {i : Nat} :
(xs.extract 0 i).replace a b = (xs.replace a b).extract 0 i := by
rcases xs with xs, rfl
simp [Array.replace_extract]
@[simp] theorem replace_mkArray_self {a : α} (h : 0 < n) :
(mkVector n a).replace a b = (#v[b] ++ mkVector (n - 1) a).cast (by omega) := by
match n, h with
| n + 1, _ => simp_all [mkVector_succ', replace_append]
@[simp] theorem replace_mkArray_ne {a b c : α} (h : !b == a) :
(mkVector n a).replace b c = mkVector n a := by
rw [replace_of_not_mem]
simp_all
end replace
/-! ## Logic -/
/-! ### any / all -/
theorem not_any_eq_all_not (xs : Vector α n) (p : α Bool) : (!xs.any p) = xs.all fun a => !p a := by
rcases xs with xs, rfl
simp [Array.not_any_eq_all_not]
theorem not_all_eq_any_not (xs : Vector α n) (p : α Bool) : (!xs.all p) = xs.any fun a => !p a := by
rcases xs with xs, rfl
simp [Array.not_all_eq_any_not]
theorem and_any_distrib_left (xs : Vector α n) (p : α Bool) (q : Bool) :
(q && xs.any p) = xs.any fun a => q && p a := by
rcases xs with xs, rfl
simp [Array.and_any_distrib_left]
theorem and_any_distrib_right (xs : Vector α n) (p : α Bool) (q : Bool) :
(xs.any p && q) = xs.any fun a => p a && q := by
rcases xs with xs, rfl
simp [Array.and_any_distrib_right]
theorem or_all_distrib_left (xs : Vector α n) (p : α Bool) (q : Bool) :
(q || xs.all p) = xs.all fun a => q || p a := by
rcases xs with xs, rfl
simp [Array.or_all_distrib_left]
theorem or_all_distrib_right (xs : Vector α n) (p : α Bool) (q : Bool) :
(xs.all p || q) = xs.all fun a => p a || q := by
rcases xs with xs, rfl
simp [Array.or_all_distrib_right]
theorem any_eq_not_all_not (xs : Vector α n) (p : α Bool) : xs.any p = !xs.all (!p .) := by
simp only [not_all_eq_any_not, Bool.not_not]
@[simp] theorem any_map {xs : Vector α n} {p : β Bool} : (xs.map f).any p = xs.any (p f) := by
rcases xs with xs, rfl
simp
@[simp] theorem all_map {xs : Vector α n} {p : β Bool} : (xs.map f).all p = xs.all (p f) := by
rcases xs with xs, rfl
simp
@[simp] theorem any_filter {xs : Vector α n} {p q : α Bool} :
(xs.filter p).any q = xs.any fun a => p a && q a := by
rcases xs with xs, rfl
simp
@[simp] theorem all_filter {xs : Vector α n} {p q : α Bool} :
(xs.filter p).all q = xs.all fun a => p a q a := by
rcases xs with xs, rfl
simp
@[simp] theorem any_filterMap {xs : Vector α n} {f : α Option β} {p : β Bool} :
(xs.filterMap f).any p = xs.any fun a => match f a with | some b => p b | none => false := by
rcases xs with xs, rfl
simp
rfl
@[simp] theorem all_filterMap {xs : Vector α n} {f : α Option β} {p : β Bool} :
(xs.filterMap f).all p = xs.all fun a => match f a with | some b => p b | none => true := by
rcases xs with xs, rfl
simp
rfl
@[simp] theorem any_append {xs : Vector α n} {ys : Vector α m} :
(xs ++ ys).any f = (xs.any f || ys.any f) := by
rcases xs with xs, rfl
rcases ys with ys, rfl
simp
@[simp] theorem all_append {xs : Vector α n} {ys : Vector α m} :
(xs ++ ys).all f = (xs.all f && ys.all f) := by
rcases xs with xs, rfl
rcases ys with ys, rfl
simp
@[congr] theorem anyM_congr [Monad m]
{xs ys : Vector α n} (w : xs = ys) {p q : α m Bool} (h : a, p a = q a) :
xs.anyM p = ys.anyM q := by
have : p = q := by funext a; apply h
subst this
subst w
rfl
@[congr] theorem any_congr
{xs ys : Vector α n} (w : xs = ys) {p q : α Bool} (h : a, p a = q a) :
xs.any p = ys.any q := by
unfold any
apply anyM_congr w h
@[congr] theorem allM_congr [Monad m]
{xs ys : Vector α n} (w : xs = ys) {p q : α m Bool} (h : a, p a = q a) :
xs.allM p = ys.allM q := by
have : p = q := by funext a; apply h
subst this
subst w
rfl
@[congr] theorem all_congr
{xs ys : Vector α n} (w : xs = ys) {p q : α Bool} (h : a, p a = q a) :
xs.all p = ys.all q := by
unfold all
apply allM_congr w h
@[simp] theorem any_flatten {xss : Vector (Vector α n) m} : xss.flatten.any f = xss.any (any · f) := by
cases xss using vector₂_induction
simp
@[simp] theorem all_flatten {xss : Vector (Vector α n) m} : xss.flatten.all f = xss.all (all · f) := by
cases xss using vector₂_induction
simp
@[simp] theorem any_flatMap {xs : Vector α n} {f : α Vector β m} {p : β Bool} :
(xs.flatMap f).any p = xs.any fun a => (f a).any p := by
rcases xs with xs
simp only [flatMap_mk, any_mk, Array.size_flatMap, size_toArray, Array.any_flatMap']
congr
funext
congr
simp [Vector.size_toArray]
@[simp] theorem all_flatMap {xs : Vector α n} {f : α Vector β m} {p : β Bool} :
(xs.flatMap f).all p = xs.all fun a => (f a).all p := by
rcases xs with xs
simp only [flatMap_mk, all_mk, Array.size_flatMap, size_toArray, Array.all_flatMap']
congr
funext
congr
simp [Vector.size_toArray]
@[simp] theorem any_reverse {xs : Vector α n} : xs.reverse.any f = xs.any f := by
rcases xs with xs, rfl
simp
@[simp] theorem all_reverse {xs : Vector α n} : xs.reverse.all f = xs.all f := by
rcases xs with xs, rfl
simp
@[simp] theorem any_cast {xs : Vector α n} : (xs.cast h).any f = xs.any f := by
rcases xs with xs, rfl
simp
@[simp] theorem all_cast {xs : Vector α n} : (xs.cast h).all f = xs.all f := by
rcases xs with xs, rfl
simp
@[simp] theorem any_mkVector {n : Nat} {a : α} :
(mkVector n a).any f = if n = 0 then false else f a := by
induction n <;> simp_all [mkVector_succ']
@[simp] theorem all_mkVector {n : Nat} {a : α} :
(mkVector n a).all f = if n = 0 then true else f a := by
induction n <;> simp_all +contextual [mkVector_succ']
/-! Content below this point has not yet been aligned with `List` and `Array`. -/
set_option linter.indexVariables false in
@@ -2511,14 +2763,6 @@ set_option linter.indexVariables false in
rcases xs with xs, rfl
simp
/--
Variant of `getElem_pop` that will sometimes fire when `getElem_pop` gets stuck because of
defeq issues in the implicit size argument.
-/
@[simp] theorem getElem_pop' (xs : Vector α (n + 1)) (i : Nat) (h : i < n + 1 - 1) :
@getElem (Vector α n) Nat α (fun _ i => i < n) instGetElemNatLt xs.pop i h = xs[i] :=
getElem_pop h
@[simp] theorem push_pop_back (xs : Vector α (n + 1)) : xs.pop.push xs.back = xs := by
ext i
by_cases h : i < n
@@ -2582,11 +2826,6 @@ theorem swap_comm (xs : Vector α n) {i j : Nat} {hi hj} :
simp only [swap_mk, mk.injEq]
rw [Array.swap_comm]
/-! ### range -/
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
simp [Vector.range]
/-! ### take -/
@[simp] theorem getElem_take (xs : Vector α n) (j : Nat) (hi : i < min n j) :

View File

@@ -115,6 +115,9 @@ theorem range'_eq_append_iff : range' s (n + m) = xs ++ ys ↔ xs = range' s n
/-! ### range -/
@[simp] theorem getElem_range (i : Nat) (hi : i < n) : (Vector.range n)[i] = i := by
simp [Vector.range]
theorem range_eq_range' (n : Nat) : range n = range' 0 n := by
simp [range, range', Array.range_eq_range']

View File

@@ -69,6 +69,11 @@ theorem eq_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a = b) = a := by s
theorem eq_congr {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = a₂) (h₂ : b₁ = b₂) : (a₁ = b₁) = (a₂ = b₂) := by simp [*]
theorem eq_congr' {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = b₂) (h₂ : b₁ = a₂) : (a₁ = b₁) = (a₂ = b₂) := by rw [h₁, h₂, Eq.comm (a := a₂)]
/-! Ne -/
theorem ne_of_ne_of_eq_left {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b c) : a c := by simp [*]
theorem ne_of_ne_of_eq_right {α : Sort u} {a b c : α} (h₁ : a = c) (h₂ : b c) : b a := by simp [*]
/-! Bool.and -/
theorem Bool.and_eq_of_eq_true_left {a b : Bool} (h : a = true) : (a && b) = b := by simp [h]

View File

@@ -69,6 +69,10 @@ structure Config where
verbose : Bool := true
/-- If `clean` is `true`, `grind` uses `expose_names` and only generates accessible names. -/
clean : Bool := true
/--
If `qlia` is `true`, `grind` may generate counterexamples for integer constraints using rational numbers.
This approach is cheaper but incomplete. -/
qlia : Bool := false
deriving Inhabited, BEq
end Lean.Grind

View File

@@ -111,9 +111,7 @@ def isExact : Constraint → Bool
theorem not_sat_of_isImpossible (h : isImpossible c) {t} : ¬ c.sat t := by
rcases c with _ | l, _ | u <;> simp [isImpossible, sat] at h
intro w
rw [Int.not_le]
exact Int.lt_of_lt_of_le h w
exact Int.lt_of_lt_of_le h
/--
Scale a constraint by multiplying by an integer.
@@ -139,17 +137,14 @@ theorem scale_sat {c : Constraint} (k) (w : c.sat t) : (scale k c).sat (k * t) :
· rcases c with _ | l, _ | u <;> split <;> rename_i h <;> simp_all [sat, flip, map]
· replace h := Int.le_of_lt h
exact Int.mul_le_mul_of_nonneg_left w h
· rw [Int.not_lt] at h
exact Int.mul_le_mul_of_nonpos_left h w
· exact Int.mul_le_mul_of_nonpos_left h w
· replace h := Int.le_of_lt h
exact Int.mul_le_mul_of_nonneg_left w h
· rw [Int.not_lt] at h
exact Int.mul_le_mul_of_nonpos_left h w
· exact Int.mul_le_mul_of_nonpos_left h w
· constructor
· exact Int.mul_le_mul_of_nonneg_left w.1 (Int.le_of_lt h)
· exact Int.mul_le_mul_of_nonneg_left w.2 (Int.le_of_lt h)
· replace h := Int.not_lt.mp h
constructor
· constructor
· exact Int.mul_le_mul_of_nonpos_left h w.2
· exact Int.mul_le_mul_of_nonpos_left h w.1
@@ -181,13 +176,13 @@ theorem combo_sat (a) (w₁ : c₁.sat x₁) (b) (w₂ : c₂.sat x₂) :
/-- The conjunction of two constraints. -/
def combine (x y : Constraint) : Constraint where
lowerBound := max x.lowerBound y.lowerBound
upperBound := min x.upperBound y.upperBound
lowerBound := Option.merge max x.lowerBound y.lowerBound
upperBound := Option.merge min x.upperBound y.upperBound
theorem combine_sat : (c : Constraint) (c' : Constraint) (t : Int)
(c.combine c').sat t = (c.sat t c'.sat t) := by
rintro _ | l₁, _ | u₁ <;> rintro _ | l₂, _ | u₂ t
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le] at *
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le, Option.merge] at *
· rw [And.comm]
· rw [ and_assoc, And.comm (a := l₂ t), and_assoc]
· rw [and_assoc]
@@ -210,21 +205,19 @@ theorem div_sat (c : Constraint) (t : Int) (k : Nat) (n : k ≠ 0) (h : (k : Int
· simp_all [sat, div]
· simp [sat, div] at w
apply Int.le_of_sub_nonneg
rw [ Int.sub_ediv_of_dvd _ h, ge_iff_le, Int.div_nonneg_iff_of_pos n]
rw [ Int.sub_ediv_of_dvd _ h, Int.ediv_nonneg_iff_of_pos n]
exact Int.sub_nonneg_of_le w
· simp [sat, div] at w
apply Int.le_of_sub_nonneg
rw [Int.sub_neg, Int.add_ediv_of_dvd_left h, ge_iff_le,
Int.div_nonneg_iff_of_pos n]
rw [Int.sub_neg, Int.add_ediv_of_dvd_left h, Int.ediv_nonneg_iff_of_pos n]
exact Int.sub_nonneg_of_le w
· simp [sat, div] at w
constructor
· apply Int.le_of_sub_nonneg
rw [Int.sub_neg, Int.add_ediv_of_dvd_left h, ge_iff_le,
Int.div_nonneg_iff_of_pos n]
rw [Int.sub_neg, Int.add_ediv_of_dvd_left h, Int.ediv_nonneg_iff_of_pos n]
exact Int.sub_nonneg_of_le w.1
· apply Int.le_of_sub_nonneg
rw [ Int.sub_ediv_of_dvd _ h, ge_iff_le, Int.div_nonneg_iff_of_pos n]
rw [ Int.sub_ediv_of_dvd _ h, Int.ediv_nonneg_iff_of_pos n]
exact Int.sub_nonneg_of_le w.2
/--

View File

@@ -57,6 +57,11 @@ def EIO.catchExceptions (act : EIO ε α) (h : ε → BaseIO α) : BaseIO α :=
| EStateM.Result.ok a s => EStateM.Result.ok a s
| EStateM.Result.error ex s => h ex s
def EIO.ofExcept (e : Except ε α) : EIO ε α :=
match e with
| Except.ok a => pure a
| Except.error e => throw e
open IO (Error) in
abbrev IO : Type Type := EIO Error

View File

@@ -48,7 +48,9 @@ inductive IO.Error where
| unexpectedEof
| userError (msg : String)
deriving Inhabited
instance : Inhabited IO.Error where
default := .userError "(`Inhabited.default` for `IO.Error`)"
@[export lean_mk_io_user_error]
def IO.userError (s : String) : IO.Error :=

View File

@@ -73,5 +73,5 @@ def Promise.result := @Promise.result!
/--
Like `Promise.result`, but resolves to `dflt` if the promise is dropped without ever being resolved.
-/
def Promise.resultD (promise : Promise α) (dflt : α): Task α :=
@[macro_inline] def Promise.resultD (promise : Promise α) (dflt : α) : Task α :=
promise.result?.map (sync := true) (·.getD dflt)

View File

@@ -8,12 +8,6 @@ import Lean.CoreM
namespace Lean
register_builtin_option debug.skipKernelTC : Bool := {
defValue := false
group := "debug"
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
}
/-- Adds given declaration to the environment, respecting `debug.skipKernelTC`. -/
def Kernel.Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
(cancelTk? : Option IO.CancelToken := none) : Except Exception Environment :=

View File

@@ -252,6 +252,13 @@ def registerEnumAttributes (attrDescrs : List (Name × String × α))
let r : Array (Name × α) := m.fold (fun a n p => a.push (n, p)) #[]
r.qsort (fun a b => Name.quickLt a.1 b.1)
statsFn := fun s => "enumeration attribute extension" ++ Format.line ++ "number of local entries: " ++ format s.size
-- We assume (and check below) that, if used asynchronously, enum attributes are set only in the
-- same context in which the tagged declaration was created
asyncMode := .async
replay? := some fun _ newState consts st => consts.foldl (init := st) fun st c =>
match newState.find? c with
| some v => st.insert c v
| _ => st
}
let attrs := attrDescrs.map fun (name, descr, val) => {
ref := ref
@@ -279,15 +286,16 @@ def getValue [Inhabited α] (attr : EnumAttributes α) (env : Environment) (decl
match (attr.ext.getModuleEntries env modIdx).binSearch (decl, default) (fun a b => Name.quickLt a.1 b.1) with
| some (_, val) => some val
| none => none
| none => (attr.ext.getState env).find? decl
| none => (attr.ext.findStateAsync env decl).find? decl
def setValue (attrs : EnumAttributes α) (env : Environment) (decl : Name) (val : α) : Except String Environment :=
def setValue (attrs : EnumAttributes α) (env : Environment) (decl : Name) (val : α) : Except String Environment := do
if (env.getModuleIdxFor? decl).isSome then
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, declaration is in an imported module")
else if ((attrs.ext.getState env).find? decl).isSome then
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, attribute has already been set")
else
Except.ok (attrs.ext.addEntry env (decl, val))
throw s!"invalid '{attrs.ext.name}'.setValue, declaration is in an imported module"
if !env.asyncMayContain decl then
throw s!"invalid '{attrs.ext.name}'.setValue, declaration is not from this async context"
if ((attrs.ext.findStateAsync env decl).find? decl).isSome then
throw s!"invalid '{attrs.ext.name}'.setValue, attribute has already been set"
return attrs.ext.addEntry env (decl, val)
end EnumAttributes

View File

@@ -11,10 +11,17 @@ namespace Lean
structure ClosedTermCache where
map : PHashMap Expr Name := {}
constNames : NameSet := {}
-- used for `replay?` only
revExprs : List Expr := []
deriving Inhabited
builtin_initialize closedTermCacheExt : EnvExtension ClosedTermCache
registerEnvExtension (pure {}) (asyncMode := .sync) -- compilation is non-parallel anyway
(replay? := some fun oldState newState _ s =>
let newExprs := newState.revExprs.take (newState.revExprs.length - oldState.revExprs.length)
newExprs.foldl (init := s) fun s e =>
let c := newState.map.find! e
{ s with map := s.map.insert e c, constNames := s.constNames.insert c, revExprs := e :: s.revExprs })
@[export lean_cache_closed_term_name]
def cacheClosedTermName (env : Environment) (e : Expr) (n : Name) : Environment :=

View File

@@ -94,6 +94,7 @@ builtin_initialize declMapExt : SimplePersistentEnvExtension Decl DeclMap ←
-- share a name prefix with the top-level Lean declaration being compiled, e.g. from
-- specialization.
asyncMode := .sync
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (!·.contains ·.name) (fun s d => s.insert d.name d)
}
@[export lean_ir_find_env_decl]

View File

@@ -143,6 +143,7 @@ builtin_initialize functionSummariesExt : SimplePersistentEnvExtension (FunId ×
addEntryFn := fun s e, n => s.insert e n
toArrayFn := fun s => sortEntries s.toArray
asyncMode := .sync -- compilation is non-parallel anyway
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (!·.contains ·.1) (fun s e, n => s.insert e n)
}
def addFunctionSummary (env : Environment) (fid : FunId) (v : Value) : Environment :=

View File

@@ -155,6 +155,7 @@ def emitMainFn : M Unit := do
int main(int argc, char ** argv) {
#if defined(WIN32) || defined(_WIN32)
SetErrorMode(SEM_FAILCRITICALERRORS);
SetConsoleOutputCP(CP_UTF8);
#endif
lean_object* in; lean_object* res;";
if usesLeanAPI then

View File

@@ -514,7 +514,9 @@ def inferStep : InterpM Bool := do
let currentVal getFunVal idx
withReader (fun ctx => { ctx with currFnIdx := idx }) do
decl.params.forM fun p => updateVarAssignment p.fvarId .top
decl.value.forCodeM interpCode
match decl.value with
| .code code .. => interpCode code
| .extern .. => updateCurrFnSummary .top
let newVal getFunVal idx
if currentVal != newVal then
return true

View File

@@ -149,8 +149,10 @@ def Decl.reduceArity (decl : Decl) : CompilerM (Array Decl) := do
match decl.value with
| .code code =>
let used collectUsedParams decl
if used.size == decl.params.size then
return #[decl] -- Declarations uses all parameters
if used.size == decl.params.size || used.size == 0 then
-- Do nothing if all params were used, or if no params were used. In the latter case,
-- this would promote the decl to a constant, which could execute unreachable code.
return #[decl]
else
trace[Compiler.reduceArity] "{decl.name}, used params: {used.toList.map mkFVar}"
let mask := decl.params.map fun param => used.contains param.fvarId

View File

@@ -111,6 +111,9 @@ builtin_initialize specExtension : SimplePersistentEnvExtension SpecEntry SpecSt
addEntryFn := SpecState.addEntry,
addImportedFn := fun es => (mkStateFromImportedEntries SpecState.addEntry {} es).switch
asyncMode := .sync -- compilation is non-parallel anyway
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (fun
| s, .info n _ => !s.specInfo.contains n
| s, .cache key _ => !s.cache.contains key) SpecState.addEntry
}
@[export lean_add_specialization_info]

View File

@@ -194,7 +194,7 @@ protected def withFreshMacroScope (x : CoreM α) : CoreM α := do
instance : MonadQuotation CoreM where
getCurrMacroScope := return ( read).currMacroScope
getMainModule := return ( get).env.mainModule
getMainModule := return ( getEnv).mainModule
withFreshMacroScope := Core.withFreshMacroScope
instance : Elab.MonadInfoTree CoreM where
@@ -365,6 +365,16 @@ for incremental reporting during elaboration of a single command.
def getAndEmptyMessageLog : CoreM MessageLog :=
modifyGet fun s => (s.messages, { s with messages := s.messages.markAllReported })
/--
Returns the current set of tasks added by `logSnapshotTask` and then resets it. When
saving/restoring state of an action that may have logged such tasks during incremental reuse, this
function must be used to store them in the corresponding snapshot tree; otherwise, they will leak
outside and may be cancelled by a later step, potentially leading to inconsistent state being
reused.
-/
def getAndEmptySnapshotTasks : CoreM (Array (Language.SnapshotTask Language.SnapshotTree)) :=
modifyGet fun s => (s.snapshotTasks, { s with snapshotTasks := #[] })
instance : MonadLog CoreM where
getRef := getRef
getFileMap := return ( read).fileMap
@@ -413,6 +423,26 @@ register_builtin_option stderrAsMessages : Bool := {
descr := "(server) capture output to the Lean stderr channel (such as from `dbg_trace`) during elaboration of a command as a diagnostic message"
}
/--
Creates snapshot reporting given `withIsolatedStreams` output and diagnostics and traces from the
given state.
-/
def mkSnapshot (output : String) (ctx : Context) (st : State)
(desc : String := by exact decl_name%.toString) : BaseIO Language.SnapshotTree := do
let mut msgs := st.messages
if !output.isEmpty then
msgs := msgs.add {
fileName := ctx.fileName
severity := MessageSeverity.information
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
data := output
}
return .mk {
desc
diagnostics := ( Language.Snapshot.Diagnostics.ofMessageLog msgs)
traces := st.traceState
} st.snapshotTasks
open Language in
/--
Wraps the given action for use in `BaseIO.asTask` etc., discarding its final state except for
@@ -443,20 +473,7 @@ def wrapAsyncAsSnapshot (act : Unit → CoreM Unit) (cancelTk? : Option IO.Cance
let ctx readThe Core.Context
return do
match ( t.toBaseIO) with
| .ok (output, st) =>
let mut msgs := st.messages
if !output.isEmpty then
msgs := msgs.add {
fileName := ctx.fileName
severity := MessageSeverity.information
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
data := output
}
return .mk {
desc
diagnostics := ( Language.Snapshot.Diagnostics.ofMessageLog msgs)
traces := st.traceState
} st.snapshotTasks
| .ok (output, st) => mkSnapshot output ctx st desc
-- interrupt or abort exception as `try catch` above should have caught any others
| .error _ => default
@@ -528,7 +545,9 @@ opaque compileDeclsOld (env : Environment) (opt : @& Options) (decls : @& List N
-- `ref?` is used for error reporting if available
partial def compileDecls (decls : List Name) (ref? : Option Declaration := none)
(logErrors := true) : CoreM Unit := do
if !Elab.async.get ( getOptions) then
-- When inside `realizeConst`, do compilation synchronously so that `_cstage*` constants are found
-- by the replay code
if !Elab.async.get ( getOptions) || ( getEnv).isRealizing then
doCompile
return
let env getEnv
@@ -646,6 +665,11 @@ def logMessageKind (kind : Name) : CoreM Bool := do
modify fun s => { s with messages.loggedKinds := s.messages.loggedKinds.insert kind }
return true
@[inherit_doc Environment.enableRealizationsForConst]
def enableRealizationsForConst (n : Name) : CoreM Unit := do
let env ( getEnv).enableRealizationsForConst ( getOptions) n
setEnv env
builtin_initialize
registerTraceClass `Elab.async
registerTraceClass `Elab.block

View File

@@ -1215,7 +1215,7 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
let fullName := Name.mkStr structName fieldName
for localDecl in ( getLCtx) do
if localDecl.isAuxDecl then
if let some localDeclFullName := ( read).auxDeclToFullName.find? localDecl.fvarId then
if let some localDeclFullName := ( getLCtx).auxDeclToFullName.find? localDecl.fvarId then
if fullName == (privateToUserName? localDeclFullName).getD localDeclFullName then
/- LVal notation is being used to make a "local" recursive call. -/
return LValResolution.localRec structName fullName localDecl.toExpr

View File

@@ -201,12 +201,27 @@ private def elabTParserMacroAux (prec lhsPrec e : Term) : TermElabM Syntax := do
@[builtin_macro Lean.Parser.Term.assert] def expandAssert : Macro
| `(assert! $cond; $body) =>
-- TODO: support for disabling runtime assertions
match cond.raw.reprint with
| some code => `(if $cond then $body else panic! ("assertion violation: " ++ $(quote code)))
| none => `(if $cond then $body else panic! ("assertion violation"))
| _ => Macro.throwUnsupported
register_builtin_option debugAssertions : Bool := {
defValue := false
descr := "enable `debug_assert!` statements\
\n\
\nDefaults to `false` unless the Lake `buildType` is `debug`."
}
@[builtin_term_elab Lean.Parser.Term.debugAssert] def elabDebugAssert : TermElab :=
adaptExpander fun
| `(Parser.Term.debugAssert| debug_assert! $cond; $body) => do
if debugAssertions.get ( getOptions) then
`(assert! $cond; $body)
else
return body
| _ => throwUnsupportedSyntax
@[builtin_macro Lean.Parser.Term.dbgTrace] def expandDbgTrace : Macro
| `(dbg_trace $arg:interpolatedStr; $body) => `(dbgTrace (s! $arg) fun _ => $body)
| `(dbg_trace $arg:term; $body) => `(dbgTrace (toString $arg) fun _ => $body)

View File

@@ -491,6 +491,8 @@ partial def elabCommand (stx : Syntax) : CommandElabM Unit := do
-- check absence of traces; see Note [Incremental Macros]
guard <| !oldSnap.hasTraces && !hasTraces
return oldSnap
if snap.old?.isSome && oldSnap?.isNone then
snap.old?.forM (·.val.cancelRec)
let oldCmds? := oldSnap?.map fun old =>
if old.newStx.isOfKind nullKind then old.newStx.getArgs else #[old.newStx]
let cmdPromises cmds.mapM fun _ => IO.Promise.new
@@ -519,6 +521,8 @@ partial def elabCommand (stx : Syntax) : CommandElabM Unit := do
let old oldSnap?
return { stx := ( oldCmd?), val := ( old.next[i]?) }
} }) do
if oldSnap?.isSome && ( read).snap?.isNone then
oldSnap?.bind (·.next[i]?) |>.forM (·.cancelRec)
elabCommand cmd
-- Resolve promise for commands not supporting incrementality; waiting for
-- `withAlwaysResolvedPromises` to do this could block reporting by later

View File

@@ -49,9 +49,11 @@ structure BodyProcessedSnapshot extends Language.Snapshot where
state : Term.SavedState
/-- Elaboration result. -/
value : Expr
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
moreSnaps : Array (SnapshotTask SnapshotTree)
deriving Nonempty
instance : Language.ToSnapshotTree BodyProcessedSnapshot where
toSnapshotTree s := s.toSnapshot, #[]
toSnapshotTree s := s.toSnapshot, s.moreSnaps
/-- Snapshot after elaboration of a definition header. -/
structure HeaderProcessedSnapshot extends Language.Snapshot where
@@ -67,13 +69,15 @@ structure HeaderProcessedSnapshot extends Language.Snapshot where
bodyStx : Syntax
/-- Result of body elaboration. -/
bodySnap : SnapshotTask (Option BodyProcessedSnapshot)
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
moreSnaps : Array (SnapshotTask SnapshotTree)
deriving Nonempty
instance : Language.ToSnapshotTree HeaderProcessedSnapshot where
toSnapshotTree s := s.toSnapshot,
(match s.tacSnap? with
| some tac => #[tac.map (sync := true) toSnapshotTree]
| none => #[]) ++
#[s.bodySnap.map (sync := true) toSnapshotTree]
#[s.bodySnap.map (sync := true) toSnapshotTree] ++ s.moreSnaps
/-- State before elaboration of a mutual definition. -/
structure DefParsed where

View File

@@ -1036,6 +1036,9 @@ def seqToTerm (action : Syntax) (k : Syntax) : M Syntax := withRef action <| wit
else if action.getKind == ``Parser.Term.doAssert then
let cond := action[1]
`(assert! $cond; $k)
else if action.getKind == ``Parser.Term.doDebugAssert then
let cond := action[1]
`(debugAssert| debug_assert! $cond; $k)
else
let action withRef action ``(($action : $((read).m) PUnit))
``(Bind.bind $action (fun (_ : PUnit) => $k))
@@ -1765,6 +1768,8 @@ mutual
return mkSeq doElem ( doSeqToCode doElems)
else if k == ``Parser.Term.doAssert then
return mkSeq doElem ( doSeqToCode doElems)
else if k == ``Parser.Term.doDebugAssert then
return mkSeq doElem ( doSeqToCode doElems)
else if k == ``Parser.Term.doNested then
let nestedDoSeq := doElem[1]
doSeqToCode (getDoSeqElems nestedDoSeq ++ doElems)

View File

@@ -166,6 +166,8 @@ private def elabHeaders (views : Array DefView)
view.value.eqWithInfoAndTraceReuse ( getOptions) old.bodyStx
-- no syntax guard to store, we already did the necessary checks
oldBodySnap? := guard reuseBody *> pure .missing, old.bodySnap
if oldBodySnap?.isNone then
old.bodySnap.cancelRec
oldTacSnap? := do
guard reuseTac
some ( old.tacStx?), ( old.tacSnap?)
@@ -229,6 +231,7 @@ private def elabHeaders (views : Array DefView)
snap.new.resolve <| some {
diagnostics :=
( Language.Snapshot.Diagnostics.ofMessageLog ( Core.getAndEmptyMessageLog))
moreSnaps := ( Core.getAndEmptySnapshotTasks)
view := newHeader.toDefViewElabHeaderData
state := newState
tacStx?
@@ -428,6 +431,10 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
if let some old := old.val.get then
snap.new.resolve <| some old
reusableResult? := some (old.value, old.state)
else
-- NOTE: this will eagerly cancel async tasks not associated with an inner snapshot, most
-- importantly kernel checking and compilation of the top-level declaration
old.val.cancelRec
let (val, state) withRestoreOrSaveFull reusableResult? header.tacSnap? do
withReuseContext header.value do
@@ -479,6 +486,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
snap.new.resolve <| some {
diagnostics :=
( Language.Snapshot.Diagnostics.ofMessageLog ( Core.getAndEmptyMessageLog))
moreSnaps := ( Core.getAndEmptySnapshotTasks)
state
value := val
}
@@ -1094,6 +1102,8 @@ def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
return .missing, oldParsed.headerProcessedSnap
new := headerPromise
} }
if snap.old?.isSome && (view.headerSnap?.bind (·.old?)).isNone then
snap.old?.forM (·.val.cancelRec)
defs := defs.push {
fullHeaderRef
headerProcessedSnap := { stx? := d, task := headerPromise.resultD default }

View File

@@ -336,7 +336,7 @@ private def withInductiveLocalDecls (rs : Array PreElabHeaderResult) (x : Array
let rec loop (i : Nat) (indFVars : Array Expr) := do
if h : i < namesAndTypes.size then
let (declName, shortDeclName, type) := namesAndTypes[i]
Term.withAuxDecl shortDeclName type declName fun indFVar => loop (i+1) (indFVars.push indFVar)
withAuxDecl shortDeclName type declName fun indFVar => loop (i+1) (indFVars.push indFVar)
else
x params indFVars
loop 0 #[]
@@ -931,6 +931,8 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
for ctor in view.ctors do
if (ctor.declId.getPos? (canonicalOnly := true)).isSome then
Term.addTermInfo' ctor.declId ( mkConstWithLevelParams ctor.declName) (isBinder := true)
enableRealizationsForConst ctor.declName
enableRealizationsForConst view.declName
return res
private def mkAuxConstructions (declNames : Array Name) : TermElabM Unit := do

View File

@@ -161,6 +161,7 @@ private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List N
if compile && shouldGenCodeFor preDef then
compileDecl decl
if applyAttrAfterCompilation then
enableRealizationsForConst preDef.declName
generateEagerEqns preDef.declName
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation

View File

@@ -82,6 +82,7 @@ Assign final attributes to the definitions. Assumes the EqnInfos to be already p
def addPreDefAttributes (preDefs : Array PreDefinition) : TermElabM Unit := do
for preDef in preDefs do
markAsRecursive preDef.declName
enableRealizationsForConst preDef.declName
generateEagerEqns preDef.declName
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
-- Unless the user asks for something else, mark the definition as irreducible

View File

@@ -20,18 +20,23 @@ Simple, coarse-grained equation theorem for nonrecursive definitions.
-/
private def mkSimpleEqThm (declName : Name) (suffix := Name.mkSimple unfoldThmSuffix) : MetaM (Option Name) := do
if let some (.defnInfo info) := ( getEnv).find? declName then
let name := declName ++ suffix
-- determinism: `name` and `info` are dependent only on `declName`, not any later env
-- modifications
realizeConst declName name (doRealize name info)
return some name
else
return none
where
doRealize name info :=
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
let type mkForallFVars xs ( mkEq lhs body)
let value mkLambdaFVars xs ( mkEqRefl lhs)
let name := declName ++ suffix
addDecl <| Declaration.thmDecl {
addDecl <| .thmDecl {
name, type, value
levelParams := info.levelParams
}
return some name
else
return none
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
if ( isRecursiveDefinition declName) then

View File

@@ -193,6 +193,10 @@ def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
addSmartUnfoldingDef preDef recArgPos
markAsRecursive preDef.declName
for preDef in preDefs do
-- must happen in separate loop so realizations can see eqnInfos of all other preDefs
enableRealizationsForConst preDef.declName
-- must happen after `enableRealizationsForConst`
generateEagerEqns preDef.declName
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation

View File

@@ -68,6 +68,7 @@ def wfRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (Option T
unless ( isProp preDef.type) do
WF.mkUnfoldEq preDef preDefNonRec.declName wfPreprocessProof
Mutual.addPreDefAttributes preDefs
enableRealizationsForConst preDefNonRec.declName
builtin_initialize registerTraceClass `Elab.definition.wf

View File

@@ -100,4 +100,7 @@ def mkUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) (wfPreprocessPr
}
trace[Elab.definition.wf] "mkUnfoldEq defined {.ofConstName name}"
builtin_initialize
registerTraceClass `Elab.definition.wf.eqns
end Lean.Elab.WF

View File

@@ -607,7 +607,9 @@ where
let parentView := view.parents[i]
withRef parentView.ref do
-- The only use case for autobound implicits for parents might be outParams, but outParam is not propagated.
let parentType whnf <| Term.withoutAutoBoundImplicit <| Term.elabType parentView.type
let parentType Term.withoutAutoBoundImplicit <| Term.elabType parentView.type
Term.synthesizeSyntheticMVarsNoPostponing
let parentType whnf parentType
if parentType.getAppFn == indFVar then
logWarning "structure extends itself, skipping"
return go (i + 1) infos parents
@@ -825,20 +827,18 @@ private partial def checkResultingUniversesForFields (fieldInfos : Array StructF
which is not less than or equal to the structure's resulting universe level{indentD u}"
throwErrorAt info.ref msg
@[extern "lean_mk_projections"]
private opaque mkProjections (env : Environment) (structName : Name) (projs : List Name) (isClass : Bool) : Except Kernel.Exception Environment
private def addProjections (r : ElabHeaderResult) (fieldInfos : Array StructFieldInfo) : TermElabM Unit := do
if r.type.isProp then
if let some fieldInfo fieldInfos.findM? (not <$> Meta.isProof ·.fvar) then
throwErrorAt fieldInfo.ref m!"failed to generate projections for 'Prop' structure, field '{format fieldInfo.name}' is not a proof"
let projNames := fieldInfos |>.filter (!·.isFromSubobject) |>.map (·.declName)
let env getEnv
let env ofExceptKernelException (mkProjections env r.view.declName projNames.toList r.view.isClass)
setEnv env
let projDecls : Array StructProjDecl :=
fieldInfos
|>.filter (!·.isFromSubobject)
|>.map (fun info => { ref := info.ref, projName := info.declName })
mkProjections r.view.declName projDecls r.view.isClass
for fieldInfo in fieldInfos do
if fieldInfo.isSubobject then
addDeclarationRangesFromSyntax fieldInfo.declName r.view.ref fieldInfo.ref
for decl in projDecls do
-- projections may generate equation theorems
enableRealizationsForConst decl.projName
private def registerStructure (structName : Name) (infos : Array StructFieldInfo) : TermElabM Unit := do
let fields infos.filterMapM fun info => do

View File

@@ -190,6 +190,26 @@ where
return (x, toExpr <| UInt64.ofBitVec (h value.bv))
else
throwError m!"Value for UInt64 was not 64 bit but {value.w} bit"
| Int8.toBitVec x =>
if h : value.w = 8 then
return (x, toExpr <| Int8.ofBitVec (h value.bv))
else
throwError m!"Value for Int8 was not 8 bit but {value.w} bit"
| Int16.toBitVec x =>
if h : value.w = 16 then
return (x, toExpr <| Int16.ofBitVec (h value.bv))
else
throwError m!"Value for Int16 was not 16 bit but {value.w} bit"
| Int32.toBitVec x =>
if h : value.w = 32 then
return (x, toExpr <| Int32.ofBitVec (h value.bv))
else
throwError m!"Value for Int32 was not 32 bit but {value.w} bit"
| Int64.toBitVec x =>
if h : value.w = 64 then
return (x, toExpr <| Int64.ofBitVec (h value.bv))
else
throwError m!"Value for Int64 was not 64 bit but {value.w} bit"
| _ =>
match var with
| .app (.const (.str p s) []) arg =>

View File

@@ -37,11 +37,9 @@ Assuming that `declName` is an enum inductive construct a function of type `decl
that maps `declName` constructors to their numeric indices as `BitVec`.
-/
def getEnumToBitVecFor (declName : Name) : MetaM Name := do
let env getEnv
let enumToBitVecName := Name.str declName enumToBitVecSuffix
if env.contains enumToBitVecName then
return enumToBitVecName
else
realizeConst declName enumToBitVecName do
let env getEnv
let .inductInfo inductiveInfo getConstInfo declName | throwError m!"{declName} is not an inductive."
if !( isEnumType declName) then
throwError m!"{declName} is not an enum inductive."
@@ -67,18 +65,15 @@ def getEnumToBitVecFor (declName : Name) : MetaM Name := do
hints := .regular (getMaxHeight env translator + 1)
safety := .safe
}
return enumToBitVecName
return enumToBitVecName
/--
Assuming that `declName` is an enum inductive, construct a proof of
`∀ (x y : declName) : x = y ↔ x.enumToBitVec = y.enumToBitVec`.
-/
def getEqIffEnumToBitVecEqFor (declName : Name) : MetaM Name := do
let env getEnv
let eqIffEnumToBitVecEqName := Name.str declName eqIffEnumToBitVecEqSuffix
if env.contains eqIffEnumToBitVecEqName then
return eqIffEnumToBitVecEqName
else
realizeConst declName eqIffEnumToBitVecEqName do
/-
We prove the lemma by constructing an inverse to `enumToBitVec` and use the fact that all
invertible functions respect equality.
@@ -145,7 +140,7 @@ def getEqIffEnumToBitVecEqFor (declName : Name) : MetaM Name := do
type := type
value := value
}
return eqIffEnumToBitVecEqName
return eqIffEnumToBitVecEqName
where
mkInverse {w : Nat} (input : Expr) (retType : Expr) (instBEq : Expr) (ctors : List Name)
(counter : BitVec w) (acc : Expr) :
@@ -169,11 +164,8 @@ Assuming that `declName` is an enum inductive, construct a proof of
constructors of `declName`.
-/
def getEnumToBitVecLeFor (declName : Name) : MetaM Name := do
let env getEnv
let enumToBitVecLeName := Name.str declName enumToBitVecLeSuffix
if env.contains enumToBitVecLeName then
return enumToBitVecLeName
else
realizeConst declName enumToBitVecLeName do
let enumToBitVec := mkConst ( getEnumToBitVecFor declName)
let .inductInfo inductiveInfo getConstInfo declName | unreachable!
let ctors := inductiveInfo.ctors
@@ -207,7 +199,7 @@ def getEnumToBitVecLeFor (declName : Name) : MetaM Name := do
type := type
value := value
}
return enumToBitVecLeName
return enumToBitVecLeName
builtin_initialize
@@ -274,11 +266,11 @@ partial def enumsPass : Pass where
let simprocs Simp.SimprocsArray.add #[] ``enumsPassPost true
let result?, _
simpGoal
goal
(ctx := simpCtx)
(simprocs := simprocs)
(fvarIdsToSimp := getPropHyps)
simpGoal
goal
(ctx := simpCtx)
(simprocs := simprocs)
(fvarIdsToSimp := getPropHyps)
let some (_, newGoal) := result? | return none
postprocess newGoal |>.run' {}
where

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
prelude
import Init.Data.SInt.Basic
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize.Basic
import Lean.Elab.Tactic.BVDecide.Frontend.Attr
import Lean.Elab.Tactic.Simp
@@ -14,7 +15,7 @@ This module contains the implementation of the pre processing pass for reducing
It:
1. runs the `int_toBitVec` simp set
2. If `USize.toBitVec` is used anywhere looks for equations of the form
2. If `USize.toBitVec`/`ISize.toBitVec` is used anywhere looks for equations of the form
`System.Platform.numBits = constant` (or flipped) and uses them to convert the system back to
fixed width.
-/
@@ -25,11 +26,12 @@ namespace Frontend.Normalize
open Lean.Meta
/--
Contains information for the `USize` elimination pass.
Contains information for the `USize`/`ISize` elimination pass.
-/
structure USizeState where
structure SizeState where
/--
Contains terms of the form `USize.toBitVec e` that we will translate to constant width `BitVec`.
Contains terms of the form `USize.toBitVec e` and `ISize.toBitVec e` that we will translate to
constant width `BitVec`.
-/
relevantTerms : Std.HashSet Expr := {}
/--
@@ -37,16 +39,16 @@ structure USizeState where
-/
relevantHyps : Std.HashSet FVarId := {}
private abbrev M := StateRefT USizeState MetaM
private abbrev M := StateRefT SizeState MetaM
namespace M
@[inline]
def addUSizeTerm (e : Expr) : M Unit := do
def addSizeTerm (e : Expr) : M Unit := do
modify fun s => { s with relevantTerms := s.relevantTerms.insert e }
@[inline]
def addUSizeHyp (f : FVarId) : M Unit := do
def addSizeHyp (f : FVarId) : M Unit := do
modify fun s => { s with relevantHyps := s.relevantHyps.insert f }
end M
@@ -64,30 +66,30 @@ def intToBitVecPass : Pass where
let hyps goal.getNondepPropHyps
let result?, _ simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := hyps)
let some (_, goal) := result? | return none
handleUSize goal |>.run' {}
handleSize goal |>.run' {}
where
handleUSize (goal : MVarId) : M MVarId := do
if detectUSize goal then
replaceUSize goal
handleSize (goal : MVarId) : M MVarId := do
if detectSize goal then
replaceSize goal
else
return goal
detectUSize (goal : MVarId) : M Bool := do
detectSize (goal : MVarId) : M Bool := do
goal.withContext do
for hyp in getPropHyps do
( hyp.getType).forEachWhere
(stopWhenVisited := true)
(·.isAppOfArity ``USize.toBitVec 1)
(fun e => e.isAppOfArity ``USize.toBitVec 1 || e.isAppOfArity ``ISize.toBitVec 1)
fun e => do
M.addUSizeTerm e
M.addUSizeHyp hyp
M.addSizeTerm e
M.addSizeHyp hyp
return !( get).relevantTerms.isEmpty
/--
Turn `goal` into a goal containing `BitVec const` instead of `USize`.
Turn `goal` into a goal containing `BitVec const` instead of `USize`/`ISize`.
-/
replaceUSize (goal : MVarId) : M MVarId := do
replaceSize (goal : MVarId) : M MVarId := do
if let some (numBits, numBitsEq) findNumBitsEq goal then
goal.withContext do
let relevantHyps := ( get).relevantHyps.toArray.map mkFVar
@@ -138,13 +140,14 @@ where
numBitsEq
(mkMVar newGoal)
goal.assign <| mkAppN casesOn (relevantTerms ++ abstractedHyps)
-- remove all of the hold hypotheses about USize.toBitVec to prevent false counter examples
-- remove all of the hold hypotheses about USize.toBitVec/ISize.toBitVec to prevent
-- false counter examples
(newGoal, _) newGoal.tryClearMany' (abstractedHyps.map Expr.fvarId!)
-- intro both the new `BitVec const` as well as all hypotheses about them
(_, newGoal) newGoal.introN (relevantTerms.size + abstractedHyps.size)
return newGoal
else
logWarning m!"Detected USize in the goal but no hypothesis about System.Platform.numBits, consider case splitting on {mkConst ``System.Platform.numBits_eq}"
logWarning m!"Detected USize/ISize in the goal but no hypothesis about System.Platform.numBits, consider case splitting on {mkConst ``System.Platform.numBits_eq}"
return goal
/--

View File

@@ -15,11 +15,12 @@ structures containing information about supported types into individual parts re
The implementation runs cases recursively on all "interesting" types where a type is interesting if
it is a non recursive structure and at least one of the following conditions hold:
- it contains something of type `BitVec`/`UIntX`/`Bool`
- it contains something of type `BitVec`/`UIntX`/`IntX`/`Bool`
- it is parametrized by an interesting type
- it contains another interesting type
Afterwards we also apply relevant `injEq` theorems to support at least equality for these types out
of the box.
Afterwards we also:
- apply relevant `injEq` theorems to support at least equality for these types out of the box.
- push projections of relevant types inside of `ite` and `cond`.
-/
namespace Lean.Elab.Tactic.BVDecide
@@ -27,6 +28,33 @@ namespace Frontend.Normalize
open Lean.Meta
def applyIteSimproc : Simp.Simproc := fun e => e.withApp fun proj args => do
if h : args.size 0 then
let_expr ite α c instDec t e := args.back | return .continue
let params := args.pop
let projApp := mkAppN proj params
let newT := mkApp projApp t
let newE := mkApp projApp e
let newIf mkAppOptM ``ite #[none, c, instDec, newT, newE]
let proof mkAppOptM ``apply_ite #[α, none, projApp, c, instDec, t, e]
return .visit { expr := newIf, proof? := some proof }
else
return .continue
def applyCondSimproc : Simp.Simproc := fun e => e.withApp fun proj args => do
if h : args.size 0 then
let_expr cond α c t e := args.back | return .continue
let params := args.pop
let projApp := mkAppN proj params
let newT := mkApp projApp t
let newE := mkApp projApp e
let newCond mkAppOptM ``cond #[none, c, newT, newE]
let proof mkAppOptM ``Bool.apply_cond #[α, none, projApp, c, t, e]
return .visit { expr := newCond, proof? := some proof }
else
return .continue
partial def structuresPass : Pass where
name := `structures
run' goal := do
@@ -36,14 +64,16 @@ partial def structuresPass : Pass where
if decl.isLet || decl.isImplementationDetail then
return false
else
let some const := decl.type.getAppFn.constName? | return false
let some const := ( instantiateMVars decl.type).getAppFn.constName? | return false
return interesting.contains const
match goals with
| [goal] => postprocess goal interesting
| _ => throwError "structures preprocessor generated more than 1 goal"
where
postprocess (goal : MVarId) (interesting : Std.HashSet Name) : PreProcessM (Option MVarId) := do
let env getEnv
goal.withContext do
let mut simprocs : Simprocs := {}
let mut relevantLemmas : SimpTheoremsArray := #[]
relevantLemmas relevantLemmas.addTheorem (.decl ``ne_eq) ( mkConstWithLevelParams ``ne_eq)
for const in interesting do
@@ -54,14 +84,43 @@ where
trace[Meta.Tactic.bv] m!"Using injEq lemma: {lemmaName}"
let statement mkConstWithLevelParams lemmaName
relevantLemmas relevantLemmas.addTheorem (.decl lemmaName) statement
let fields := (getStructureInfo env const).fieldNames.size
let numParams := constInfo.numParams
for proj in [0:fields] do
-- We use the simprocs with pre such that we push in projections eagerly in order to
-- potentially not have to simplify complex structure expressions that we only project one
-- element out of.
let path := mkDiscrPathFor const numParams proj ``ite 5
simprocs := simprocs.addCore path ``applyIteSimproc false (.inl applyIteSimproc)
let path := mkDiscrPathFor const numParams proj ``cond 4
simprocs := simprocs.addCore path ``applyCondSimproc false (.inl applyCondSimproc)
let cfg PreProcessM.getConfig
let simpCtx Simp.mkContext
(config := { failIfUnchanged := false, maxSteps := cfg.maxSteps })
(simpTheorems := relevantLemmas)
(congrTheorems := getSimpCongrTheorems)
let result?, _ simpGoal goal (ctx := simpCtx) (fvarIdsToSimp := getPropHyps)
let result?, _
simpGoal
goal
(ctx := simpCtx)
(simprocs := #[simprocs])
(fvarIdsToSimp := getPropHyps)
let some (_, newGoal) := result? | return none
return newGoal
/--
For `Prod.fst` and `ite` this function creates the path: `Prod.fst (ite (Prod _ _) _ _ _ _)`.
This path can be used to match on applications of structure projections onto control flow primitives.
-/
mkDiscrPathFor (struct : Name) (structParams : Nat) (projIdx : Nat) (controlFlow : Name)
(controlFlowParams : Nat) : Array DiscrTree.Key := Id.run do
let stars := structParams + controlFlowParams - 1
let mut path : Array DiscrTree.Key := Array.mkEmpty (3 + stars)
path := path.push <| .proj struct projIdx 0
path := path.push <| .const controlFlow controlFlowParams
path := path.push <| .const struct structParams
path := Nat.fold (init := path) stars (fun _ _ acc => acc.push .star)
return path
end Frontend.Normalize
end Lean.Elab.Tactic.BVDecide

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
prelude
import Init.Data.SInt.Basic
import Lean.Elab.Tactic.BVDecide.Frontend.Normalize.Basic
/-!
@@ -21,6 +22,9 @@ partial def typeAnalysisPass : Pass where
name := `typeAnalysis
run' goal := do
checkContext goal
let analysis PreProcessM.getTypeAnalysis
trace[Meta.Tactic.bv] m!"Type analysis found structures: {analysis.interestingStructures.toList}"
trace[Meta.Tactic.bv] m!"Type analysis found enums: {analysis.interestingEnums.toList}"
return goal
where
checkContext (goal : MVarId) : PreProcessM Unit := do
@@ -64,6 +68,11 @@ where
| UInt32 => return true
| UInt64 => return true
| USize => return true
| Int8 => return true
| Int16 => return true
| Int32 => return true
| Int64 => return true
| ISize => return true
| Bool => return true
| _ =>
let some const := expr.getAppFn.constName? | return false

View File

@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
prelude
import Lean.Data.RBMap
import Init.Data.Nat.Fold
import Std.Tactic.BVDecide.LRAT.Actions
import Std.Data.HashMap
@@ -17,7 +17,6 @@ This module implements the LRAT trimming algorithm described in section 4 of
namespace Lean.Elab.Tactic.BVDecide
namespace LRAT
open Lean (RBMap)
open Std.Tactic.BVDecide.LRAT (IntAction)
namespace trim
@@ -41,16 +40,18 @@ structure Context where
structure State where
/--
The set of used proof step ids.
For each proof step `i` contains at index `i - initialId` `0` if `i` is unused, `1` if it is
used.
-/
used : RBMap Nat Unit compare := {}
used : ByteArray
/--
A mapping from old proof step ids to new ones. Used such that the proof remains a sequence without
For each proof step `i` contains at index `i - initialId` the step that `i` maps to in the new
proof or `0` if that step is not yet set. Used such that the proof remains a sequence without
gaps.
-/
mapped : Std.HashMap Nat Nat := {}
mapped : Array Nat
abbrev M : Type Type := ReaderT Context <| ExceptT String <| StateM State
abbrev M : Type Type := ReaderT Context <| StateM State
namespace M
@@ -78,7 +79,9 @@ def run (proof : Array IntAction) (x : M α) : Except String α := do
| .addEmpty id .. | .addRup id .. | .addRat id .. => acc.insert id a
| .del .. => acc
let proof := proof.foldl (init := {}) folder
ReaderT.run x { proof, initialId, addEmptyId } |>.run |>.run' {}
let used := Nat.fold proof.size (init := ByteArray.mkEmpty proof.size) (fun _ _ acc => acc.push 0)
let mapped := Array.mkArray proof.size 0
return ReaderT.run x { proof, initialId, addEmptyId } |>.run' { used, mapped }
@[inline]
def getInitialId : M Nat := do
@@ -90,6 +93,10 @@ def getEmptyId : M Nat := do
let ctx read
return ctx.addEmptyId
@[inline]
private def idIndex (id : Nat) : M Nat := do
return id - ( M.getInitialId)
@[inline]
def getProofStep (id : Nat) : M (Option IntAction) := do
let ctx read
@@ -98,22 +105,20 @@ def getProofStep (id : Nat) : M (Option IntAction) := do
@[inline]
def isUsed (id : Nat) : M Bool := do
let s get
return s.used.contains id
return s.used[ idIndex id]! == 1
@[inline]
def markUsed (id : Nat) : M Unit := do
-- If we are referring to a proof step that is not part of the proof, it is part of the CNF.
-- We do not trim the CNF so just forget about the fact that this step was used.
if ( getProofStep id).isSome then
modify (fun s => { s with used := s.used.insert id () })
if id >= ( M.getInitialId) then
let idx idIndex id
modify (fun s => { s with used := s.used.set! idx 1 })
@[inline]
def getUsedSet : M (RBMap Nat Unit Ord.compare) := do
let s get
return s.used
def registerIdMap (oldId : Nat) (newId : Nat) : M Unit := do
modify (fun s => { s with mapped := s.mapped.insert oldId newId })
let idx idIndex oldId
modify (fun s => { s with mapped := s.mapped.set! idx newId })
def mapStep (step : IntAction) : M IntAction := do
match step with
@@ -139,8 +144,12 @@ def mapStep (step : IntAction) : M IntAction := do
where
@[inline]
mapIdent (ident : Nat) : M Nat := do
let s get
return s.mapped[ident]? |>.getD ident
if ident < ( getInitialId) then
return ident
else
let s get
let newId := s.mapped[ idIndex ident]!
return newId
end M
@@ -150,14 +159,17 @@ up with DFS.
-/
partial def useAnalysis : M Unit := do
let emptyId M.getEmptyId
go [emptyId]
go #[emptyId]
where
go (workList : List Nat) : M Unit := do
match workList with
| [] => return ()
| id :: workList =>
go (worklist : Array Nat) : M Unit := do
let mut worklist := worklist
if h : worklist.size = 0 then
return ()
else
let id := worklist.back
worklist := worklist.pop
if M.isUsed id then
go workList
go worklist
else
M.markUsed id
let step? M.getProofStep id
@@ -165,36 +177,37 @@ where
| some step =>
match step with
| .addEmpty _ hints =>
let workList := hints.toList ++ workList
go workList
worklist := worklist ++ hints
go worklist
| .addRup _ _ hints =>
let workList := hints.toList ++ workList
go workList
worklist := worklist ++ hints
go worklist
| .addRat _ _ _ rupHints ratHints =>
let folder acc a :=
a.fst :: a.snd.toList ++ acc
let ratHints := ratHints.foldl (init := []) folder
let workList := rupHints.toList ++ ratHints ++ workList
go workList
| .del .. => go workList
| none => go workList
let folder acc a := acc.push a.fst ++ a.snd
let ratHints := ratHints.foldl (init := Array.mkEmpty ratHints.size) folder
worklist := worklist ++ ratHints ++ rupHints
go worklist
| .del .. => go worklist
| none => go worklist
/--
Map the set of used proof steps to a new LRAT proof that has no holes in the sequence of proof
identifiers.
-/
def mapping : M (Array IntAction) := do
let used M.getUsedSet
let mut nextMapped M.getInitialId
let mut newProof := Array.mkEmpty used.size
for (id, _) in used do
M.registerIdMap id nextMapped
-- This should never panic as the use def analysis has already marked this step as being used
-- so it must exist.
let step := ( M.getProofStep id).get!
let newStep M.mapStep step
newProof := newProof.push newStep
nextMapped := nextMapped + 1
let emptyId M.getEmptyId
let initialId M.getInitialId
let mut nextMapped := initialId
let mut newProof := #[]
for id in [initialId:emptyId+1] do
if M.isUsed id then
M.registerIdMap id nextMapped
-- This should never panic as the use def analysis has already marked this step as being used
-- so it must exist.
let step := ( M.getProofStep id).get!
let newStep M.mapStep step
newProof := newProof.push newStep
nextMapped := nextMapped + 1
return newProof
def go : M (Array IntAction) := do
@@ -207,7 +220,7 @@ end trim
Trim the LRAT `proof` by removing all steps that are not used in reaching the empty clause
conclusion.
-/
def trim (proof : Array IntAction) : Except String (Array IntAction) :=
def trim (proof : Array IntAction) : Except String (Array IntAction) := do
trim.go.run proof
end LRAT

View File

@@ -224,6 +224,8 @@ where
guard <| state.term.meta.core.traceState.traces.size == 0
guard <| traceState.traces.size == 0
return old.val.get
if snap.old?.isSome && old?.isNone then
snap.old?.forM (·.val.cancelRec)
let promise IO.Promise.new
-- Store new unfolding in the snapshot tree
snap.new.resolve {
@@ -233,6 +235,7 @@ where
finished := .finished stx' {
diagnostics := .empty
state? := ( Tactic.saveState)
moreSnaps := #[]
}
next := #[{ stx? := stx', task := promise.resultD default }]
}

View File

@@ -101,6 +101,7 @@ where
( Core.getAndEmptyMessageLog))
infoTree? := ( Term.getInfoTreeWithContext?)
state? := state
moreSnaps := ( Core.getAndEmptySnapshotTasks)
}
finally
modifyInfoState fun s => { s with trees := trees ++ s.trees }

View File

@@ -173,6 +173,10 @@ partial def mkElimApp (elimInfo : ElimInfo) (targets : Array Expr) (tag : Name)
addNewArg arg
loop
| _ =>
let s get
let ctx read
unless s.targetPos = ctx.targets.size do
throwError "unexpected number of targets for '{elimInfo.elimExpr}'"
pure ()
let (_, s) (loop).run { elimInfo := elimInfo, targets := targets }
|>.run { f := elimInfo.elimExpr, fType := elimInfo.elimType, motive := none }
@@ -303,7 +307,11 @@ where
return old.stx, ( old.next[i]?)
new := prom
}
finished.resolve { diagnostics := .empty, state? := ( saveState) }
finished.resolve {
diagnostics := .empty
state? := ( saveState)
moreSnaps := ( Core.getAndEmptySnapshotTasks)
}
return
goWithIncremental #[]

View File

@@ -24,28 +24,70 @@ Implementation of the `exact?` tactic.
def exact? (ref : Syntax) (required : Option (Array (TSyntax `term))) (requireClose : Bool) :
TacticM Unit := do
let mvar getMainGoal
let initialState saveState
let (_, goal) ( getMainGoal).intros
goal.withContext do
let required := ( (required.getD #[]).mapM getFVarId).toList.map .fvar
let tactic := fun exfalso =>
solveByElim required (exfalso := exfalso) (maxDepth := 6)
solveByElim required (exfalso := exfalso) (maxDepth := 6)
let allowFailure := fun g => do
let g g.withContext (instantiateMVars (.mvar g))
return required.all fun e => e.occurs g
match librarySearch goal tactic allowFailure with
match ( librarySearch goal tactic allowFailure) with
-- Found goal that closed problem
| none =>
addExactSuggestion ref ( instantiateMVars (mkMVar mvar)).headBeta
addSuggestionIfValid ref mvar initialState
-- Found suggestions
| some suggestions =>
if requireClose then throwError
"`exact?` could not close the goal. Try `apply?` to see partial suggestions."
if requireClose then
let hint := if suggestions.isEmpty then "" else " Try `apply?` to see partial suggestions."
throwError "`exact?` could not close the goal.{hint}"
reportOutOfHeartbeats `apply? ref
for (_, suggestionMCtx) in suggestions do
withMCtx suggestionMCtx do
addExactSuggestion ref ( instantiateMVars (mkMVar mvar)).headBeta (addSubgoalsMsg := true)
addSuggestionIfValid ref mvar initialState (addSubgoalsMsg := true) (errorOnInvalid := false)
if suggestions.isEmpty then logError "apply? didn't find any relevant lemmas"
admitGoal goal
where
/--
Executes `tac` in `savedState` (then restores the current state). Used to ensure that a suggested
tactic is valid.
Remark: we don't merely elaborate the proof term's syntax because it may successfully round-trip
(d)elaboration but still produce an invalid tactic (see the example in #5407).
-/
evalTacticWithState (savedState : Tactic.SavedState) (tac : TSyntax `tactic) : TacticM Unit := do
let currState saveState
savedState.restore
try
Term.withoutErrToSorry <| withoutRecover <| evalTactic tac
finally
currState.restore
/--
Suggests using the value of `goal` as a proof term if the corresponding tactic is valid at
`origGoal`, or else informs the user that a proof exists but is not syntactically valid.
-/
addSuggestionIfValid (ref : Syntax) (goal : MVarId) (initialState : Tactic.SavedState)
(addSubgoalsMsg := false) (errorOnInvalid := true) : TacticM Unit := do
let proofExpr := ( instantiateMVars (mkMVar goal)).headBeta
let proofMVars getMVars proofExpr
let hasMVars := !proofMVars.isEmpty
let suggestion mkExactSuggestionSyntax proofExpr (useRefine := hasMVars) (exposeNames := false)
let mut exposeNames := false
try evalTacticWithState initialState suggestion
catch _ =>
exposeNames := true
let suggestion' mkExactSuggestionSyntax proofExpr (useRefine := hasMVars) (exposeNames := true)
try evalTacticWithState initialState suggestion'
catch _ =>
let suggestionStr SuggestionText.prettyExtra suggestion
-- Pretty-print the version without `expose_names` so variable names match the Infoview
let msg := m!"found a {if hasMVars then "partial " else ""}proof, \
but the corresponding tactic failed:{indentD suggestionStr}"
if errorOnInvalid then throwError msg else logInfo msg
return
addExactSuggestion ref proofExpr (addSubgoalsMsg := addSubgoalsMsg) (exposeNames := exposeNames)
@[builtin_tactic Lean.Parser.Tactic.exact?]
def evalExact : Tactic := fun stx => do
@@ -69,7 +111,7 @@ def elabExact?Term : TermElab := fun stx expectedType? => do
introdGoal.withContext do
if let some suggestions librarySearch introdGoal then
if suggestions.isEmpty then logError "`exact?%` didn't find any relevant lemmas"
else logError "`exact?%` could not close the goal. Try `by apply` to see partial suggestions."
else logError "`exact?%` could not close the goal. Try `by apply?` to see partial suggestions."
mkLabeledSorry expectedType (synthetic := true) (unique := true)
else
addTermSuggestion stx ( instantiateMVars goal).headBeta

View File

@@ -48,14 +48,6 @@ private def isExprAccessible (e : Expr) : MetaM Bool := do
let (_, s) e.collectFVars |>.run {}
s.fvarIds.allM isAccessible
/-- Creates a temporary local context where all names are exposed, and executes `k`-/
private def withExposedNames (k : MetaM α) : MetaM α := do
withNewMCtxDepth do
-- Create a helper goal to apply
let mvarId := ( mkFreshExprMVar (mkConst ``True)).mvarId!
let mvarId mvarId.exposeNames
mvarId.withContext do k
/-- Executes `tac` in the saved state. This function is used to validate a tactic before suggesting it. -/
def checkTactic (savedState : SavedState) (tac : TSyntax `tactic) : TacticM Unit := do
let currState saveState

View File

@@ -227,9 +227,11 @@ structure SavedState where
structure TacticFinishedSnapshot extends Language.Snapshot where
/-- State saved for reuse, if no fatal exception occurred. -/
state? : Option SavedState
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
moreSnaps : Array (SnapshotTask SnapshotTree)
deriving Inhabited
instance : ToSnapshotTree TacticFinishedSnapshot where
toSnapshotTree s := s.toSnapshot, #[]
toSnapshotTree s := s.toSnapshot, s.moreSnaps
/-- Snapshot just before execution of a tactic. -/
structure TacticParsedSnapshot extends Language.Snapshot where
@@ -256,10 +258,6 @@ namespace Term
structure Context where
declName? : Option Name := none
/--
Map `.auxDecl` local declarations used to encode recursive declarations to their full-names.
-/
auxDeclToFullName : FVarIdMap Name := {}
macroStack : MacroStack := []
/--
When `mayPostpone == true`, an elaboration function may interrupt its execution by throwing `Exception.postpone`.
@@ -421,13 +419,17 @@ part. `act` is then run on the inner part but with reuse information adjusted as
context.
For any tactic that participates in reuse, `withNarrowedTacticReuse` should be applied to the
tactic's syntax and `act` should be used to do recursive tactic evaluation of nested parts.
tactic's syntax and `act` should be used to do recursive tactic evaluation of nested parts. Also,
after this function, `getAndEmptySnapshotTasks` should be called and the result stored in a snapshot
so that the tasks don't end up in a snapshot further up and are cancelled together with it; see
note [Incremental Cancellation].
-/
def withNarrowedTacticReuse [Monad m] [MonadWithReaderOf Core.Context m]
[MonadWithReaderOf Context m] [MonadOptions m] (split : Syntax Syntax × Syntax)
(act : Syntax m α) (stx : Syntax) : m α := do
def withNarrowedTacticReuse [Monad m] [MonadReaderOf Context m] [MonadLiftT BaseIO m]
[MonadWithReaderOf Core.Context m] [MonadWithReaderOf Context m] [MonadOptions m]
(split : Syntax Syntax × Syntax) (act : Syntax m α) (stx : Syntax) : m α := do
let (outer, inner) := split stx
let opts getOptions
let ctx readThe Term.Context
withTheReader Term.Context (fun ctx => { ctx with tacSnap? := ctx.tacSnap?.map fun tacSnap =>
{ tacSnap with old? := tacSnap.old?.bind fun old => do
let (oldOuter, oldInner) := split old.stx
@@ -435,6 +437,9 @@ def withNarrowedTacticReuse [Monad m] [MonadWithReaderOf Core.Context m]
return { old with stx := oldInner }
}
}) do
if let some oldOuter := ctx.tacSnap?.bind (·.old?) then
if ( read).tacSnap?.bind (·.old?) |>.isNone then
oldOuter.val.cancelRec
withReuseContext inner (act inner)
/--
@@ -446,8 +451,9 @@ NOTE: child nodes after `argIdx` are not tested (which would almost always disab
necessarily shifted by changes at `argIdx`) so it must be ensured that the result of `arg` does not
depend on them (i.e. they should not be inspected beforehand).
-/
def withNarrowedArgTacticReuse [Monad m] [MonadWithReaderOf Core.Context m] [MonadWithReaderOf Context m]
[MonadOptions m] (argIdx : Nat) (act : Syntax m α) (stx : Syntax) : m α :=
def withNarrowedArgTacticReuse [Monad m] [MonadReaderOf Context m] [MonadLiftT BaseIO m]
[MonadWithReaderOf Core.Context m] [MonadWithReaderOf Context m] [MonadOptions m]
(argIdx : Nat) (act : Syntax m α) (stx : Syntax) : m α :=
withNarrowedTacticReuse (fun stx => (mkNullNode stx.getArgs[:argIdx], stx[argIdx])) act stx
/--
@@ -617,15 +623,6 @@ def withLevelNames (levelNames : List Name) (x : TermElabM α) : TermElabM α :=
setLevelNames levelNames
try x finally setLevelNames levelNamesSaved
/--
Declare an auxiliary local declaration `shortDeclName : type` for elaborating recursive declaration `declName`,
update the mapping `auxDeclToFullName`, and then execute `k`.
-/
def withAuxDecl (shortDeclName : Name) (type : Expr) (declName : Name) (k : Expr TermElabM α) : TermElabM α :=
withLocalDecl shortDeclName .default (kind := .auxDecl) type fun x =>
withReader (fun ctx => { ctx with auxDeclToFullName := ctx.auxDeclToFullName.insert x.fvarId! declName }) do
k x
def withoutErrToSorryImp (x : TermElabM α) : TermElabM α :=
withReader (fun ctx => { ctx with errToSorry := false }) x
@@ -1512,169 +1509,6 @@ def blockImplicitLambda (stx : Syntax) : Bool :=
isExplicit stx || isExplicitApp stx || isLambdaWithImplicit stx || isHole stx || isTacticBlock stx ||
isNoImplicitLambda stx || isTypeAscription stx
def resolveLocalName (n : Name) : TermElabM (Option (Expr × List String)) := do
let lctx getLCtx
let auxDeclToFullName := ( read).auxDeclToFullName
let currNamespace getCurrNamespace
let view := extractMacroScopes n
/- Simple case. "Match" function for regular local declarations. -/
let matchLocalDecl? (localDecl : LocalDecl) (givenName : Name) : Option LocalDecl := do
guard (localDecl.userName == givenName)
return localDecl
/-
"Match" function for auxiliary declarations that correspond to recursive definitions being defined.
This function is used in the first-pass.
Note that we do not check for `localDecl.userName == givenName` in this pass as we do for regular local declarations.
Reason: consider the following example
```
mutual
inductive Foo
| somefoo : Foo | bar : Bar → Foo → Foo
inductive Bar
| somebar : Bar| foobar : Foo → Bar → Bar
end
mutual
private def Foo.toString : Foo → String
| Foo.somefoo => go 2 ++ toString.go 2 ++ Foo.toString.go 2
| Foo.bar b f => toString f ++ Bar.toString b
where
go (x : Nat) := s!"foo {x}"
private def _root_.Ex2.Bar.toString : Bar → String
| Bar.somebar => "bar"
| Bar.foobar f b => Foo.toString f ++ Bar.toString b
end
```
In the example above, we have two local declarations named `toString` in the local context, and
we want the `toString f` to be resolved to `Foo.toString f`.
-/
let matchAuxRecDecl? (localDecl : LocalDecl) (fullDeclName : Name) (givenNameView : MacroScopesView) : Option LocalDecl := do
let fullDeclView := extractMacroScopes fullDeclName
/- First cleanup private name annotations -/
let fullDeclView := { fullDeclView with name := (privateToUserName? fullDeclView.name).getD fullDeclView.name }
let fullDeclName := fullDeclView.review
let localDeclNameView := extractMacroScopes localDecl.userName
/- If the current namespace is a prefix of the full declaration name,
we use a relaxed matching test where we must satisfy the following conditions
- The local declaration is a suffix of the given name.
- The given name is a suffix of the full declaration.
Recall the `let rec`/`where` declaration naming convention. For example, suppose we have
```
def Foo.Bla.f ... :=
... go ...
where
go ... := ...
```
The current namespace is `Foo.Bla`, and the full name for `go` is `Foo.Bla.f.g`, but we want to
refer to it using just `go`. It is also accepted to refer to it using `f.go`, `Bla.f.go`, etc.
-/
if currNamespace.isPrefixOf fullDeclName then
/- Relaxed mode that allows us to access `let rec` declarations using shorter names -/
guard (localDeclNameView.isSuffixOf givenNameView)
guard (givenNameView.isSuffixOf fullDeclView)
return localDecl
else
/-
It is the standard algorithm we are using at `resolveGlobalName` for processing namespaces.
The current solution also has a limitation when using `def _root_` in a mutual block.
The non `def _root_` declarations may update the namespace. See the following example:
```
mutual
def Foo.f ... := ...
def _root_.g ... := ...
let rec h := ...
...
end
```
`def Foo.f` updates the namespace. Then, even when processing `def _root_.g ...`
the condition `currNamespace.isPrefixOf fullDeclName` does not hold.
This is not a big problem because we are planning to modify how we handle the mutual block in the future.
Note that we don't check for `localDecl.userName == givenName` here.
-/
let rec go (ns : Name) : Option LocalDecl := do
if { givenNameView with name := ns ++ givenNameView.name }.review == fullDeclName then
return localDecl
match ns with
| .str pre .. => go pre
| _ => failure
return ( go currNamespace)
/- Traverse the local context backwards looking for match `givenNameView`.
If `skipAuxDecl` we ignore `auxDecl` local declarations. -/
let findLocalDecl? (givenNameView : MacroScopesView) (skipAuxDecl : Bool) : Option LocalDecl :=
let givenName := givenNameView.review
let localDecl? := lctx.decls.findSomeRev? fun localDecl? => do
let localDecl localDecl?
if localDecl.isAuxDecl then
guard (!skipAuxDecl)
if let some fullDeclName := auxDeclToFullName.find? localDecl.fvarId then
matchAuxRecDecl? localDecl fullDeclName givenNameView
else
matchLocalDecl? localDecl givenName
else
matchLocalDecl? localDecl givenName
if localDecl?.isSome || skipAuxDecl then
localDecl?
else
-- Search auxDecls again trying an exact match of the given name
lctx.decls.findSomeRev? fun localDecl? => do
let localDecl localDecl?
guard localDecl.isAuxDecl
matchLocalDecl? localDecl givenName
/-
We use the parameter `globalDeclFound` to decide whether we should skip auxiliary declarations or not.
We set it to true if we found a global declaration `n` as we iterate over the `loop`.
Without this workaround, we would not be able to elaborate an example such as
```
def foo.aux := 1
def foo : Nat → Nat
| n => foo.aux -- should not be interpreted as `(foo).aux`
```
See test `aStructPerfIssue.lean` for another example.
We skip auxiliary declarations when `projs` is not empty and `globalDeclFound` is true.
Remark: we did not use to have the `globalDeclFound` parameter. Without this extra check we failed
to elaborate
```
example : Nat :=
let n := 0
n.succ + (m |>.succ) + m.succ
where
m := 1
```
See issue #1850.
-/
let rec loop (n : Name) (projs : List String) (globalDeclFound : Bool) := do
let givenNameView := { view with name := n }
let mut globalDeclFoundNext := globalDeclFound
unless globalDeclFound do
let r resolveGlobalName givenNameView.review
let r := r.filter fun (_, fieldList) => fieldList.isEmpty
unless r.isEmpty do
globalDeclFoundNext := true
/-
Note that we use `globalDeclFound` instead of `globalDeclFoundNext` in the following test.
Reason: a local should shadow a global with the same name.
Consider the following example. See issue #3079
```
def foo : Nat := 1
def bar : Nat :=
foo.add 1 -- should be 11
where
foo := 10
```
-/
match findLocalDecl? givenNameView (skipAuxDecl := globalDeclFound && !projs.isEmpty) with
| some decl => return some (decl.toExpr, projs)
| none => match n with
| .str pre s => loop pre (s::projs) globalDeclFoundNext
| _ => return none
loop view.name [] (globalDeclFound := false)
/-- Return true iff `stx` is a `Syntax.ident`, and it is a local variable. -/
def isLocalIdent? (stx : Syntax) : TermElabM (Option Expr) :=
match stx with

View File

@@ -18,8 +18,10 @@ import Lean.Util.Path
import Lean.Util.FindExpr
import Lean.Util.Profile
import Lean.Util.InstantiateLevelParams
import Lean.Util.FoldConsts
import Lean.PrivateName
import Lean.LoadDynlib
import Init.Dynamic
/-!
# Note [Environment Branches]
@@ -65,6 +67,12 @@ paths back together.
-/
namespace Lean
register_builtin_option debug.skipKernelTC : Bool := {
defValue := false
group := "debug"
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
}
/-- Opaque environment extension state. -/
opaque EnvExtensionStateSpec : (α : Type) × Inhabited α := Unit, ()
def EnvExtensionState : Type := EnvExtensionStateSpec.fst
@@ -252,6 +260,28 @@ inductive Exception where
| excessiveMemory
| deepRecursion
| interrupted
deriving Nonempty
/-- Basic `Exception` formatting without `MessageData` dependency. -/
private def Exception.toRawString : Kernel.Exception String
| unknownConstant _ constName => s!"(kernel) unknown constant '{constName}'"
| alreadyDeclared _ constName => s!"(kernel) constant has already been declared '{constName}'"
| declTypeMismatch _ _ _ => s!"(kernel) declaration type mismatch"
| declHasMVars _ constName _ => s!"(kernel) declaration has metavariables '{constName}'"
| declHasFVars _ constName _ => s!"(kernel) declaration has free variables '{constName}'"
| funExpected _ _ e => s!"(kernel) function expected: {e}"
| typeExpected _ _ e => s!"(kernel) type expected: {e}"
| letTypeMismatch _ _ n _ _ => s!"(kernel) let-declaration type mismatch '{n}'"
| exprTypeMismatch _ _ e _ => s!"(kernel) type mismatch at {e}"
| appTypeMismatch _ _ e fnType argType =>
s!"application type mismatch: {e}\nargument has type {argType}\nbut function has type {fnType}"
| invalidProj _ _ e => s!"(kernel) invalid projection {e}"
| thmTypeIsNotProp _ constName type => s!"(kernel) type of theorem '{constName}' is not a proposition: {type}"
| other msg => s!"(kernel) {msg}"
| deterministicTimeout => "(kernel) deterministic timeout"
| excessiveMemory => "(kernel) excessive memory consumption detected"
| deepRecursion => "(kernel) deep recursion detected"
| interrupted => "(kernel) interrupted"
namespace Environment
@@ -346,6 +376,7 @@ structure AsyncConstantInfo where
sig : Task ConstantVal
/-- The final, complete constant info, potentially filled asynchronously. -/
constInfo : Task ConstantInfo
deriving Inhabited
namespace AsyncConstantInfo
@@ -365,21 +396,25 @@ end AsyncConstantInfo
/--
Information about the current branch of the environment representing asynchronous elaboration.
Use `Environment.enterAsync` instead of `mkRaw`.
-/
structure AsyncContext where
private structure AsyncContext where mkRaw ::
/--
Name of the declaration asynchronous elaboration was started for. All constants added to this
environment branch must have the name as a prefix, after erasing macro scopes and private name
prefixes.
-/
declPrefix : Name
/-- Whether we are in `realizeConst`, used to restrict env ext modifications. -/
realizing : Bool
deriving Nonempty
/--
Checks whether a declaration named `n` may be added to the environment in the given context. See
also `AsyncContext.declPrefix`.
-/
def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
private def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
ctx.declPrefix.isPrefixOf <| privateToUserName n.eraseMacroScopes
/--
@@ -394,28 +429,50 @@ structure AsyncConst where
exts? : Option (Task (Array EnvExtensionState))
/-- Data structure holding a sequence of `AsyncConst`s optimized for efficient access. -/
structure AsyncConsts where
toArray : Array AsyncConst := #[]
private structure AsyncConsts where
size : Nat
revList : List AsyncConst
/-- Map from declaration name to const for fast direct access. -/
private map : NameMap AsyncConst := {}
map : NameMap AsyncConst
/-- Trie of declaration names without private name prefixes for fast longest-prefix access. -/
private normalizedTrie : NameTrie AsyncConst := {}
normalizedTrie : NameTrie AsyncConst
deriving Inhabited
def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
{ aconsts with
toArray := aconsts.toArray.push aconst
private def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
let normalizedName := privateToUserName aconst.constInfo.name
if let some aconst' := aconsts.normalizedTrie.find? normalizedName then
panic! s!"AsyncConsts.add: duplicate normalized declaration name {aconst.constInfo.name} vs. {aconst'.constInfo.name}"
else { aconsts with
size := aconsts.size + 1
revList := aconst :: aconsts.revList
map := aconsts.map.insert aconst.constInfo.name aconst
normalizedTrie := aconsts.normalizedTrie.insert (privateToUserName aconst.constInfo.name) aconst
normalizedTrie := aconsts.normalizedTrie.insert normalizedName aconst
}
def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
private def AsyncConsts.find? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
aconsts.map.find? declName
/-- Finds the constant in the collection that is a prefix of `declName`, if any. -/
def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
-- as macro scopes are a strict suffix,
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName.eraseMacroScopes)
private def AsyncConsts.findPrefix? (aconsts : AsyncConsts) (declName : Name) : Option AsyncConst :=
-- as macro scopes are a strict suffix, we do not have to remove them before calling
-- `findLongestPrefix?`
aconsts.normalizedTrie.findLongestPrefix? (privateToUserName declName)
/-- Context for `realizeConst` established by `enableRealizationsForConst`. -/
private structure RealizationContext where
/--
Saved `Environment`, untyped to avoid cyclic reference. Import environment for imported constants.
-/
env : NonScalar
/-- Saved options. Empty for imported constants. -/
opts : Options
/--
`realizeConst _ c ..` adds a mapping from `c` to a task of the realization results: the newly
added constants (incl. extension data in `AsyncConst.exts?`), a function for replaying the
changes onto a derived kernel environment, and auxiliary data (always `SnapshotTree` in builtin
uses, but untyped to avoid cyclic module references).
-/
constsRef : IO.Ref (NameMap (Task (List AsyncConst × (Kernel.Environment Kernel.Environment) × Dynamic)))
/--
Elaboration-specific extension of `Kernel.Environment` that adds tracking of asynchronously
@@ -443,19 +500,32 @@ structure Environment where
-/
checked : Task Kernel.Environment := .pure checkedWithoutAsync
/--
Container of asynchronously elaborated declarations, i.e.
`checked = checkedWithoutAsync ⨃ asyncConsts`.
Container of asynchronously elaborated declarations. For consistency, `updateBaseAfterKernelAdd`
makes sure this contains constants added even synchronously, i.e. this is a superset of
`checkedWithoutAsync` except for imported constants.
-/
private asyncConsts : AsyncConsts := {}
private asyncConsts : AsyncConsts := default
/-- Information about this asynchronous branch of the environment, if any. -/
private asyncCtx? : Option AsyncContext := none
/--
Realized constants belonging to imported declarations. Must be initialized by calling
`enableRealizationsForImports`.
-/
private realizedImportedConsts? : Option RealizationContext
/--
Realized constants belonging to local declarations. This is a map from local declarations, which
need to be registered synchronously using `enableRealizationsForConst`, to their realization
context incl. a ref of realized constants.
-/
private realizedLocalConsts : NameMap RealizationContext := {}
deriving Nonempty
namespace Environment
-- used only when the kernel calls into the interpreter, and in `Lean.Kernel.Exception.mkCtx`
@[export lean_elab_environment_of_kernel_env]
def ofKernelEnv (env : Kernel.Environment) : Environment :=
{ checkedWithoutAsync := env }
{ checkedWithoutAsync := env, realizedImportedConsts? := none }
@[export lean_elab_environment_to_kernel_env]
def toKernelEnv (env : Environment) : Kernel.Environment :=
@@ -469,6 +539,10 @@ private def modifyCheckedAsync (env : Environment) (f : Kernel.Environment → K
private def setCheckedSync (env : Environment) (newChecked : Kernel.Environment) : Environment :=
{ env with checked := .pure newChecked, checkedWithoutAsync := newChecked }
/-- True while inside `realizeConst`'s `realize`. -/
def isRealizing (env : Environment) : Bool :=
env.asyncCtx?.any (·.realizing)
/--
Checks whether the given declaration name may potentially added, or have been added, to the current
environment branch, which is the case either if this is the main branch or if the declaration name
@@ -550,7 +624,7 @@ private def findNoAsync (env : Environment) (n : Name) : Option ConstantInfo :=
/--
Looks up the given declaration name in the environment, avoiding forcing any in-progress elaboration
tasks.
tasks unless necessary.
-/
def findAsync? (env : Environment) (n : Name) : Option AsyncConstantInfo := do
-- Check declarations already added to the kernel environment (e.g. because they were imported)
@@ -574,6 +648,45 @@ def findConstVal? (env : Environment) (n : Name) : Option ConstantVal := do
return asyncConst.constInfo.toConstantVal
else env.findNoAsync n |>.map (·.toConstantVal)
/--
Allows `realizeConst` calls for imported declarations in all derived environment branches.
Realizations will run using the given environment and options to ensure deterministic results.
This function should be called directly after `setMainModule` to ensure that all realized constants
use consistent private prefixes.
-/
def enableRealizationsForImports (env : Environment) (opts : Options) : BaseIO Environment :=
return { env with realizedImportedConsts? := some {
-- safety: `RealizationContext` is private
env := unsafe unsafeCast env
opts
constsRef := ( IO.mkRef {})
}
}
/--
Allows `realizeConst` calls for the given declaration in all derived environment branches.
Realizations will run using the given environment and options to ensure deterministic results. Note
that while we check that the function isn't called too *early*, i.e. before the declaration is
actually added to the environment, we cannot automatically check that it isn't called too *late*,
i.e. before all environment extensions that may be relevant to realizations have been set. We do
check that we are not calling it from a different branch than `c` was added on, which would be
definitely too late.
-/
def enableRealizationsForConst (env : Environment) (opts : Options) (c : Name) :
BaseIO Environment := do
if env.findAsync? c |>.isNone then
panic! s!"Environment.enableRealizationsForConst: declaration {c} not found in environment"
if let some asyncCtx := env.asyncCtx? then
if !asyncCtx.mayContain c then
panic! s!"Environment.enableRealizationsForConst: {c} is outside current context {asyncCtx.declPrefix}"
if env.realizedLocalConsts.contains c then
return env
return { env with realizedLocalConsts := env.realizedLocalConsts.insert c {
-- safety: `RealizationContext` is private
env := unsafe unsafeCast env
opts
constsRef := ( IO.mkRef {}) } }
/--
Looks up the given declaration name in the environment, blocking on the corresponding elaboration
task if not yet complete.
@@ -590,9 +703,14 @@ def find? (env : Environment) (n : Name) : Option ConstantInfo :=
def dbgFormatAsyncState (env : Environment) : BaseIO String :=
return s!"\
asyncCtx.declPrefix: {repr <| env.asyncCtx?.map (·.declPrefix)}\
\nasyncConsts: {repr <| env.asyncConsts.toArray.map (·.constInfo.name)}\
\ncheckedWithoutAsync.constants.map₂: {repr <|
env.checkedWithoutAsync.constants.map₂.toList.map (·.1)}"
\nasyncConsts: {repr <| env.asyncConsts.revList.reverse.map (·.constInfo.name)}\
\nrealizedLocalConsts: {repr (← env.realizedLocalConsts.toList.mapM fun (n, ctx) => do
let consts := (← ctx.constsRef.get).toList
return (n, consts.map (·.1)))}
\nrealizedImportedConsts?: {repr <| (← env.realizedImportedConsts?.mapM fun ctx => do
return (← ctx.constsRef.get).toList.map fun (n, m?) =>
(n, m?.get.1.map (fun c : AsyncConst => c.constInfo.name.toString) |> toString))}
\ncheckedWithoutAsync.constants.map₂: {repr <| env.checkedWithoutAsync.constants.map₂.toList.map (·.1)}"
/-- Returns debug output about the synchronous state of the environment. -/
def dbgFormatCheckedSyncState (env : Environment) : BaseIO String :=
@@ -614,6 +732,13 @@ structure PromiseCheckedResult where
asyncEnv : Environment
private checkedEnvPromise : IO.Promise Kernel.Environment
/-- Creates an async context for the given declaration name, normalizing it for use as a prefix. -/
private def enterAsync (declName : Name) (realizing := false) (env : Environment) : Environment :=
{ env with asyncCtx? := some {
declPrefix := privateToUserName declName.eraseMacroScopes
-- `realizing` is sticky
realizing := realizing || env.asyncCtx?.any (·.realizing) } }
/--
Starts an asynchronous modification of the kernel environment. The environment is split into a
"main" branch that will block on access to the kernel environment until
@@ -626,10 +751,8 @@ def promiseChecked (env : Environment) : BaseIO PromiseCheckedResult := do
checked := checkedEnvPromise.result?.bind (sync := true) fun
| some kenv => .pure kenv
| none => env.checked }
asyncEnv := { env with
-- Do not allow adding new constants
asyncCtx? := some { declPrefix := `__reserved__Environment_promiseChecked }
}
-- Do not allow adding new constants
asyncEnv := env.enterAsync `__reserved__Environment_promiseChecked
checkedEnvPromise
}
@@ -664,28 +787,14 @@ structure AddConstAsyncResult where
private extensionsPromise : IO.Promise (Array EnvExtensionState)
private checkedEnvPromise : IO.Promise Kernel.Environment
/--
Starts the asynchronous addition of a constant to the environment. The environment is split into a
"main" branch that holds a reference to the constant to be added but will block on access until the
corresponding information has been added on the "async" environment branch and committed there; see
the respective fields of `AddConstAsyncResult` as well as the [Environment Branches] note for more
information.
-/
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
IO AddConstAsyncResult := do
assert! env.asyncMayContain constName
let sigPromise IO.Promise.new
let infoPromise IO.Promise.new
let extensionsPromise IO.Promise.new
let checkedEnvPromise IO.Promise.new
-- fallback info in case promises are dropped unfulfilled
let fallbackVal := {
/-- Creates fallback info to be used in case promises are dropped unfulfilled. -/
private def mkFallbackConstInfo (constName : Name) (kind : ConstantKind) : ConstantInfo :=
let fallbackVal : ConstantVal := {
name := constName
levelParams := []
type := mkApp2 (mkConst ``sorryAx [0]) (mkSort 0) (mkConst ``true)
type := mkApp2 (mkConst ``sorryAx [1]) (mkSort 0) (mkConst ``true)
}
let fallbackInfo := match kind with
match kind with
| .defn => .defnInfo { fallbackVal with
value := mkApp2 (mkConst ``sorryAx [0]) fallbackVal.type (mkConst ``true)
hints := .abbrev
@@ -697,16 +806,38 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
| .axiom => .axiomInfo { fallbackVal with
isUnsafe := false
}
| k => panic! s!"AddConstAsyncResult.addConstAsync: unsupported constant kind {repr k}"
| k => panic! s!"Environment.mkFallbackConstInfo: unsupported constant kind {repr k}"
/--
Starts the asynchronous addition of a constant to the environment. The environment is split into a
"main" branch that holds a reference to the constant to be added but will block on access until the
corresponding information has been added on the "async" environment branch and committed there; see
the respective fields of `AddConstAsyncResult` as well as the [Environment Branches] note for more
information.
-/
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true)
(checkMayContain := true) :
IO AddConstAsyncResult := do
if checkMayContain then
if let some ctx := env.asyncCtx? then
if !ctx.mayContain constName then
throw <| .userError s!"cannot add declaration {constName} to environment as it is \
restricted to the prefix {ctx.declPrefix}"
let sigPromise IO.Promise.new
let infoPromise IO.Promise.new
let extensionsPromise IO.Promise.new
let checkedEnvPromise IO.Promise.new
let fallbackConstInfo := mkFallbackConstInfo constName kind
let asyncConst := {
constInfo := {
name := constName
kind
sig := sigPromise.resultD fallbackVal
constInfo := infoPromise.resultD fallbackInfo
sig := sigPromise.resultD fallbackConstInfo.toConstantVal
constInfo := infoPromise.resultD fallbackConstInfo
}
exts? := guard reportExts *> some (extensionsPromise.resultD #[])
exts? := guard reportExts *> some (extensionsPromise.resultD env.toKernelEnv.extensions)
}
return {
constName, kind
@@ -715,9 +846,7 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
checked := checkedEnvPromise.result?.bind (sync := true) fun
| some kenv => .pure kenv
| none => env.checked }
asyncEnv := { env with
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
}
asyncEnv := env.enterAsync constName
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
}
@@ -773,6 +902,13 @@ def AddConstAsyncResult.commitCheckEnv (res : AddConstAsyncResult) (env : Enviro
def contains (env : Environment) (n : Name) : Bool :=
env.findAsync? n |>.isSome
/--
Checks whether the given declaration is known on the current branch, in which case `findAsync?` will
not block.
-/
def containsOnBranch (env : Environment) (n : Name) : Bool :=
(env.asyncConsts.find? n |>.isSome) || env.checkedWithoutAsync.constants.contains n
def header (env : Environment) : EnvironmentHeader :=
-- can be assumed to be in sync with `env.checked`; see `setMainModule`, the only modifier of the header
env.checkedWithoutAsync.header
@@ -783,7 +919,10 @@ def imports (env : Environment) : Array Import :=
def allImportedModuleNames (env : Environment) : Array Name :=
env.header.moduleNames
def setMainModule (env : Environment) (m : Name) : Environment :=
def setMainModule (env : Environment) (m : Name) : Environment := Id.run do
if env.realizedImportedConsts?.isSome then
panic! "Environment.setMainModule: cannot set after `enableRealizationsForImports`"
return env
env.modifyCheckedAsync ({ · with header.mainModule := m })
def mainModule (env : Environment) : Name :=
@@ -880,6 +1019,9 @@ inductive EnvExtension.AsyncMode where
| async
deriving Inhabited
abbrev ReplayFn (σ : Type) :=
(oldState : σ) (newState : σ) (newConsts : List Name) σ σ
/--
Environment extension, can only be generated by `registerEnvExtension` that allocates a unique index
for this extension into each environment's extension state's array.
@@ -888,6 +1030,13 @@ structure EnvExtension (σ : Type) where private mk ::
idx : Nat
mkInitial : IO σ
asyncMode : EnvExtension.AsyncMode
/--
Optional function that, given state before and after realization and newly added constants,
replays this change onto a state from another (derived) environment. This function is used only
when making changes to an extension inside a `realizeConst` call, in which case it must be
present.
-/
replay? : Option (ReplayFn σ)
deriving Inhabited
namespace EnvExtension
@@ -949,19 +1098,21 @@ from different environment branches are reconciled.
Note that in modes `sync` and `async`, `f` will be called twice, on the local and on the `checked`
state.
-/
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ σ) : Environment :=
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ σ) : Environment := Id.run do
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
match ext.asyncMode with
| .mainOnly =>
if let some asyncCtx := env.asyncCtx? then
let _ : Inhabited Environment := env
panic! s!"Environment.modifyState: environment extension is marked as `mainOnly` but used in \
async context '{asyncCtx.declPrefix}'"
else
{ env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
{if asyncCtx.realizing then "realization" else "async"} context '{asyncCtx.declPrefix}'"
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
| .local =>
{ env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
return { env with checkedWithoutAsync.extensions := unsafe ext.modifyStateImpl env.checkedWithoutAsync.extensions f }
| _ =>
if ext.replay?.isNone then
if let some asyncCtx := env.asyncCtx?.filter (·.realizing) then
panic! s!"Environment.modifyState: environment extension must set `replay?` field to be \
used in realization context '{asyncCtx.declPrefix}'"
env.modifyCheckedAsync fun env =>
{ env with extensions := unsafe ext.modifyStateImpl env.extensions f }
@@ -992,6 +1143,24 @@ recommended and should be considered only for important optimizations.
opaque getState {σ : Type} [Inhabited σ] (ext : EnvExtension σ) (env : Environment)
(asyncMode := ext.asyncMode) : σ
-- `unsafe` fails to infer `Nonempty` here
private unsafe def findStateAsyncUnsafe {σ : Type} [Inhabited σ]
(ext : EnvExtension σ) (env : Environment) (declPrefix : Name) : σ :=
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
if let some { exts? := some exts, .. } := env.asyncConsts.findPrefix? declPrefix then
ext.getStateImpl exts.get
else
ext.getStateImpl env.checkedWithoutAsync.extensions
/--
Returns the final extension state on the environment branch corresponding to the passed declaration
name, if any, or otherwise the state on the current branch. In other words, at most one environment
branch will be blocked on.
-/
@[implemented_by findStateAsyncUnsafe]
opaque findStateAsync {σ : Type} [Inhabited σ] (ext : EnvExtension σ)
(env : Environment) (declPrefix : Name) : σ
end EnvExtension
/-- Environment extensions can only be registered during initialization.
@@ -1002,12 +1171,13 @@ end EnvExtension
Note that by default, extension state is *not* stored in .olean files and will not propagate across `import`s.
For that, you need to register a persistent environment extension. -/
def registerEnvExtension {σ : Type} (mkInitial : IO σ)
(replay? : Option (ReplayFn σ) := none)
(asyncMode : EnvExtension.AsyncMode := .mainOnly) : IO (EnvExtension σ) := do
unless ( initializing) do
throw (IO.userError "failed to register environment, extensions can only be registered during initialization")
let exts EnvExtension.envExtensionsRef.get
let idx := exts.size
let ext : EnvExtension σ := { idx, mkInitial, asyncMode }
let ext : EnvExtension σ := { idx, mkInitial, asyncMode, replay? }
-- safety: `EnvExtensionState` is opaque, so we can upcast to it
EnvExtension.envExtensionsRef.modify fun exts => exts.push (unsafe unsafeCast ext)
pure ext
@@ -1019,7 +1189,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
let initializing IO.initializing
if initializing then throw (IO.userError "environment objects cannot be created during initialization")
let exts mkInitialExtensionStates
pure {
return {
checkedWithoutAsync := {
const2ModIdx := {}
constants := {}
@@ -1027,6 +1197,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
extraConstNames := {}
extensions := exts
}
realizedImportedConsts? := none
}
structure PersistentEnvExtensionState (α : Type) (σ : Type) where
@@ -1117,8 +1288,9 @@ def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : En
{ s with state := state }
/-- Get the current state of the given extension in the given environment. -/
def getState {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment) : σ :=
(ext.toEnvExtension.getState env).state
def getState {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment)
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
(ext.toEnvExtension.getState (asyncMode := asyncMode) env).state
/-- Set the current state of the given extension in the given environment. -/
def setState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (s : σ) : Environment :=
@@ -1128,23 +1300,11 @@ def setState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : En
def modifyState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (f : σ σ) : Environment :=
ext.toEnvExtension.modifyState env fun ps => { ps with state := f (ps.state) }
-- `unsafe` fails to infer `Nonempty` here
private unsafe def findStateAsyncUnsafe {α β σ : Type} [Inhabited σ]
(ext : PersistentEnvExtension α β σ) (env : Environment) (declPrefix : Name) : σ :=
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
if let some { exts? := some exts, .. } := env.asyncConsts.findPrefix? declPrefix then
ext.toEnvExtension.getStateImpl exts.get |>.state
else
ext.toEnvExtension.getStateImpl env.checkedWithoutAsync.extensions |>.state
@[inherit_doc EnvExtension.findStateAsync]
def findStateAsync {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
(env : Environment) (declPrefix : Name) : σ :=
ext.toEnvExtension.findStateAsync env declPrefix |>.state
/--
Returns the final extension state on the environment branch corresponding to the passed declaration
name, if any, or otherwise the state on the current branch. In other words, at most one environment
branch will be blocked on.
-/
@[implemented_by findStateAsyncUnsafe]
opaque findStateAsync {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
(env : Environment) (declPrefix : Name) : σ
end PersistentEnvExtension
@@ -1158,11 +1318,14 @@ structure PersistentEnvExtensionDescr (α β σ : Type) where
exportEntriesFn : σ Array α
statsFn : σ Format := fun _ => Format.nil
asyncMode : EnvExtension.AsyncMode := .mainOnly
replay? : Option (ReplayFn σ) := none
unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ) := do
let pExts persistentEnvExtensionsRef.get
if pExts.any (fun ext => ext.name == descr.name) then throw (IO.userError s!"invalid environment extension, '{descr.name}' has already been used")
let ext registerEnvExtension (asyncMode := descr.asyncMode) do
let replay? := descr.replay?.map fun replay =>
fun oldState newState newConsts s => { s with state := replay oldState.state newState.state newConsts s.state }
let ext registerEnvExtension (asyncMode := descr.asyncMode) (replay? := replay?) do
let initial descr.mkInitial
let s : PersistentEnvExtensionState α σ := {
importedEntries := #[],
@@ -1195,6 +1358,18 @@ structure SimplePersistentEnvExtensionDescr (α σ : Type) where
addImportedFn : Array (Array α) σ
toArrayFn : List α Array α := fun es => es.toArray
asyncMode : EnvExtension.AsyncMode := .mainOnly
replay? : Option ((newEntries : List α) (newState : σ) σ List α × σ) := none
/--
Returns a function suitable for `SimplePersistentEnvExtensionDescr.replay?` that replays all new
entries onto the state using `addEntryFn`. `p` should filter out entries that have already been
added to the state by a prior replay of the same realizable constant.
-/
def SimplePersistentEnvExtension.replayOfFilter (p : σ α Bool)
(addEntryFn : σ α σ) : List α σ σ List α × σ :=
fun newEntries _ s =>
let newEntries := newEntries.filter (p s)
(newEntries, newEntries.foldl (init := s) addEntryFn)
def registerSimplePersistentEnvExtension {α σ : Type} [Inhabited σ] (descr : SimplePersistentEnvExtensionDescr α σ) : IO (SimplePersistentEnvExtension α σ) :=
registerPersistentEnvExtension {
@@ -1206,6 +1381,10 @@ def registerSimplePersistentEnvExtension {α σ : Type} [Inhabited σ] (descr :
exportEntriesFn := fun s => descr.toArrayFn s.1.reverse,
statsFn := fun s => format "number of local entries: " ++ format s.1.length
asyncMode := descr.asyncMode
replay? := descr.replay?.map fun replay oldState newState _ (entries, s) =>
let newEntries := newState.1.take (newState.1.length - oldState.1.length)
let (newEntries, s) := replay newEntries newState.2 s
(entries ++ newEntries, s)
}
namespace SimplePersistentEnvExtension
@@ -1219,8 +1398,9 @@ def getEntries {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension
(PersistentEnvExtension.getState ext env).1
/-- Get the current state of the given `SimplePersistentEnvExtension`. -/
def getState {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ) (env : Environment) : σ :=
(PersistentEnvExtension.getState ext env).2
def getState {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ) (env : Environment)
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
(PersistentEnvExtension.getState (asyncMode := asyncMode) ext env).2
/-- Set the current state of the given `SimplePersistentEnvExtension`. This change is *not* persisted across files. -/
def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (s : σ) : Environment :=
@@ -1230,6 +1410,11 @@ def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : En
def modifyState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (f : σ σ) : Environment :=
PersistentEnvExtension.modifyState ext env (fun entries, s => (entries, f s))
@[inherit_doc PersistentEnvExtension.findStateAsync]
def findStateAsync {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ)
(env : Environment) (declPrefix : Name) : σ :=
PersistentEnvExtension.findStateAsync ext env declPrefix |>.2
end SimplePersistentEnvExtension
/-- Environment extension for tagging declarations.
@@ -1329,8 +1514,12 @@ unsafe def Environment.freeRegions (env : Environment) : IO Unit :=
def mkModuleData (env : Environment) : IO ModuleData := do
let pExts persistentEnvExtensionsRef.get
let entries := pExts.map fun pExt =>
let state := pExt.getState env
let entries := pExts.map fun pExt => Id.run do
-- get state from `checked` at the end if `async`; it would otherwise panic
let mut asyncMode := pExt.toEnvExtension.asyncMode
if asyncMode matches .async then
asyncMode := .sync
let state := pExt.getState (asyncMode := asyncMode) env
(pExt.name, pExt.exportEntriesFn state)
let kenv := env.toKernelEnv
let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
@@ -1403,7 +1592,9 @@ where
let pExtDescrs persistentEnvExtensionsRef.get
if h : i < pExtDescrs.size then
let extDescr := pExtDescrs[i]
let s := extDescr.toEnvExtension.getState env
-- `local` as `async` does not allow for `getState` but it's all safe here as there is only
-- one branch so far.
let s := extDescr.toEnvExtension.getState (asyncMode := .local) env
let prevSize := ( persistentEnvExtensionsRef.get).size
let prevAttrSize getNumBuiltinAttributes
let newState extDescr.addImportedFn s.importedEntries { env := env, opts := opts }
@@ -1522,6 +1713,7 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
moduleData := s.moduleData
}
}
realizedImportedConsts? := none
}
env setImportedEntries env s.moduleData
if leakEnv then
@@ -1583,6 +1775,9 @@ builtin_initialize namespacesExt : SimplePersistentEnvExtension Name NameSSet
let map := mkStateFromImportedEntries (fun map name => map.insert name ()) map as
SMap.fromHashMap map |>.switch
addEntryFn := fun s n => s.insert n
-- Namespaces from local helper constants can be disregarded in other environment branches. We
-- do *not* want `getNamespaceSet` to have to wait on all prior branches.
asyncMode := .local
}
@[inherit_doc Kernel.Environment.enableDiag]
@@ -1616,8 +1811,18 @@ def getNamespaceSet (env : Environment) : NameSSet :=
namespacesExt.getState env
@[export lean_elab_environment_update_base_after_kernel_add]
private def updateBaseAfterKernelAdd (env : Environment) (kernel : Kernel.Environment) : Environment :=
{ env with checked := .pure kernel, checkedWithoutAsync := { kernel with extensions := env.checkedWithoutAsync.extensions } }
private def updateBaseAfterKernelAdd (env : Environment) (kernel : Kernel.Environment) (decl : Declaration) : Environment :=
{ env with
checked := .pure kernel
checkedWithoutAsync := { kernel with extensions := env.checkedWithoutAsync.extensions }
-- make constants available in `asyncConsts` as well; see its docstring
asyncConsts := decl.getNames.foldl (init := env.asyncConsts) fun asyncConsts n =>
if asyncConsts.find? n |>.isNone then
asyncConsts.add {
constInfo := .ofConstantInfo (kernel.find? n |>.get!)
exts? := none
}
else asyncConsts }
@[export lean_display_stats]
def displayStats (env : Environment) : IO Unit := do
@@ -1666,6 +1871,107 @@ def hasUnsafe (env : Environment) (e : Expr) : Bool :=
| _ => false;
c?.isSome
/-- Plumbing function for `Lean.Meta.realizeConst`; see documentation there. -/
def realizeConst (env : Environment) (forConst : Name) (constName : Name)
(realize : Environment Options BaseIO (Environment × Dynamic)) :
IO (Environment × Dynamic) := do
let mut env := env
-- find `RealizationContext` for `forConst` in `realizedImportedConsts?` or `realizedLocalConsts`
let ctx if env.checkedWithoutAsync.const2ModIdx.contains forConst then
env.realizedImportedConsts?.getDM <|
throw <| .userError s!"Environment.realizeConst: `realizedImportedConsts` is empty"
else
match env.realizedLocalConsts.find? forConst with
| some ctx => pure ctx
| none =>
throw <| .userError s!"trying to realize {constName} but `enableRealizationsForConst` must be called for '{forConst}' first"
let prom IO.Promise.new
-- ensure `prom` is not left unresolved from stray exceptions
BaseIO.toIO do
-- atomically check whether we are the first branch to realize `constName`
let existingConsts? ctx.constsRef.modifyGet fun m => match m.find? constName with
| some prom' => (some prom', m)
| none => (none, m.insert constName prom.result!)
let (consts, replay, dyn) if let some existingConsts := existingConsts? then
pure existingConsts.get
else
-- safety: `RealizationContext` is private
let realizeEnv : Environment := unsafe unsafeCast ctx.env
let realizeEnv := { realizeEnv with
-- allow realizations to recursively realize other constants for `forConst`. Do note that
-- this allows for recursive realization of `constName` itself, which will deadlock.
realizedLocalConsts := realizeEnv.realizedLocalConsts.insert forConst ctx
realizedImportedConsts? := env.realizedImportedConsts?
}
-- ensure realized constants are nested below `forConst` and that environment extension
-- modifications know they are in an async context
let realizeEnv := realizeEnv.enterAsync (realizing := true) forConst
-- skip kernel in `realize`, we'll re-typecheck anyway
let realizeOpts := debug.skipKernelTC.set ctx.opts true
let (realizeEnv', dyn) realize realizeEnv realizeOpts
-- We could check that `c` was indeed added here but in practice `realize` has already
-- reported an error so we don't.
-- find new constants incl. nested realizations, add current extension state, and compute
-- closure
let numNewConsts := realizeEnv'.asyncConsts.size - realizeEnv.asyncConsts.size
let consts := realizeEnv'.asyncConsts.revList.take numNewConsts |>.reverse
let consts := consts.map fun c =>
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.checkedWithoutAsync.extensions }
else c
let exts EnvExtension.envExtensionsRef.get
let replay := (maybeAddToKernelEnv realizeEnv realizeEnv' consts · exts)
prom.resolve (consts, replay, dyn)
pure (consts, replay, dyn)
return ({ env with
asyncConsts := consts.foldl (init := env.asyncConsts) fun consts c =>
if consts.find? c.constInfo.name |>.isSome then
consts
else
consts.add c
checked := env.checked.map replay
}, dyn)
where
-- Adds `consts` if they haven't already been added by a previous branch. Note that this
-- conditional is deterministic because of the linearizing effect of `env.checked`.
maybeAddToKernelEnv (oldEnv newEnv : Environment) (consts : List AsyncConst)
(kenv : Kernel.Environment)
(exts : Array (EnvExtension EnvExtensionState)) : Kernel.Environment := Id.run do
let mut kenv := kenv
for c in consts do
if kenv.find? c.constInfo.name |>.isSome then
continue
let info := c.constInfo.toConstantInfo
if info.isUnsafe then
-- Checking unsafe declarations is not necessary for consistency, and it is necessary to
-- avoid checking them in the case of the old code generator, which adds ill-typed constants
-- to the kernel environment. We can delete this branch after removing the old code
-- generator.
kenv := kenv.add info
continue
let decl := match info with
| .thmInfo thm => .thmDecl thm
| .defnInfo defn => .defnDecl defn
| _ => panic! s!"Environment.realizeConst: {c.constInfo.name} must be definition/theorem"
-- realized kernel additions cannot be interrupted - which would be bad anyway as they can be
-- reused between snapshots
match kenv.addDeclCore 0 decl none with
| .ok kenv' => kenv := kenv'
| .error e =>
let _ : Inhabited Kernel.Environment := kenv
panic! s!"Environment.realizeConst: failed to add {c.constInfo.name} to environment\n{e.toRawString}"
for ext in exts do
if let some replay := ext.replay? then
kenv := { kenv with
-- safety: like in `modifyState`, but that one takes an elab env instead of a kernel env
extensions := unsafe (ext.modifyStateImpl kenv.extensions <|
replay
(ext.getStateImpl oldEnv.toKernelEnv.extensions)
(ext.getStateImpl newEnv.toKernelEnv.extensions)
(consts.map (·.constInfo.name))) }
return kenv
end Environment
namespace Kernel
@@ -1721,4 +2027,13 @@ def mkDefinitionValInferrringUnsafe [Monad m] [MonadEnv m] (name : Name) (levelP
let safety := if env.hasUnsafe type || env.hasUnsafe value then DefinitionSafety.unsafe else DefinitionSafety.safe
return { name, levelParams, type, value, hints, safety }
def getMaxHeight (env : Environment) (e : Expr) : UInt32 :=
e.foldConsts 0 fun constName max =>
match env.find? constName with
| ConstantInfo.defnInfo val =>
match val.hints with
| ReducibilityHints.regular h => if h > max then h else max
| _ => max
| _ => max
end Lean

View File

@@ -1272,11 +1272,23 @@ This operation traverses the expression tree.
@[extern "lean_expr_has_loose_bvar"]
opaque hasLooseBVar (e : @& Expr) (bvarIdx : @& Nat) : Bool
/-- Return true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter, or in the range if `tryRange == true`. -/
def hasLooseBVarInExplicitDomain : Expr Nat Bool Bool
| Expr.forallE _ d b bi, bvarIdx, tryRange =>
(bi.isExplicit && hasLooseBVar d bvarIdx) || hasLooseBVarInExplicitDomain b (bvarIdx+1) tryRange
| e, bvarIdx, tryRange => tryRange && hasLooseBVar e bvarIdx
/--
Returns true if `e` contains the loose bound variable `bvarIdx` in an explicit parameter,
or in the range if `considerRange == true`.
Additionally, if the bound variable appears in an implicit parameter,
it transitively looks for that implicit parameter.
-/
-- This should be kept in sync with `lean::has_loose_bvars_in_domain`
def hasLooseBVarInExplicitDomain (e : Expr) (bvarIdx : Nat) (considerRange : Bool) : Bool :=
match e with
| Expr.forallE _ d b bi =>
(hasLooseBVar d bvarIdx
&& (bi.isExplicit
-- "Transitivity": bvar occurs in current implicit argument,
-- so we search for the current argument in the body.
|| hasLooseBVarInExplicitDomain b 0 considerRange))
|| hasLooseBVarInExplicitDomain b (bvarIdx+1) considerRange
| e => considerRange && hasLooseBVar e bvarIdx
/--
Lower the loose bound variables `>= s` in `e` by `d`.
@@ -1297,16 +1309,16 @@ opaque liftLooseBVars (e : @& Expr) (s d : @& Nat) : Expr
It marks any parameter with an explicit binder annotation if there is another explicit arguments that depends on it or
the resulting type if `considerRange == true`.
Remark: we use this function to infer the bind annotations of inductive datatype constructors, and structure projections.
When the `{}` annotation is used in these commands, we set `considerRange == false`.
Remark: we use this function to infer the binder annotations of structure projections.
-/
def inferImplicit : Expr Nat Bool Expr
| Expr.forallE n d b bi, i+1, considerRange =>
-- This should be kept in synch with `lean::infer_implicit`
def inferImplicit (e : Expr) (numParams : Nat) (considerRange : Bool) : Expr :=
match e, numParams with
| Expr.forallE n d b bi, i + 1 =>
let b := inferImplicit b i considerRange
let newInfo := if bi.isExplicit && hasLooseBVarInExplicitDomain b 0 considerRange then BinderInfo.implicit else bi
mkForall n newInfo d b
| e, 0, _ => e
| e, _, _ => e
| e, _ => e
/--
Instantiates the loose bound variables in `e` using the `subst` array,

View File

@@ -184,7 +184,7 @@ structure SnapshotTree where
element : Snapshot
/-- The asynchronously available children of the snapshot tree node. -/
children : Array (SnapshotTask SnapshotTree)
deriving Inhabited
deriving Inhabited, TypeName
/--
Helper class for projecting a heterogeneous hierarchy of snapshot classes to a homogeneous

View File

@@ -177,6 +177,55 @@ created from a quotation, the ref usually has to be changed to a less variable s
simple example and the implementation of tactic `have` for a complex example.
-/
/-
# Note [Incremental Cancellation]
With incrementality, there is a tension between telling the elaboration of the previous document
version(s) to stop processing as soon as possible in order to free resources for use in the current
elaboration run and having the old version continue in case its results can be used as is so as not
to duplicate effort.
Before parallelism, we were able to use a single cancellation token for the entire elaboration of a
specific document version. We could trigger the cancellation token as soon as we started elaborating
a new version because the generated exceptions would prevent the elaborator from storing any
half-elaborated state in snapshots that might then be picked up for reuse. This was a simple and
sound solution, though it did mean we may have cancelled some work eagerly that could have been
reused.
This approach is no longer sound with parallelism: a tactic may have spawned async tasks (e.g.
kernel checking of a helper definition) and then completed, creating a snapshot that references the
result of the task e.g. via an asynchronous constant in the environment. If we then interrupted
elaboration of that document version throughout all its tasks, we might end up with a snapshot that
looks eligible for reuse but references data (eventually) resulting from cancellation. We could
(asynchronously) wait for it to complete and then check whether it completed because of cancellation
and redo its work in that case but that would be as wasteful as mentioned above and add new latency
on top.
Instead, we now make sure we cancel only the parts of elaboration we have ruled out for reuse, at
the earliest point where we can decide that. We do this by storing cancellation tokens in the
snapshot tree such that we can trigger all tokens of tasks belonging to a specific subtree
(`SnapshotTask.cancelRec`; async tasks belonging to the subtree are usually collected via
`Core.getAndEmptySnapshotTasks`). Thus when traversing the old snapshot tree, we need to be careful
about cancelling any children we decide not to descend into. This is automated in e.g.
`withNarrowedTacticReuse` but not in other places that do not lend themselves to abstraction into
combinators. Note that we can still cancel parsing and elaboration below the changed command eagerly
as we never consider them for reuse.
This approach is still not optimal in the sense that async tasks in later snapshots not part of the
current subtree are considered for cancellation only when elaboration reaches that point. Thus if
inside a single proof we have some significant work done synchronously by one tactic and then
significant work done asynchronously by a later tactic and neither tactic is eligible for reuse, the
second task will only be cancelled after redoing the synchronous work up to the point of the second
tactic. However, as tactics such as `bv_decide` that do significant kernel work do so synchronously
at the moment in order to post-process any failures and as the most significant async work, that of
checking/compiling/linting/... the top-level definition, is interrupted immediately when the mutual
def elaborator notices that the body syntax has changed, this should not be a significant issue in
practice. If we do want to optimize this, instead of cancelling subtrees of the snapshot tree, we
would likely have to store an asynchronously resolved list of cancellation tokens associated with
the tactic snapshot at hand *and all further snapshots* so that we can cancel them eagerly instead
of waiting for elaboration to visit those later snapshots.
-/
set_option linter.missingDocs true
namespace Lean.Language.Lean
@@ -425,6 +474,7 @@ where
return { diagnostics, result? := none }
let headerEnv := headerEnv.setMainModule setup.mainModuleName
let headerEnv headerEnv.enableRealizationsForImports setup.opts
let mut traceState := default
if trace.profiler.output.get? setup.opts |>.isSome then
traceState := {
@@ -471,7 +521,7 @@ where
parseCmd (old? : Option CommandParsedSnapshot) (parserState : Parser.ModuleParserState)
(cmdState : Command.State) (prom : IO.Promise CommandParsedSnapshot) (sync : Bool)
(cancelTk : IO.CancelToken) : LeanProcessingM Unit := do
(parseCancelTk : IO.CancelToken) : LeanProcessingM Unit := do
let ctx read
let unchanged old newParserState : BaseIO Unit :=
@@ -519,13 +569,21 @@ where
if stx.eqWithInfo old.stx then
-- Here we must make sure to pass the *new* parser state; see NOTE in `unchanged`
return ( unchanged old parserState)
-- on first change, make sure to cancel old invocation
-- TODO: cancel nested tasks on invalidation
old.elabSnap.cancelTk?.forM (·.set)
-- On first change, immediately cancel old invocation for all subsequent commands. This
-- includes setting the global parse cancellation token, which is stored in
-- `next?` below. Thus we can be sure that no further commands will start to elaborate in the
-- old invocation from this point on.
old.nextCmdSnap?.forM (·.cancelRec)
-- For the current command, we depend on the elaborator to either reuse parts of `old` or
-- cancel them as soon as reuse can be ruled out.
-- check for cancellation, most likely during elaboration of previous command, before starting
-- processing of next command
if ( cancelTk.isSet) then
if ( parseCancelTk.isSet) then
if let some old := old? then
-- all of `old` is discarded, so cancel all of it
toSnapshotTree old |>.children.forM (·.cancelRec)
-- this is a bit ugly as we don't want to adjust our API with `Option`s just for cancellation
-- (as no-one should look at this result in that case) but anything containing `Environment`
-- is not `Inhabited`
@@ -563,19 +621,22 @@ where
let nextCmdSnap? := next?.map ({
stx? := none
reportingRange? := some parserState.pos, ctx.input.endPos
cancelTk? := parseCancelTk
task := ·.result!
})
let diagnostics Snapshot.Diagnostics.ofMessageLog msgLog
-- use per-command cancellation token for elaboration so that
let elabCmdCancelTk IO.CancelToken.new
prom.resolve {
diagnostics, finishedSnap, nextCmdSnap?
stx := stx', parserState := parserState'
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some cancelTk }
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some elabCmdCancelTk }
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result! }
}
let cmdState doElab stx cmdState beginPos
{ old? := old?.map fun old => old.stx, old.elabSnap, new := elabPromise }
finishedPromise cancelTk ctx
finishedPromise elabCmdCancelTk ctx
let traceTask
if ( isTracingEnabledForCore `Elab.snapshotTree cmdState.scopes.head!.opts) then
-- We want to trace all of `CommandParsedSnapshot` but `traceTask` is part of it, so let's
@@ -609,7 +670,7 @@ where
}
if let some next := next? then
-- We're definitely off the fast-forwarding path now
parseCmd none parserState cmdState next (sync := false) cancelTk ctx
parseCmd none parserState cmdState next (sync := false) elabCmdCancelTk ctx
doElab (stx : Syntax) (cmdState : Command.State) (beginPos : String.Pos)
(snap : SnapshotBundle DynamicSnapshot) (finishedPromise : IO.Promise CommandFinishedSnapshot)

View File

@@ -159,14 +159,16 @@ end LocalDecl
/-- A LocalContext is an ordered set of local variable declarations.
It is used to store the free variables (also known as local constants) that
are in scope.
are in scope. It also maps free variables corresponding to auxiliary declarations
(recursive references and `where` and `let rec` bindings) to their fully-qualified global names.
When inspecting a goal or expected type in the infoview, the local
context is all of the variables above the `⊢` symbol.
-/
structure LocalContext where
fvarIdToDecl : PersistentHashMap FVarId LocalDecl := {}
decls : PersistentArray (Option LocalDecl) := {}
fvarIdToDecl : PersistentHashMap FVarId LocalDecl := {}
decls : PersistentArray (Option LocalDecl) := {}
auxDeclToFullName : FVarIdMap Name := {}
deriving Inhabited
namespace LocalContext
@@ -186,10 +188,10 @@ It should not be used directly since the argument `(fvarId : FVarId)` is
assumed to be unique. You can create a unique fvarId with `mkFreshFVarId`. -/
def mkLocalDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (bi : BinderInfo := BinderInfo.default) (kind : LocalDeclKind := .default) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
let idx := decls.size
let decl := LocalDecl.cdecl idx fvarId userName type bi kind
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl }
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl, auxDeclToFullName }
-- `mkLocalDecl` without `kind`
@[export lean_local_ctx_mk_local_decl]
@@ -199,23 +201,34 @@ private def mkLocalDeclExported (lctx : LocalContext) (fvarId : FVarId) (userNam
/-- Low level API for let declarations. Do not use directly.-/
def mkLetDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nonDep := false) (kind : LocalDeclKind := default) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
let idx := decls.size
let decl := LocalDecl.ldecl idx fvarId userName type value nonDep kind
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl }
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl, auxDeclToFullName }
@[export lean_local_ctx_mk_let_decl]
private def mkLetDeclExported (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (value : Expr) (nonDep : Bool) : LocalContext :=
mkLetDecl lctx fvarId userName type value nonDep
/-- Low level API for auxiliary declarations. Do not use directly. -/
def mkAuxDecl (lctx : LocalContext) (fvarId : FVarId) (userName : Name) (type : Expr) (fullName : Name) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
let idx := decls.size
let decl := LocalDecl.cdecl idx fvarId userName type .default .auxDecl
let auxDeclToFullName := auxDeclToFullName.insert fvarId fullName
{ fvarIdToDecl := map.insert fvarId decl, decls := decls.push decl, auxDeclToFullName }
/-- Low level API for adding a local declaration.
Do not use directly. -/
def addDecl (lctx : LocalContext) (newDecl : LocalDecl) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
let idx := decls.size
let newDecl := newDecl.setIndex idx
{ fvarIdToDecl := map.insert newDecl.fvarId newDecl, decls := decls.push newDecl }
{ fvarIdToDecl := map.insert newDecl.fvarId newDecl
decls := decls.push newDecl
auxDeclToFullName }
@[export lean_local_ctx_find]
def find? (lctx : LocalContext) (fvarId : FVarId) : Option LocalDecl :=
@@ -260,18 +273,26 @@ private partial def popTailNoneAux (a : PArray (Option LocalDecl)) : PArray (Opt
@[export lean_local_ctx_erase]
def erase (lctx : LocalContext) (fvarId : FVarId) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
match map.find? fvarId with
| none => lctx
| some decl => { fvarIdToDecl := map.erase fvarId, decls := popTailNoneAux (decls.set decl.index none) }
| some decl =>
{ fvarIdToDecl := map.erase fvarId
decls := popTailNoneAux (decls.set decl.index none)
auxDeclToFullName :=
if decl.isAuxDecl then auxDeclToFullName.erase fvarId else auxDeclToFullName }
def pop (lctx : LocalContext): LocalContext :=
def pop (lctx : LocalContext) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
if _ : decls.size = 0 then lctx
else match decls[decls.size - 1] with
| none => lctx -- unreachable
| some decl => { fvarIdToDecl := map.erase decl.fvarId, decls := popTailNoneAux decls.pop }
| some decl =>
{ fvarIdToDecl := map.erase decl.fvarId
decls := popTailNoneAux decls.pop
auxDeclToFullName :=
if decl.isAuxDecl then auxDeclToFullName.erase decl.fvarId else auxDeclToFullName }
def findFromUserName? (lctx : LocalContext) (userName : Name) : Option LocalDecl :=
lctx.decls.findSomeRev? fun decl =>
@@ -298,18 +319,20 @@ def lastDecl (lctx : LocalContext) : Option LocalDecl :=
def setUserName (lctx : LocalContext) (fvarId : FVarId) (userName : Name) : LocalContext :=
let decl := lctx.get! fvarId
let decl := decl.setUserName userName
{ fvarIdToDecl := lctx.fvarIdToDecl.insert decl.fvarId decl,
decls := lctx.decls.set decl.index decl }
{ fvarIdToDecl := lctx.fvarIdToDecl.insert decl.fvarId decl,
decls := lctx.decls.set decl.index decl,
auxDeclToFullName := lctx.auxDeclToFullName }
def renameUserName (lctx : LocalContext) (fromName : Name) (toName : Name) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
match lctx.findFromUserName? fromName with
| none => lctx
| some decl =>
let decl := decl.setUserName toName;
{ fvarIdToDecl := map.insert decl.fvarId decl,
decls := decls.set decl.index decl }
decls := decls.set decl.index decl,
auxDeclToFullName }
/--
Low-level function for updating the local context.
@@ -317,13 +340,14 @@ def renameUserName (lctx : LocalContext) (fromName : Name) (toName : Name) : Loc
the `index` nor `fvarId` are modified. -/
@[inline] def modifyLocalDecl (lctx : LocalContext) (fvarId : FVarId) (f : LocalDecl LocalDecl) : LocalContext :=
match lctx with
| { fvarIdToDecl := map, decls := decls } =>
| { fvarIdToDecl := map, decls := decls, auxDeclToFullName } =>
match lctx.find? fvarId with
| none => lctx
| some decl =>
let decl := f decl
{ fvarIdToDecl := map.insert decl.fvarId decl
decls := decls.set decl.index decl }
decls := decls.set decl.index decl
auxDeclToFullName }
/--
Set the kind of the given fvar.
@@ -520,6 +544,7 @@ def LocalDecl.replaceFVarId (fvarId : FVarId) (e : Expr) (d : LocalDecl) : Local
def LocalContext.replaceFVarId (fvarId : FVarId) (e : Expr) (lctx : LocalContext) : LocalContext :=
let lctx := lctx.erase fvarId
{ fvarIdToDecl := lctx.fvarIdToDecl.map (·.replaceFVarId fvarId e)
decls := lctx.decls.map fun localDecl? => localDecl?.map (·.replaceFVarId fvarId e) }
decls := lctx.decls.map fun localDecl? => localDecl?.map (·.replaceFVarId fvarId e),
auxDeclToFullName := lctx.auxDeclToFullName }
end Lean

View File

@@ -241,6 +241,11 @@ partial def formatAux : NamingContext → Option MessageDataContext → MessageD
| nCtx, ctx, compose d₁ d₂ => return ( formatAux nCtx ctx d₁) ++ ( formatAux nCtx ctx d₂)
| nCtx, ctx, group d => Format.group <$> formatAux nCtx ctx d
| nCtx, ctx, trace data header children => do
let childFmts children.mapM (formatAux nCtx ctx)
if data.cls.isAnonymous then
-- Sequence of top-level traces collected by `addTraceAsMessages`, do not indent.
return .joinSep childFmts.toList "\n"
let mut msg := f!"[{data.cls}]"
if data.startTime != 0 then
msg := f!"{msg} [{data.stopTime - data.startTime}]"
@@ -250,7 +255,6 @@ partial def formatAux : NamingContext → Option MessageDataContext → MessageD
if maxNum > 0 && children.size > maxNum then
children := children.take maxNum |>.push <|
ofFormat f!"{children.size - maxNum} more entries... (increase `maxTraceChildren` to see more)"
let childFmts children.mapM (formatAux nCtx ctx)
return .nest 2 (.joinSep (msg::childFmts.toList) "\n")
| nCtx, ctx?, ofLazy pp _ => do
let dyn pp (ctx?.map (mkPPContext nCtx))

View File

@@ -1664,6 +1664,21 @@ def withLocalDeclsDND [Inhabited α] (declInfos : Array (Name × Expr)) (k : (xs
withLocalDeclsD
(declInfos.map (fun (name, typeCtor) => (name, fun _ => pure typeCtor))) k
private def withAuxDeclImp (shortDeclName : Name) (type : Expr) (declName : Name) (k : Expr MetaM α) : MetaM α := do
let fvarId mkFreshFVarId
let ctx read
let lctx := ctx.lctx.mkAuxDecl fvarId shortDeclName type declName
let fvar := mkFVar fvarId
withReader (fun ctx => { ctx with lctx := lctx }) do
withNewFVar fvar type k
/--
Declare an auxiliary local declaration `shortDeclName : type` for elaborating recursive
declaration `declName`, update the mapping `auxDeclToFullName`, and then execute `k`.
-/
def withAuxDecl (shortDeclName : Name) (type : Expr) (declName : Name) (k : Expr n α) : n α :=
map1MetaM (fun k => withAuxDeclImp shortDeclName type declName k) k
private def withNewBinderInfosImp (bs : Array (FVarId × BinderInfo)) (k : MetaM α) : MetaM α := do
let lctx := bs.foldl (init := ( getLCtx)) fun lctx (fvarId, bi) =>
lctx.setBinderInfo fvarId bi
@@ -2203,10 +2218,110 @@ def instantiateMVarsIfMVarApp (e : Expr) : MetaM Expr := do
else
return e
private partial def setAllDiagRanges (snap : Language.SnapshotTree) (pos endPos : Position) :
BaseIO Language.SnapshotTree := do
let msgLog := snap.element.diagnostics.msgLog
let msgLog := { msgLog with unreported := msgLog.unreported.map fun diag =>
{ diag with pos, endPos } }
return {
element.diagnostics := ( Language.Snapshot.Diagnostics.ofMessageLog msgLog)
children := ( snap.children.mapM fun task => return { task with
stx? := none
task := ( BaseIO.mapTask (t := task.task) (setAllDiagRanges · pos endPos)) })
}
open Language
private structure RealizeConstantResult where
snap : SnapshotTree
error? : Option Exception
deriving TypeName
/--
Makes the helper constant `constName` that is derived from `forConst` available in the environment.
`enableRealizationsForConst forConst` must have been called first on this environment branch. If
this is the first environment branch requesting `constName` to be realized (atomically), `realize`
is called with the environment and options at the time of calling `enableRealizationsForConst` if
`forConst` is from the current module and the state just after importing (when
`enableRealizationsForImports` should be called) otherwise, thus helping achieve deterministic
results despite the non-deterministic choice of which thread is tasked with realization. In other
words, the state after calling `realizeConst` is *as if* `realize` had been called immediately after
`enableRealizationsForConst forConst`, though the effects of this call are visible only after
calling `realizeConst`. See below for more details on the replayed effects.
`realizeConst` cannot check what other data is captured in the `realize` closure,
so it is best practice to extract it into a separate function and pay close attention to the passed
arguments, if any. `realize` must return with `constName` added to the environment,
at which point all callers of `realizeConst` with this `constName` will be unblocked
and have access to an updated version of their own environment containing any new constants
`realize` added, including recursively realized constants. Traces, diagnostics, and raw std stream
output are reported at all callers via `Core.logSnapshotTask` (so that the location of generated
diagnostics is deterministic). Note that, as `realize` is run using the options at declaration time
of `forConst`, trace options must be set prior to that (or, for imported constants, on the cmdline)
in order to be active. The environment extension state at the end of `realize` is available to each
caller via `EnvExtension.findStateAsync` for `constName`. If `realize` throws an exception or fails
to add `constName` to the environment, an appropriate diagnostic is reported to all callers but no
constants are added to the environment.
-/
def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
MetaM Unit := do
let env getEnv
-- If `constName` is already known on this branch, avoid the trace node. We should not use
-- `contains` as it could block as well as find realizations on other branches, which would lack
-- the relevant local environment extension state when accessed on this branch.
if env.containsOnBranch constName then
return
withTraceNode `Meta.realizeConst (fun _ => return constName) do
let coreCtx readThe Core.Context
let coreCtx := {
-- these fields should be invariant throughout the file
fileName := coreCtx.fileName, fileMap := coreCtx.fileMap
-- heartbeat limits inside `realizeAndReport` should be measured from this point on
initHeartbeats := ( IO.getNumHeartbeats)
}
let (env, dyn) env.realizeConst forConst constName (realizeAndReport coreCtx)
if let some res := dyn.get? RealizeConstantResult then
let mut snap := res.snap
-- localize diagnostics
if let some range := ( getRef).getRange? then
let fileMap getFileMap
snap setAllDiagRanges snap (fileMap.toPosition range.start) (fileMap.toPosition range.stop)
Core.logSnapshotTask <| .finished (stx? := none) snap
if let some e := res.error? then
throw e
setEnv env
where
-- similar to `wrapAsyncAsSnapshot` but not sufficiently so to share code
realizeAndReport (coreCtx : Core.Context) env opts := do
let coreCtx := { coreCtx with options := opts }
let act :=
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get opts) do
-- catch all exceptions
let _ : MonadExceptOf _ MetaM := MonadAlwaysExcept.except
try
realize
if !( getEnv).contains constName then
throwError "Lean.Meta.realizeConst: {constName} was not added to the environment"
finally
addTraceAsMessages
let res? act |>.run' |>.run coreCtx { env } |>.toBaseIO
match res? with
| .ok ((output, ()), st) => pure (st.env, .mk {
snap := ( Core.mkSnapshot output coreCtx st)
error? := none
: RealizeConstantResult
})
| .error e => pure (env, .mk {
snap := toSnapshotTree { diagnostics := .empty : Language.SnapshotLeaf}
error? := some e
: RealizeConstantResult
})
end Meta
builtin_initialize
registerTraceClass `Meta.isLevelDefEq.postponed
registerTraceClass `Meta.realizeConst
export Meta (MetaM)

View File

@@ -784,6 +784,7 @@ def mkMatcherAuxDefinition (name : Name) (type : Expr) (value : Expr) : MetaM (E
modifyEnv fun env => matcherExt.modifyState env fun s => s.insert (result.value, compile) name
addMatcherInfo name mi
setInlineAttribute name
enableRealizationsForConst name
if compile then
compileDecl decl
return (mkMatcherConst name, some addMatcher)

View File

@@ -663,13 +663,15 @@ where
else
k altsNew
/--
Create conditional equations and splitter for the given match auxiliary declaration. -/
private partial def mkEquationsFor (matchDeclName : Name) : MetaM MatchEqns := withLCtx {} {} do
withTraceNode `Meta.Match.matchEqs (fun _ => return m!"mkEquationsFor '{matchDeclName}'") do
withConfig (fun c => { c with etaStruct := .none }) do
/-
Creates conditional equations and splitter for the given match auxiliary declaration.
See also `getEquationsFor`.
-/
@[export lean_get_match_equations_for]
def getEquationsForImpl (matchDeclName : Name) : MetaM MatchEqns := do
/-
Remark: user have requested the `split` tactic to be available for writing code.
Remark: users have requested the `split` tactic to be available for writing code.
Thus, the `splitter` declaration must be a definition instead of a theorem.
Moreover, the `splitter` is generated on demand, and we currently
can't import the same definition from different modules. Thus, we must
@@ -677,6 +679,12 @@ private partial def mkEquationsFor (matchDeclName : Name) : MetaM MatchEqns :=
-/
let baseName := mkPrivateName ( getEnv) matchDeclName
let splitterName := baseName ++ `splitter
-- NOTE: `go` will generate both splitter and equations but we use the splitter as the "key" for
-- `realizeConst` as well as for looking up the resultant environment extension sate via
-- `findStateAsync`.
realizeConst matchDeclName splitterName (go baseName splitterName)
return matchEqnsExt.findStateAsync ( getEnv) splitterName |>.map.find! matchDeclName
where go baseName splitterName := withConfig (fun c => { c with etaStruct := .none }) do
let constInfo getConstInfo matchDeclName
let us := constInfo.levelParams.map mkLevelParam
let some matchInfo getMatcherInfo? matchDeclName | throwError "'{matchDeclName}' is not a matcher function"
@@ -758,14 +766,6 @@ private partial def mkEquationsFor (matchDeclName : Name) : MetaM MatchEqns :=
setInlineAttribute splitterName
let result := { eqnNames, splitterName, splitterAltNumParams }
registerMatchEqns matchDeclName result
return result
/- See header at `MatchEqsExt.lean` -/
@[export lean_get_match_equations_for]
def getEquationsForImpl (matchDeclName : Name) : MetaM MatchEqns := do
match matchEqnsExt.getState ( getEnv) |>.map.find? matchDeclName with
| some matchEqns => return matchEqns
| none => mkEquationsFor matchDeclName
builtin_initialize registerTraceClass `Meta.Match.matchEqs

View File

@@ -24,7 +24,10 @@ structure MatchEqnsExtState where
/- We generate the equations and splitter on demand, and do not save them on .olean files. -/
builtin_initialize matchEqnsExt : EnvExtension MatchEqnsExtState
registerEnvExtension (pure {})
-- Using `local` allows us to use the extension in `realizeConst` without specifying `replay?`.
-- The resulting state can still be accessed on the generated declarations using `findStateAsync`;
-- see below
registerEnvExtension (pure {}) (asyncMode := .local)
def registerMatchEqns (matchDeclName : Name) (matchEqns : MatchEqns) : CoreM Unit := do
modifyEnv fun env => matchEqnsExt.modifyState env fun { map, eqns } => {
@@ -41,7 +44,7 @@ opaque getEquationsFor (matchDeclName : Name) : MetaM MatchEqns
/--
Returns `true` if `declName` is the name of a `match` equational theorem.
-/
def isMatchEqnTheorem (env : Environment) (declName : Name) : Bool :=
matchEqnsExt.getState env |>.eqns.contains declName
def isMatchEqnTheorem (env : Environment) (declName : Name) : Bool := Id.run do
matchEqnsExt.findStateAsync env declName |>.eqns.contains declName
end Lean.Meta.Match

View File

@@ -1,16 +1,23 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
Additional helper methods that require `MetaM` infrastructure.
Authors: Leonardo de Moura, Kyle Miller
-/
prelude
import Lean.AddDecl
import Lean.Structure
import Lean.Meta.AppBuilder
/-!
# Structure methods that require `MetaM` infrastructure
-/
namespace Lean.Meta
/--
If `struct` is an application of the form `S ..` with `S` a constant for a structure,
returns the name of the structure, otherwise throws an error.
-/
def getStructureName (struct : Expr) : MetaM Name :=
match struct.getAppFn with
| Expr.const declName .. => do
@@ -19,4 +26,87 @@ def getStructureName (struct : Expr) : MetaM Name :=
return declName
| _ => throwError "expected structure"
/--
Structure projection declaration for `mkProjections`.
-/
structure StructProjDecl where
ref : Syntax
projName : Name
/--
Adds projection functions to the environment for the one-constructor inductive type named `n`.
- The `projName`s in each `StructProjDecl` are used for the names of the declarations added to the environment.
- If `instImplicit` is true, then generates projections with `self` being instance implicit.
Notes:
- This function supports everything that `Expr.proj` supports (see `lean::type_checker::infer_proj`).
This means we can generate projections for inductive types with one-constructor,
even if it is an indexed family (which is not supported by the `structure` command).
- We throw errors in the cases that `Expr.proj` is not type-correct.
-/
def mkProjections (n : Name) (projDecls : Array StructProjDecl) (instImplicit : Bool) : MetaM Unit :=
withLCtx {} {} do
let indVal getConstInfoInduct n
if indVal.numCtors != 1 then
throwError "cannot generate projections for '{.ofConstName n}', does not have exactly one constructor"
let ctorVal getConstInfoCtor indVal.ctors.head!
let isPredicate isPropFormerType indVal.type
let lvls := indVal.levelParams.map mkLevelParam
forallBoundedTelescope ctorVal.type indVal.numParams fun params ctorType => do
if params.size != indVal.numParams then
throwError "projection generation failed, '{.ofConstName n}' is an ill-formed inductive datatype"
let selfType := mkAppN (.const n lvls) params
let selfBI : BinderInfo := if instImplicit then .instImplicit else .default
withLocalDecl `self selfBI selfType fun self => do
let projArgs := params.push self
-- Make modifications to parameter binder infos that apply to all projections
let mut lctx getLCtx
for param in params do
let fvarId := param.fvarId!
let decl fvarId.getDecl
if !decl.binderInfo.isInstImplicit && !decl.type.isOutParam then
/- We reset the implicit binder to have it be inferred by `Expr.inferImplicit`.
However, outparams must be implicit. -/
lctx := lctx.setBinderInfo fvarId .default
else if decl.binderInfo.isInstImplicit && instImplicit then
lctx := lctx.setBinderInfo fvarId .implicit
-- Construct the projection functions:
let mut ctorType := ctorType
for h : i in [0:projDecls.size] do
let {ref, projName} := projDecls[i]
unless ctorType.isForall do
throwErrorAt ref "\
failed to generate projection '{projName}' for '{.ofConstName n}', \
not enough constructor fields"
let resultType := ctorType.bindingDomain!.consumeTypeAnnotations
let isProp isProp resultType
if isPredicate && !isProp then
throwErrorAt ref "\
failed to generate projection '{projName}' for the 'Prop'-valued type '{.ofConstName n}', \
field must be a proof, but it has type\
{indentExpr resultType}"
let projType := lctx.mkForall projArgs resultType
let projType := projType.inferImplicit indVal.numParams (considerRange := true)
let projVal := lctx.mkLambda projArgs <| Expr.proj n i self
let cval : ConstantVal := { name := projName, levelParams := indVal.levelParams, type := projType }
withRef ref do
if isProp then
let env getEnv
addDecl <|
if env.hasUnsafe projType || env.hasUnsafe projVal then
-- Theorems cannot be unsafe, using opaque instead.
Declaration.opaqueDecl { cval with value := projVal, isUnsafe := true }
else
Declaration.thmDecl { cval with value := projVal }
else
let decl mkDefinitionValInferrringUnsafe projName indVal.levelParams projType projVal ReducibilityHints.abbrev
-- Projections have special compiler support. No need to compile.
addDecl <| Declaration.defnDecl decl
-- Recall: we want instance projections to be in "reducible canonical form"
if !instImplicit then
setReducibleAttribute projName
modifyEnv fun env => addProjectionFnInfo env projName ctorVal.name indVal.numParams i instImplicit
let proj := mkApp (mkAppN (.const projName lvls) params) self
ctorType := ctorType.bindingBody!.instantiate1 proj
end Lean.Meta

View File

@@ -120,6 +120,8 @@ where
else
collect (b.instantiate1 ( mkFreshExprMVar d)) (argIdx+1) targetIdx implicits targets'
| _ =>
unless targetIdx = targets.size do
throwError "extra targets for '{elimInfo.elimExpr}'"
return (implicits, targets')
structure CustomEliminator where

View File

@@ -7,15 +7,13 @@ prelude
import Lean.Meta.Tactic.Util
namespace Lean.Meta
/--
Creates a new goal whose local context has been "exposed" so that every local declaration has a clear, accessible name.
If no local declarations require renaming, the original goal is returned unchanged.
-/
def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
mvarId.checkNotAssigned `expose_names
/-- Returns a copy of the local context in which all declarations have clear, accessible names. -/
private def getLCtxWithExposedNames : MetaM LocalContext := do
let mut map : Std.HashMap Name FVarId := {}
let mut toRename := #[]
for localDecl in ( getLCtx) do
let mut lctx getLCtx
for localDecl in lctx do
let userName := localDecl.userName
if userName.hasMacroScopes then
toRename := toRename.push localDecl.fvarId
@@ -25,9 +23,8 @@ def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.wi
toRename := toRename.push fvarId
map := map.insert userName localDecl.fvarId
if toRename.isEmpty then
return mvarId
return lctx
let mut next : Std.HashMap Name Nat := {}
let mut lctx getLCtx
-- Remark: Shadowed variables may be inserted later.
toRename := toRename.qsort fun fvarId₁ fvarId₂ =>
(lctx.get! fvarId₁).index < (lctx.get! fvarId₂).index
@@ -49,8 +46,21 @@ def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.wi
next := next.insert baseName i
map := map.insert userName fvarId
lctx := lctx.modifyLocalDecl fvarId (·.setUserName userName)
return lctx
/--
Creates a new goal whose local context has been "exposed" so that every local declaration has a clear, accessible name.
If no local declarations require renaming, the original goal is returned unchanged.
-/
def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
mvarId.checkNotAssigned `expose_names
let lctx getLCtxWithExposedNames
let mvarNew mkFreshExprMVarAt lctx ( getLocalInstances) ( mvarId.getType) .syntheticOpaque ( mvarId.getTag)
mvarId.assign mvarNew
return mvarNew.mvarId!
/-- Creates a temporary local context where all names are exposed, and executes `k` -/
def withExposedNames (k : MetaM α) : MetaM α := do
withNewMCtxDepth <| withLCtx ( getLCtxWithExposedNames) ( getLocalInstances) k
end Lean.Meta

View File

@@ -28,6 +28,7 @@ import Lean.Meta.Tactic.Grind.Arith
import Lean.Meta.Tactic.Grind.Ext
import Lean.Meta.Tactic.Grind.MatchCond
import Lean.Meta.Tactic.Grind.MatchDiscrOnly
import Lean.Meta.Tactic.Grind.Diseq
namespace Lean

View File

@@ -14,10 +14,13 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Types
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Var
import Lean.Meta.Tactic.Grind.Arith.Cutsat.EqCnstr
import Lean.Meta.Tactic.Grind.Arith.Cutsat.SearchM
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Model
namespace Lean
builtin_initialize registerTraceClass `grind.cutsat
builtin_initialize registerTraceClass `grind.cutsat.model
builtin_initialize registerTraceClass `grind.cutsat.subst
builtin_initialize registerTraceClass `grind.cutsat.eq
builtin_initialize registerTraceClass `grind.cutsat.eq.unsat (inherited := true)
@@ -43,4 +46,13 @@ builtin_initialize registerTraceClass `grind.cutsat.le.upper (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.assign
builtin_initialize registerTraceClass `grind.cutsat.conflict
builtin_initialize registerTraceClass `grind.cutsat.diseq
builtin_initialize registerTraceClass `grind.cutsat.diseq.trivial (inherited := true)
builtin_initialize registerTraceClass `grind.debug.cutsat.eq
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq.split
builtin_initialize registerTraceClass `grind.debug.cutsat.backtrack
builtin_initialize registerTraceClass `grind.debug.cutsat.search
end Lean

View File

@@ -0,0 +1,24 @@
/-
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 Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
namespace Lean.Meta.Grind.Arith.Cutsat
def CooperSplit.numCases (s : CooperSplit) : Nat :=
let a := s.c₁.p.leadCoeff
let b := s.c₂.p.leadCoeff
match s.c₃? with
| none => if s.left then a.natAbs else b.natAbs
| some c₃ =>
let c := c₃.p.leadCoeff
let d := c₃.d
if s.left then
Int.lcm a (a * d / Int.gcd (a * d) c)
else
Int.lcm b (b * d / Int.gcd (b * d) c)
end Lean.Meta.Grind.Arith.Cutsat

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