Compare commits

...

385 Commits

Author SHA1 Message Date
Joachim Breitner
502a4281ab experiment: index recursor_rules by constructor index 2025-09-24 13:31:51 +02:00
Leonardo de Moura
9fc18b8ab4 doc: extra grind docstrings (#10486)
This PR adds and expands `grind` related docstrings.
2025-09-22 03:27:48 +00:00
Leonardo de Moura
852a3db447 chore: improve grind imports (#10491) 2025-09-22 01:25:41 +00:00
Lean stage0 autoupdater
d0d5d4ca39 chore: update stage0 2025-09-21 11:13:12 +00:00
Sebastian Ullrich
b32f3e8930 chore: revert "feat: add vectored write and fix rc issue in tcp and udp cancel functions" (#10485)
Reverts leanprover/lean4#10367 due to Windows build failure
2025-09-21 10:43:46 +00:00
David Thrane Christiansen
34f5fba54d chore: remove bootstrapping workaround (#10484)
This PR removes temporary bootstrapping workarounds introduced in PR
#10479.
2025-09-21 07:36:49 +00:00
Leonardo de Moura
4c9601e60f feat: support for injective functions in grind (#10483)
This PR completes support for injective functions in grind. See
examples:
```lean

/-! Add some injectivity theorems. -/

def double (x : Nat) := 2*x

@[grind inj] theorem double_inj : Function.Injective double := by
  grind [Function.Injective, double]

structure InjFn (α : Type) (β : Type) where
  f : α → β
  h : Function.Injective f

instance : CoeFun (InjFn α β) (fun _ => α → β) where
  coe s := s.f

@[grind inj] theorem fn_inj (F : InjFn α β) : Function.Injective (F : α → β) := by
  grind [Function.Injective, cases InjFn]

def toList (a : α) : List α := [a]

@[grind inj] theorem toList_inj : Function.Injective (toList : α → List α) := by
  grind [Function.Injective, toList]

/-! Examples -/

example (x y : Nat) : toList (double x) = toList (double y) → x = y := by
  grind

example (f : InjFn (List Nat) α) (x y z : Nat)
    : f (toList (double x)) = f (toList y) →
      y = double z →
      x = z := by
  grind
```
2025-09-21 06:31:46 +00:00
Leonardo de Moura
42be7bb5c7 fix: [grind inj] attribute (#10482)
This PR fixes symbol collection for the `@[grind inj]` attribute.
2025-09-21 04:14:17 +00:00
Leonardo de Moura
5f68c1662d refactor: generalize theorem activation in grind (#10481)
This PR generalizes the theorem activation function used in `grind`. 
The goal is to reuse it to implement the injective function module.
2025-09-21 02:50:55 +00:00
Leonardo de Moura
2d14d51935 fix: equality resolution in grind (#10480)
This PR fixes a bug in the equality resolution frontend used in `grind`.
2025-09-21 02:40:38 +00:00
Lean stage0 autoupdater
7cbeb14e46 chore: update stage0 2025-09-20 22:47:35 +00:00
David Thrane Christiansen
cee2886154 feat: improvements to Verso docstrings (#10479)
This PR implements module docstrings in Verso syntax, as well as adding
a number of improvements and fixes to Verso docstrings in general. In
particular, they now have language server support and are parsed at
parse time rather than elaboration time, so the snapshot's syntax tree
includes the parsed documentation.
2025-09-20 22:05:57 +00:00
Leonardo de Moura
35764213fc fix: grind sort internalization (#10477)
This PR ensures sorts are internalized by `grind`.
2025-09-20 18:31:20 +00:00
Sofia Rodrigues
6b92cbdfa4 feat: add vectored write and fix rc issue in tcp and udp cancel functions (#10367)
This PR adds vectored write for TCP and UDP (that helps a lot with not
copying the arrays over and over) and fix a RC issue in TCP and UDP
cancel functions with the line `lean_dec((lean_object*)udp_socket);` and
a similar one that tries to decrement the object inside of the `socket`.
2025-09-20 17:01:20 +00:00
Leonardo de Moura
72bb7cf364 fix: infer_let in the kernel (#10476)
This PR fixes the dead `let` elimination code in the kernel's
`infer_let` function.

Closes #10475
2025-09-20 16:26:46 +00:00
Sofia Rodrigues
4881c3042e refactor: replace Task with Async and minor changes to some basic Async functions (#10366)
This PR refactors the Async module to use the `Async` type in all of the
`Async` files.
2025-09-20 16:23:06 +00:00
Leonardo de Moura
ec7add0b48 doc: ! modifier in grind parameters (#10474)
This PR adds a doc string for the `!` parameter modifier in `grind`.
2025-09-20 08:06:05 +00:00
Leonardo de Moura
9b842b7554 fix: message context in grind code actions (#10473)
This PR ensures the code action messages produced by `grind` include the
full context
2025-09-20 08:02:12 +00:00
Leonardo de Moura
fc718eac88 feat: code action for grind parameters (#10472)
This PR adds a code action for `grind` parameters. We need to use
`set_option grind.param.codeAction true` to enable the option. The PR
also adds a modifier to instruct `grind` to use the "default" pattern
inference strategy.
2025-09-20 07:30:39 +00:00
Michał Dobranowski
8b3c82cce2 fix: lake: GH action template condition (#10459)
This PR fixes a conditional check in a GitHub Action template generated
by Lake.

Closes #10420.
2025-09-20 06:33:42 +00:00
Mac Malone
0d1b7e6c88 chore: lake: fix tests/lean (#10470)
The ordering of the `--setup` JSON object changed at some point,
breaking this test. This PR fixes it by avoiding the potential for such
breakages.
2025-09-20 02:11:50 +00:00
Leonardo de Moura
d898c9ed17 fix: grind canonicalizer (#10469)
This PR fixes an incorrect optimization in the `grind` canonicalizer.
See the new test for an example that exposes the problem.
2025-09-20 01:24:54 +00:00
Leonardo de Moura
c6abc3c036 feat: improve grind diagnostics (#10466)
This PR reduces noise in the 'Equivalence classes' section of the
`grind` diagnostics. It now uses a notion of *support expressions*.
Right now, it is hard-coded, but we will probably make it extensible in
the future. The current definition is

- `match`, `ite` and `dite`-applications. They have builtin support in
`grind`.
- Cast-like applications used by `grind`: `toQ`, `toInt`, `Nat.cast`,
`Int.cast`, and `cast`
- `grind` gadget applications (e.g., `Grind.nestedDecidable`)
- Projections of constructors (e.g., `{ x := 1, y := 2}.x`)
- Auxiliary arithmetic terms constructed by solvers such as `cutsat` and
`ring`.

If an equivalence class contains at most one non-support term, it goes
into the “others” bucket. Otherwise, we display the non-support elements
and place the support terms in a child node.

**BEFORE**:
<img width="1397" height="1558" alt="image"
src="https://github.com/user-attachments/assets/4fd4de31-7300-4158-908b-247024381243"
/>

**AFTER**:
<img width="840" height="340" alt="image"
src="https://github.com/user-attachments/assets/05020f34-4ade-49bf-8ccc-9eb0ba53c861"
/>

**Remark**: No information is lost; it is just grouped differently."
2025-09-19 23:44:30 +00:00
Leonardo de Moura
d07862db2a chore: skip cast-like operations in grind mbtc (#10465)
This PR skips cast-like helper `grind` functions during `grind mbtc`
2025-09-19 21:12:25 +00:00
Leonardo de Moura
8a79ef3633 chore: missing grind normalization (#10463)
This PR adds `Nat.sub_zero` as a `grind` normalization rule.
2025-09-19 18:50:39 +00:00
Leonardo de Moura
b1c82f776b chore: mbtc in grind cutsat (#10462)
Minor improvement to `grind mbtc` in `cutsat`.
2025-09-19 18:47:10 +00:00
Leonardo de Moura
f278f31469 fix: unnecessary case-splits in grind mbtc (#10461)
This PR fixes unnecessary case splits generated by the `grind mbtc`
module. Here, `mbtc` stands for model-based theory combination.
2025-09-19 17:24:57 +00:00
Joachim Breitner
38b4062edb feat: linear-size Ord instance (#10270)
This PR adds an alternative implementation of `Deriving Ord` based on
comparing `.ctorIdx` and using a dedicated matcher for comparing same
constructors (added in #10152). The new option
`deriving.ord.linear_construction_threshold` sets the constructor count
threshold (10 by default) for using the new construction.

It also (unconditionally) changes the implementation for enumeration
types to simply compare the `ctorIdx`.
2025-09-19 14:13:57 +00:00
Sebastian Graf
ae8dc414c3 feat: mvcgen invariants? to scaffold initial invariants (#10456)
This PR implements `mvcgen invariants?` for providing initial invariant
skeletons for the user to flesh out. When the loop body has an early
return, it will helpfully suggest `Invariant.withEarlyReturn ...` as a
skeleton.

```lean
def mySum (l : List Nat) : Nat := Id.run do
  let mut acc := 0
  for x in l do
    acc := acc + x
  return acc

/--
info: Try this:
  invariants
    · ⇓⟨xs, acc⟩ => _
-/
#guard_msgs (info) in
theorem mySum_suggest_invariant (l : List Nat) : mySum l = l.sum := by
  generalize h : mySum l = r
  apply Id.of_wp_run_eq h
  mvcgen invariants?
  all_goals admit

def nodup (l : List Int) : Bool := Id.run do
  let mut seen : HashSet Int := ∅
  for x in l do
    if x ∈ seen then
      return false
    seen := seen.insert x
  return true

/--
info: Try this:
  invariants
    · Invariant.withEarlyReturn (onReturn := fun r acc => _) (onContinue := fun xs acc => _)
-/
#guard_msgs (info) in
theorem nodup_suggest_invariant (l : List Int) : nodup l ↔ l.Nodup := by
  generalize h : nodup l = r
  apply Id.of_wp_run_eq h
  mvcgen invariants?
  all_goals admit
```
2025-09-19 14:05:24 +00:00
Sebastian Ullrich
7822ee4500 fix: check that compiler does not infer inconsistent types between modules (#10418)
This PR fixes a potential miscompilation when using non-exposed type
definitions using the module system by turning it into a static error. A
future revision may lift the restriction by making the compiler metadata
independent of the current module.
2025-09-19 12:36:47 +00:00
Joachim Breitner
8f22c56420 refactor: less public section in Eqns.lean files (#10454) 2025-09-19 11:52:56 +00:00
Joachim Breitner
0e122870be perf: mkNoConfusionCtors: cheaper inferType (#10455)
This PR changes `mkNoConfusionCtors` so that its use of `inferType` does
not have to reduce `noConfusionType`, to make #10315 really effective.
2025-09-19 10:51:17 +00:00
Sebastian Graf
13c23877d4 fix: reduce through lets in mvcgen main loop (#10453)
This PR makes `mvcgen` reduce through `let`s, so that it progresses over
`(have t := 42; fun _ => foo t) 23` by reduction to `have t := 42; foo
t` and then introducing `t`.
2025-09-19 08:21:04 +00:00
Kim Morrison
7fba12f8f7 chore: add test for website primes example (#10451)
(This test is currently failing. We either need to change the failing
line to `grind [!factorial_pos]`, or again change the behaviour of
grind.)
2025-09-19 04:56:28 +00:00
Kim Morrison
abb487a0c0 chore: include lean-lang.org in release checklist (#10450) 2025-09-19 03:46:32 +00:00
Leonardo de Moura
1091053824 chore: reportIssue! at grind ematch (#10449)
This PR ensures that issues reported by the E-matching module are
displayed only when `set_option grind.debug true` is enabled. Users
reported that these messages are too distracting and not very useful.
They are more valuable for library developers when annotating their
libraries.
2025-09-19 02:43:03 +00:00
Leonardo de Moura
545bd8a96c feat: add [grind inj] attribute (#10447)
This PR adds the `[grind inj]` attribute for marking injectivity
theorems for `grind`.
2025-09-19 00:49:05 +00:00
Sebastian Ullrich
2f9618f76b chore: make def used by proofwidgets public again (#10437) 2025-09-18 21:30:23 +00:00
Joachim Breitner
fa36fcd448 feat: linear-size BEq instance (#10268)
This PR adds an alternative implementation of `DerivingBEq` based on
comparing `.ctorIdx` and using a dedicated matcher for comparing same
constructors (added in #10152), to avoid the quadratic overhead of the
default match implementation. The new option
`deriving.beq.linear_construction_threshold` sets the constructor count
threshold (10 by default) for using the new construction. Such instances
also allow `deriving ReflBEq, LawfulBeq`, although these proofs for
these properties are still quadratic.
2025-09-18 21:27:25 +00:00
Henrik Böving
9a3b4b2716 fix: overeager inc insertion for large uint constants (#10444)
This PR fixes an overeager insertion of `inc` operations for large uint
constants.


Closes: #10443
2025-09-18 20:54:19 +00:00
Leonardo de Moura
9fb5ab8450 feat: helper definitions for injective function support in grind (#10445)
This PR adds helper definitions in preparation for the upcoming
injective function support in `grind`.
2025-09-18 19:42:15 +00:00
Leonardo de Moura
a62c0bce77 chore: missing grind modifier (#10441) 2025-09-18 14:44:57 +00:00
Lean stage0 autoupdater
3ce554abd7 chore: update stage0 2025-09-18 13:48:00 +00:00
Joachim Breitner
257c347f9f feat: reduceCtorIdx simproc (#10440)
This PR adds the `reduceCtorIdx` simproc which recognizes and reduces
`ctorIdx` applications. This is not on by default yet because it does
not use the discrimination tree (yet).
2025-09-18 13:05:14 +00:00
Sebastian Ullrich
ca1315e3ba fix: backtracking kernel errors under Elab.async (#10438)
This PR fixes an issue where notations and other overloadings would
signal kernel errors even though there exists a successful
interpretation.
2025-09-18 12:33:57 +00:00
Markus Himmel
197bc6cb66 feat: redefine String, part one (#10304)
This PR redefines `String` to be the type of byte arrays `b` for which
`b.IsValidUtf8`.

This moves the data model of strings much closer to the actual data
representation at runtime.

In the near future, we will

- provide variants of `String.Pos` and `Substring` that only allow for
valid positions
- redefine all `String` functions to be much closer to their C++
implementations

In the near-to-medium future we will then provide comprehensive
verification of `String` based on these refactors.
2025-09-18 11:36:52 +00:00
Luisa Cicolini
02ca710872 feat: add BitVec.ctz to bv_decide (#9298)
This PR adds support the Count Trailing Zeros operation `BitVec.ctz` to
the bitvector library and to `bv_decide`, relying on the existing `clz`
circuit. We also build some theory around `BitVec.ctz` (analogous to the
theory existing for `BitVec.clz`) and introduce lemmas
`BitVec.[ctz_eq_reverse_clz, clz_eq_reverse_ctz, ctz_lt_iff_ne_zero,
getLsbD_false_of_lt_ctz, getLsbD_true_ctz_of_ne_zero,
two_pow_ctz_le_toNat_of_ne_zero, reverse_reverse_eq,
reverse_eq_zero_iff]`.

`ctz` operation is common in numerous compiler intrinsics (see
[here](https://clang.llvm.org/docs/LanguageExtensions.html#intrinsics-support-within-constant-expressions))
and architectures (see
[here](https://en.wikipedia.org/wiki/Find_first_set)).

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-09-18 08:38:07 +00:00
Kim Morrison
3fbf080d72 chore: update script/release_notes.py for changelog-tactics (#10436) 2025-09-18 07:22:53 +00:00
Kim Morrison
4379002d05 feat: add reprove command for re-proving theorems with a specified tactic (#10434)
This PR adds `reprove N by T`, which effectively elaborates `example
type_of% N := by T`. It supports multiple identifiers. This is useful
for testing tactics.


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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-18 06:29:53 +00:00
Lean stage0 autoupdater
5d50ec90f9 chore: update stage0 2025-09-18 04:56:02 +00:00
Leonardo de Moura
6ca699b1ff feat: enable new E-matching pattern inference procedure in grind (#10432)
This PR enables the new E-matching pattern inference heuristic for
`grind`, implemented in PR #10422.
**Important**: Users can still use the old pattern inference heuristic
by setting:

```lean
set_option backward.grind.inferPattern true
```

In PR #10422, we introduced the new modifier `@[grind!]` for enabling
the minimal indexable subexpression condition. This option can now also
be set in `grind` parameters. Example:

```lean
opaque f : Nat → Nat
opaque fInv : Nat → Nat 
axiom fInv_f : fInv (f x) = x

/-- trace: [grind.ematch.pattern] fInv_f: [f #0] -/
#guard_msgs in 
set_option trace.grind.ematch.pattern true in
example {x y} : f x = f y → x = y := by
  /-
  The modifier `!` instructs `grind` to use the minimal indexable subexpression 
  (i.e., `f x` in this case).   
  -/
  grind [!fInv_f] 
```
2025-09-18 04:13:54 +00:00
Kim Morrison
c2d56fa031 chore: custom steps in release checklist for CSLib (#10433) 2025-09-18 01:12:00 +00:00
Lean stage0 autoupdater
b6d590ccc3 chore: update stage0 2025-09-17 21:44:32 +00:00
Sebastian Ullrich
719765ec5c feat: overhaul meta system (#10362)
This PR refines and clarifies the `meta` phase distinction in the module
system.

* `meta import A` without `public` now has the clarified meaning of
"enable compile-time evaluation of declarations in or above `A` in the
current module, but not downstream". This is now checked statically by
enforcing that public meta defs, which therefore may be referenced from
outside, can only use public meta imports, and that global evaluating
attributes such as `@[term_parser]` can only be applied to public meta
defs.
* `meta def`s may no longer reference non-meta defs even when in the
same module. This clarifies the meta distinction as well as improves
locality of (new) error messages.
* parser references in `syntax` are now also properly tracked as meta
references.
* A `meta import` of an `import` now properly loads only the `.ir` of
the nested module for the purposes of execution instead of also making
its declarations available for general elaboration.
* `initialize` is now no longer being run on import under the module
system, which is now covered by `meta initialize`.
2025-09-17 21:04:29 +00:00
Lean stage0 autoupdater
11b0e7d89c chore: update stage0 2025-09-17 18:46:58 +00:00
Leonardo de Moura
37f3f0e1e2 feat: minimal indexable subexpressions in grind parameters (#10430)
This PR ensures users can select the "minimal indexable subexpression"
condition in `grind` parameters. Example, they can now write `grind [!
-> thmName]`. `grind?` will include the `!` modifier whenever users had
used `@[grind!]`. This PR also fixes a missing case in the new pattern
inference procedure.
It also adjusts some `grind` annotations and tests in preparation for
setting the new pattern inference heuristic as the new default.
2025-09-17 18:04:05 +00:00
Henrik Böving
85645958f9 fix: overeager specialisation reuse in codegen (#10429)
This PR fixes and overeager reuse of specialisation in the code
generator.

The issue was originally discovered in
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Miscompilation.20.28incorrect.20code.29.20in.20new.20compiler/near/540037917
and occurs because the specialisation cache didnt't take the name of
alternatives in pattern matches
into account.
2025-09-17 17:35:40 +00:00
Leonardo de Moura
a80169165e chore: missing grind modifiers and local grind theorems config (#10428)
This PR makes explicit missing `grind` modifiers, and ensures `grind`
uses "minIndexable" for local theorems.
2025-09-17 16:15:16 +00:00
Robert J. Simmons
8dca311ba5 doc: update URLs that are currently pointing to redirects (#10397)
This PR updates several URLs that are currently pointing to redirects on
lean-lang.org, most importantly a few in the top-level README
2025-09-17 15:50:07 +00:00
Lean stage0 autoupdater
e6dfde1ad6 chore: update stage0 2025-09-17 14:32:29 +00:00
Joachim Breitner
e532ce95ce refactor: change how equations for structural recursion are proved (#10415)
This PR changes the order of steps tried when proving equational
theorems for structural recursion. In order to avoid goals that `split`
cannot handle, avoid unfolding the LHS of the equation to `.brecOn` and
`.rec` until after the RHS has been split into its final cases.

Fixes: #10195
2025-09-17 13:46:45 +00:00
Joachim Breitner
e74b81169d feat: split to generalize proof discriminants (#10425)
This PR lets the `split` tactic generalize discriminants that are not
free variables and proofs using `generalize`. If the only
non-fvar-discriminants are proofs, then this avoids the more elaborate
generalization strategy of `split`, which can fail with dependent
motives, thus mitigating issue #10424.
2025-09-17 12:18:30 +00:00
Markus Himmel
cf8ffc28d3 chore: kernel changes ahead of String redefinition (#10330)
This PR changes the defeq algorithm to perform `whnf` on the `String.mk`
expression it creates for string literals.

This is currently a no-op, but will no longer be one once `String` is
redefined so that `String.mk` is a regular function instead of a
constructor.
2025-09-17 09:12:07 +00:00
Marc Huisinga
d625aaa96f feat: server-side for trace search (#10365)
This PR implements the server-side for a new trace search mechanism in
the InfoView.

Demo:
![Search
demo](https://github.com/user-attachments/assets/f8f1cdfd-a4f2-4258-8cb8-360f64ea06e9)
2025-09-17 08:58:56 +00:00
Lean stage0 autoupdater
89e4f9815f chore: update stage0 2025-09-17 09:11:32 +00:00
Sebastian Ullrich
9002cc8761 fix: elaborate private inductive ctor in the private scope (#10423) 2025-09-17 08:39:27 +00:00
Lean stage0 autoupdater
5a7d663624 chore: update stage0 2025-09-17 03:26:25 +00:00
Leonardo de Moura
efb398b040 feat: new grind pattern inference heuristic and code action (#10422)
This PR implements the new E-matching pattern inference heuristic for
`grind`. It is not enabled yet. You can activate the new behavior using
`set_option backward.grind.inferPattern false`. Here is a summary of the
new behavior.

* `[grind =]`, `[grind =_]`, `[grind _=_]`, `[grind <-=]`: no changes;
we keep the current behavior.
  
* `[grind ->]`, `[grind <-]`, `[grind =>]`, `[grind <=]`: we stop using
the *minimal indexable subexpression* and instead use the first
indexable one.

* `[grind! <mod>]`: behaves like `[grind <mod>]` but uses the minimal
indexable subexpression restriction. We generate an error if the user
writes `[grind! =]`, `[grind! =_]`, `[grind! _=_]`, or `[grind! <-=]`,
since there is no pattern search in these cases.
  
* `[grind]`: it tries `=`, `=_`, `<-`, `->`, `<=`, `=>` with and without
the minimal indexable subexpression restriction. For the ones that work,
we generate a code action to encourage users to select the one they
prefer.

* `[grind!]`: it tries `<-`, `->`, `<=`, `=>` using the minimal
indexable subexpression restriction. For the ones that work, we generate
a code action to encourage users to select the one they prefer.

* `[grind? <mod>]`: where `<mod>` is one of the modifiers above, it
behaves like `[grind <mod>]` but also displays the pattern.
  
Example:
```lean
/--
info: Try these:
  • [grind =] for pattern: [f (g #0)]
  • [grind =_] for pattern: [r #0 #0]
  • [grind! ←] for pattern: [g #0]
-/
#guard_msgs in
@[grind] axiom fg₇ : f (g x) = r x x
```
2025-09-17 02:44:11 +00:00
Leonardo de Moura
4cbd1a439a feat: non-commutative semiring normalizer in grind (#10421)
This PR adds a normalizer for non-commutative semirings to `grind`.
Examples:
```lean
open Lean.Grind
variable (R : Type u) [Semiring R]

example (a b c : R) : a * (b + c) = a * c + a * b := by grind
example (a b : R) : (a + 2 * b)^2 = a^2 + 2 * a * b + 2 * b * a + 4 * b^2 := by grind
example (a b : R) : b^2 + (a + 2 * b)^2 = a^2 + 2 * a * b + b * (1+1) * a * 1 + 5 * b^2 := by grind
example (a b : R) : a^3 + a^2*b + a*b*a + b*a^2 + a*b^2 + b*a*b + b^2*a + b^3 = (a+b)^3 := by grind
```
2025-09-16 20:15:38 +00:00
Leonardo de Moura
20873d5d72 feat: helper theorem for normalizing non-commutative semirings (#10419)
This PR adds the helper theorem `eq_normS_nc` for normalizing
non-commutative semirings. We will use this theorem to justify
normalization steps in the `grind ring` module.
2025-09-16 18:09:34 +00:00
Leonardo de Moura
4c1830e5ae refactor: semiring support in grind ring (#10403)
This PR reduces a bit of redundancy in the `grind ring`.
2025-09-16 17:37:55 +00:00
Joachim Breitner
7b75db7c6e refactor: use deriving LawfulBEq in Init (#10411)
This PR starts using `deriving LawfulBEq` in `Init`, removing some hairy
hand-rolled proofs.
2025-09-16 16:26:32 +00:00
Joachim Breitner
8d418201a6 fix: use with_reducible in deriving_LawfulEq_tactic_step (#10417)
This PR changes the automation in `deriving_LawfulEq_tactic_step` to use
`with_reducible` when asserting the shape of the goal using `change`, so
that we do not accidentally unfold `x == x'` calls here. Fixes #10416.
2025-09-16 16:07:42 +00:00
Lean stage0 autoupdater
850a4c897f chore: update stage0 2025-09-16 13:43:34 +00:00
Joachim Breitner
186f5a6960 feat: deriving ReflBEq and LawfulBEq (#10351)
This PR adds the ability to do `deriving ReflBEq, LawfulBEq`. Both
classes have to listed in the `deriving` clause. For `ReflBEq`, a simple
`simp`-based proof is used. For `LawfulBEq`, a dedicated,
syntax-directed tactic is used that should work for derived `BEq`
instances. This is meant to work with `deriving BEq` (but you can try to
use it on hand-rolled `@[methods_specs] instance : BEq…` instances).
Does not support mutual or nested inductives.
2025-09-16 12:58:01 +00:00
Lean stage0 autoupdater
917715c862 chore: update stage0 2025-09-16 11:06:37 +00:00
Joachim Breitner
50435417ac chore: remove comment from src/stdlib_flags.h (#10409)
This PR removes an update-stage0-comment from
`src/stdlib_flags.h`. Again. Sorry for that.
2025-09-16 10:39:27 +00:00
Joachim Breitner
9deff2751f refactor: use reduceBEq in Init (#10398)
This PR uses the `reduceBEq` simproc in Init, but mostly only for
testing, because afer #10351 this code will be derived.
2025-09-16 10:35:46 +00:00
Joachim Breitner
f3d93970dc feat: @[method_specs_simp] in Init (#10407)
This PR adds `@[method_specs_simp]` in `Init` for type classes like
`HAppend`.
2025-09-16 10:27:33 +00:00
Lean stage0 autoupdater
d1577fda7a chore: update stage0 2025-09-16 09:49:47 +00:00
Joachim Breitner
ca10fd7c4f fix: method spec theorems to be private when appropriate (#10406)
This PR improves upon #10302 to properly make the method spec theorems
private if the implementation function is not exposed.
2025-09-16 09:20:04 +00:00
Kim Morrison
a1cd945e82 chore: remove deprecated Xor (#10404) 2025-09-16 03:41:20 +00:00
Kim Morrison
9c372b9bc2 chore: begin development cycle for v4.25.0 (#10402) 2025-09-16 00:25:06 +00:00
Kim Morrison
38214ac121 chore: fix to release scripts (#10401) 2025-09-16 00:23:55 +00:00
Lean stage0 autoupdater
6d30aeefe5 chore: update stage0 2025-09-15 17:51:09 +00:00
Kyle Miller
112fa51e08 fix: keep abstract nested proofs procedure from hiding sorry warning (#10388)
This PR fixes a bug where definitions with nested proofs that contain
`sorry` might not report "warning: declaration uses 'sorry'" if the
proof has the same type as another nested proof from a previous
declaration. The bug only affected log messages; `#print axioms` would
still correctly report uses of `sorryAx`.

The fix is that now the abstract nested proofs procedure does not
consult the aux lemma cache if the proof contains a `sorry`.

Closes #10196
2025-09-15 17:07:49 +00:00
David Thrane Christiansen
9b53e39804 feat: activate Verso docstring builtins (#10386)
This PR activates the builtin expanders for Verso docstrings.
2025-09-15 17:07:33 +00:00
Kyle Miller
ede1acfb44 fix: let anonymous constructor notation elaborate with insufficient arguments (#10391)
This PR gives anonymous constructor notation (`⟨x,y⟩`) an error recovery
mechanism where if there are not enough arguments then synthetic sorries
are inserted for the missing arguments and an error is logged, rather
than outright failing.

Closes #9591.
2025-09-15 16:44:34 +00:00
Kyle Miller
0799e5c4e9 fix: make sure error ranges for if tactic are correct (#10392)
This PR fixes an issue with the `if` tactic where errors were not placed
at the correct source ranges. It also adds some error recovery to avoid
additional errors about unsolved goals on the `if` token when the tactic
has incomplete syntax.

Closes #7972
2025-09-15 16:40:11 +00:00
Lean stage0 autoupdater
32a4c88986 chore: update stage0 2025-09-15 17:07:15 +00:00
Joachim Breitner
4cf3c0ae67 feat: reduceBEq and reduceOrd simprocs (#10394)
This PR adds the `reduceBEq` and `reduceOrd` simprocs. They rewrite
occurrences of `_ == _` resp. `Ord.compare _ _` if both arguments are
constructors and the corresponding instance has been marked with
`@[method_specs]` (introduced in #10302), which now by default is the
case for derived instances.
2025-09-15 16:24:44 +00:00
Lean stage0 autoupdater
06ba748221 chore: update stage0 2025-09-15 15:31:41 +00:00
Joachim Breitner
d2d32f13c0 chore: remove comment from src/stdlib_flags.h (#10396)
This PR removes an update-stage0-comment from
`src/stdlib_flags.h`; these comments should be added to
`stage0/src/stdlib_flags.h`.
2025-09-15 15:03:12 +00:00
Joachim Breitner
9aa6448fa9 feat: use @[method_specs] when deriving BEq and Ord (#10346)
This PR lets `deriving BEq` and `deriving Ord` use `@[method_specs]`
from #10302 when applicable (i.e. when not using `partial`).
2025-09-15 14:58:00 +00:00
Sofia Rodrigues
3bea7e209e feat: add signal handling support using libuv (#9258)
This PR adds support for signal handlers to the Lean standard library.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-09-15 13:09:50 +00:00
Joachim Breitner
88fa4212d7 feat: @[method_specs] to generate specification theorems from class instances (#10302)
This PR introduces the `@[specs]` attribute. It can be applied to
(certain) type class instances and define “specification theorems” for
the class’ operations, by taking the equational theorems of the
implementation function mentioned in the type class instance and
rephrasing them in terms of the overloaded operations. Fixes #5295.

Example:

```
inductive L α where
  | nil  : L α
  | cons : α → L α → L α

def L.beqImpl [BEq α] : L α → L α → Bool
  | nil, nil           => true
  | cons x xs, cons y ys => x == y && L.beqImpl xs ys
  | _, _               => false

@[method_specs] instance [BEq α] : BEq (L α) := ⟨L.beqImpl⟩

/--
info: theorem instBEqL.beq_spec_2.{u_1} : ∀ {α : Type u_1} [inst : BEq α] (x_2 : α) (xs : L α) (y : α) (ys : L α),
  (L.cons x_2 xs == L.cons y ys) = (x_2 == y && xs == ys)
-/
#guard_msgs(pass trace, all) in
#print sig instBEqL.beq_spec_2
```

It also introduces the `method_specs_norm` simpset to allow registering
further normalization of the theorems. The intended use of this is to
rewrite, say, `Append.append` to the `HAppend.hAppend` (i.e. `++`) that
the user wants to see. Library annotations to follow in a separate PR.
2025-09-15 11:17:06 +00:00
David Thrane Christiansen
97464c9d7f fix: trailing whitespace setting for string literals was ignored (#10389)
This PR fixes a bug where string literal parsing ignored its trailing
whitespace setting.
2025-09-15 09:51:56 +00:00
Sebastian Ullrich
8df968de01 feat: have example default to the private scope (#10168) 2025-09-15 09:10:56 +00:00
Lean stage0 autoupdater
d869c38e7b chore: update stage0 2025-09-15 05:12:36 +00:00
Kim Morrison
4d8d502754 chore: remove bad grind annotation on List.eq_nil_of_map_eq_nil (#10356) 2025-09-15 04:33:16 +00:00
David Thrane Christiansen
8e1df86939 feat: improvements to Verso docstrings (#10382)
This PR makes the builtin Verso docstring elaborators bootstrap
correctly, adds the ability to postpone checks (which is necessary for
resolving forward references and bootstrapping issues), and fixes a
minor parser bug.
2025-09-15 04:28:29 +00:00
Kim Morrison
4ff33eaef5 feat: updates to release process for cslib (#10385)
This PR updates the release checklist scripts to handle a corner case in
Cslib.
2025-09-15 01:41:17 +00:00
Kim Morrison
22a4cab8c7 feat: updates to release process for v4.23.0 (#10383)
This PR includes some improvements to the release process, making the
updating of `stable` branches more robust, and including `cslib` in the
release checklist.
2025-09-14 23:52:19 +00:00
Lean stage0 autoupdater
1e12cdddc0 chore: update stage0 2025-09-14 22:54:32 +00:00
Kyle Miller
cab33ac1da feat: syntax name heuristic for unicode(...) (#10381)
This PR sets the `syntax` naming heuristic for `unicode(" → ", " -> ")`
to use `→` rather than `→->`.

Continuation of #10373.
2025-09-14 21:53:48 +00:00
Leonardo de Moura
6b97e41650 feat: sanity check for instances in grind ring (#10380)
This PR implements sanity checks in the `grind ring` module to ensure
the instances synthesized by type class resolution are definitionally
equal to the corresponding ones in the `grind` core classes. The
definitional equality test is performed with reduction restricted to
reducible definitions and instances.
2025-09-14 21:04:40 +00:00
Lean stage0 autoupdater
c2521e94e1 chore: update stage0 2025-09-14 21:03:42 +00:00
Kyle Miller
f771dea78b fix: make sure app elaborator eta feature does not result in capturable variables (#10377)
This PR fixes an issue where the "eta feature" in the app elaborator,
which is invoked when positional arguments are skipped due to named
arguments, results in variables that can be captured by those named
arguments. Now the temporary local variables that implement this feature
get fresh names. The names used for the closed lambda expression still
use the original parameter names.

Closes #6373
2025-09-14 20:19:50 +00:00
Kyle Miller
02a4713875 feat: enable notationItem in "mixfix" notation commands (#10378)
This PR enables using `notation` items in
`infix`/`infixl`/`infixr`/`prefix`/`postfix`. The motivation for this is
to enable being able to use `pp.unicode`-aware parsers. A followup PR
can combine core parsers as such:
```lean
infixr:30 unicode(" ∨ ", " \\/ ") => Or
```

Continuation of #10373.
2025-09-14 18:54:36 +00:00
Kyle Miller
7407534eb8 feat: include := in the atomic part of tactic configuration items (#10379)
This PR modifies the syntax for tactic configurations. Previously just
`(ident` would commit to tactic configuration item parsing, but now it
needs to be `(ident :=`. This enables reliably using tactic
configurations before the `term` category. For example, given `syntax
"my_tac" optConfig term : tactic`, it used to be that `my_tac (x + y)`
would have an error on `+` with "expected `:=`", but now it parses the
term.

An additional rationale is that these are like named arguments; (1)
terms can't begin with named arguments so now there is no parsing
ambiguity and (2) `Parser.Term.namedArgument` indeed already includes
`:=` in the atomic part.
2025-09-14 18:53:47 +00:00
Kyle Miller
3f80e530d3 feat: suppress safe shadowing within fun binders (#10376)
This PR modifies pretty printing of `fun` binders, suppressing the safe
shadowing feature among the binders in the same `fun`. For example,
rather than pretty printing as `fun x x => 0`, we now see `fun x x_1 =>
0`. The calculation is done per `fun`, so for example `fun x => id fun x
=> 0` pretty prints as-is, taking advantage of safe shadowing.

The motivation for this change is that many users have reported that
safe shadowing within the same `fun` is confusing.
2025-09-14 15:54:59 +00:00
Lean stage0 autoupdater
3146f6c651 chore: update stage0 2025-09-14 08:05:12 +00:00
Leonardo de Moura
22aab5c3bb feat: non-commutative ring normalizer in grind (#10375)
This PR adds support for non-commutative ring normalization in `grind`.
The new normalizer also accounts for the `IsCharP` type class. Examples:
```lean
open Lean Grind

variable (R : Type u) [Ring R]
example (a b : R) : (a + 2 * b)^2 = a^2 + 2 * a * b + 2 * b * a + 4 * b^2 := by grind
example (a b : R) : (a + 2 * b)^2 = a^2 + 2 * a * b + -b * (-4) * a - 2*b*a + 4 * b^2 := by grind

variable [IsCharP R 4]
example (a b : R) : (a - b)^2 = a^2 - a * b - b * 5 * a + b^2 := by grind
example (a b : R) : (a - b)^2 = 13*a^2 - a * b - b * 5 * a + b*3*b*3 := by grind
```
2025-09-14 07:35:08 +00:00
Kyle Miller
7e9ea00ac0 feat: add option pp.piBinderNames (#10374)
This PR adds the options `pp.piBinderNames` and
`pp.piBinderNames.hygienic`. Enabling `pp.piBinderNames` causes
non-dependent pi binder names to be pretty printed, rather than be
omitted. When `pp.piBinderNames.hygienic` is false (the default) then
only non-hygienic such biner names are pretty printed. Setting `pp.all`
enables `pp.piBinderNames` if it is not otherwise explicitly set.

Implementation note: this is exposing the secret pretty printer option
`pp.piBinderNames` that was being used within the signature pretty
printer.

Closes #1134.
2025-09-14 05:15:04 +00:00
Kyle Miller
409cbe1da9 fix: make rw collect only new goals, occurs check (#10306)
This PR fixes a few bugs in the `rw` tactic: it could "steal" goals
because they appear in the type of the rewrite, it did not do an occurs
check, and new proof goals would not be synthetic opaque. This PR also
lets the `rfl` tactic assign synthetic opaque metavariables so that it
is equivalent to `exact rfl`.

Implementation note: filtering old vs new is not sufficient. This PR
partially addresses the bug where the rw tactic creates natural
metavariables for each of the goals; now new proof goals are synthetic
opaque.

Metaprogramming API: Instead of `Lean.MVarId.rewrite` prefer
`Lean.Elab.Tactic.elabRewrite` for elaborating rewrite theorems and
applying rewrites to expressions.

Closes #10172
2025-09-14 04:44:55 +00:00
Kyle Miller
3e4fa12c72 feat: add unicode(...) parser syntax and pp.unicode option (#10373)
This PR adds a `pp.unicode` option and a `unicode("→", "->")` syntax
description alias for the lower-level `unicodeSymbol "→" "->"` parser.
The syntax is added to the `notation` command as well. When `pp.unicode`
is true (the default) then the first form is used when pretty printing,
and otherwise the second ASCII form is used. A variant, `unicode("→",
"->", preserveForPP)` causes the `->` form to be preferred; delaborators
can insert `→` directly into the syntax, which will be pretty printed
as-is; this allows notations like `fun` to use custom options such as
`pp.unicode.fun` to opt into the unicode form when pretty printing.

Additionally:
- Adds more documentation for the `symbol` and `nonReservedSymbol`
parser descriptions.
- Adds documentation for the
`infix`/`infixr`/`infixl`/`prefix`/`postfix` commands.
- The parenthesizers for symbols are improved to backtrack if the atom
doesn't match.
- Fixes a bug where `&"..."` symbols aren't validated.

This is partial progress for issue #1056. What remains is enabling
`unicode(...)` for mixfix commands and then making use of it for core
notation.
2025-09-14 04:40:03 +00:00
Mac Malone
ed5dc328d9 refactor: import Lake.Util.* from Lake (#10371)
This PR explicitly imports `Lake.Util` submodules in `Lake`, ensuring
Lake utilities are consistently available by default in configuration
files.

It also simplifies the Lake globs for the core build to ensure all Lake
submodules are built (even if they are not imported).
2025-09-13 13:56:54 +00:00
Anne Baanen
2bbf5db04f fix: add infotree context to classical tactic (#10332)
This PR ensures that the infotree recognizes `Classical.propDecidable`
as an instance, when below a `classical` tactic.

The `classical` tactic modifies the environment that the subsequent
sequence of tactics runs in (by making `Classical.propDecidable` an
instance). However, it does not add a corresponding `InfoTree.context`
node, so its effects are not visible when we want to replay a tactic
sequence (for example when running a tactic in the tactic analysis
framework). We should add a call to `Lean.Elab.withSafeInfoContext` to
remedy this issue.

There are two potential places to add this class: in the meta-level
`Lean.Elab.Tactic.classical` wrapper, or the tactic-level
`evalClassical` tactic elaborator. I chose the latter since meta-level
does not have access to info tree operations (unless we add many
parameters to `Lean.Elab.Tactic.classical`: `[MonadNameGenerator m]
[MonadOptions m] [MonadMCtx m] [MonadResolveName m] [MonadFileMap m]`).

A testcase that uses the tactic analysis framework is available here:
https://github.com/leanprover-community/mathlib4/pull/29501
2025-09-12 16:30:44 +00:00
Sebastian Ullrich
116b708269 chore: CI: ensure cache reuse in update-stage0 (#10359) 2025-09-12 14:56:32 +00:00
Lean stage0 autoupdater
4b6eab762f chore: update stage0 2025-09-12 13:30:23 +00:00
Sebastian Ullrich
9d6f391414 fix: missing docs linter under the module system (#10361) 2025-09-12 12:41:06 +00:00
Sebastian Ullrich
245ede65b5 feat: meta initialize (#10360)
This PR adds syntax for defining compile-time initializers under the
module system, with other initializers to be restricted from running at
compile time in a follow-up PR.
2025-09-12 12:39:06 +00:00
Lean stage0 autoupdater
2422b9db87 chore: update stage0 2025-09-12 11:43:21 +00:00
Sebastian Ullrich
3f9f8f094d chore: split Std.Do.SPred.Notation in preparation for meta semantics restrictions (#10358) 2025-09-12 11:09:26 +00:00
Joachim Breitner
cf18337157 perf: use per-constructor noConfusion in mkNoConfusion (#10318)
This PR uses the per-constructor `noConfusion` principles (from #10315)
in the `mkNoConfusion` app builder, if possible. This means they are
used by `injection`, `grind`, `simp` and other places. This brings
notable performance improvements when dealing with inductives with a
large number of constructors.
2025-09-12 08:42:24 +00:00
Joachim Breitner
3cf7fdcbe0 feat: per-constructor noConfusion constructions (#10315)
This PR adds `T.ctor.noConfusion` declarations, which are
specializations of `T.noConfusion` to equalities between `T.ctor`. The
point is to avoid reducing the `T.noConfusionType` construction every
time we use `injection` or a similar tactic.

```lean
Vec.cons.noConfusion.{u_1, u} {α : Type u} (P : Sort u_1) {n : Nat}
  (x : α) (xs : Vec α n) (x' : α) (xs' : Vec α n)
  (h : Vec.cons x xs = Vec.cons x' xs')
  (k : n = n → x = x' → xs ≍ xs' → P) : P
```

The constructions are not as powerful as `T.noConfusion` when the
indices of the inductive type are not just constructor parameters (or
constructor applications of these parameters), so the full
`T.noConfusion` construction is still needed as a fallback.

It may seem costly to generate these eagerly, but given that we eagerly
generate injectivity theorems already, and we will use them there, it
seems reasonable for now.

To further reduce the cost, we only generate them for constructors with
fields (for others, the `T.noConfusion` theorem doesn't provide any
information), and we use `macro_inline` to prevent the compiler from
creating code for these, given that the compiler has special support for
`T.noConfusion` that we want it to use).

An earlier version of this PR also removed trivial equations and
un-HEq-ed others, leading to
```
 (k : x = x' → xs = xs' → P) 
```
in the example above. I backed out of that change, as it makes it harder
for tactics like `injectivity` to know how often to `intro`, so better
to keep things uniform.
2025-09-12 08:00:12 +00:00
Paul Reichert
caa0eacea8 feat: ranges in UInt* (#10303)
This PR adds range support to`BitVec` and the `UInt*` types. This means
that it is now possible to write, for example, `for i in (1 : UInt8)...5
do`, in order to loop over the values 1, 2, 3 and 4 of type `UInt8`.
2025-09-12 07:52:45 +00:00
David Thrane Christiansen
b8e584a054 fix: restore monad-polymorphism to some docstring functions (#10357)
This PR makes the Markdown docstring functions monad-polymorphic again,
and improves documentation for the internal docstring API.
2025-09-12 07:41:47 +00:00
Paul Reichert
ae682ed225 feat: more iterator/range lemmas about toList and toArray (#10244)
This PR adds more lemmas about the `toList` and `toArray` functions on
ranges and iterators. It also renames `Array.mem_toArray` into
`List.mem_toArray`.
2025-09-12 07:14:28 +00:00
Cameron Zwarich
b64111d5a8 fix: convert .proj on builtin types to use projection functions (#10355)
This PR changes `toLCNF` to convert `.proj` for builtin types to use
projection functions instead.

Fixes #10078.
2025-09-12 00:19:45 +00:00
Kim Morrison
72cc6c85eb chore: correct order of implicit arguments for Injective/Surjective API (#10354) 2025-09-11 23:30:19 +00:00
Sofia Rodrigues
a966ce64ca feat: add useful functions in Parsec, add error variant and Std.Data.ByteSlice (#9599)
This PR adds the type `Std.Internal.Parsec.Error`, which contains the
constructors `.eof` (useful for checking if parsing failed due to not
having enough input and then retrying when more input arrives that is
useful in the HTTP server) and `.other`, which describes other errors.
It also adds documentation to many functions, along with some new
functions to the `ByteArray` Parsec, such as `peekWhen?`, `octDigit`,
`takeWhile`, `takeUntil`, `skipWhile`, and `skipUntil`.
2025-09-11 14:53:41 +00:00
Lean stage0 autoupdater
5c88a2bf56 chore: update stage0 2025-09-11 13:47:30 +00:00
Sebastian Ullrich
73c85b177e refactor: split Init.Meta in preparation for meta semantics restrictions (#10343) 2025-09-11 13:01:03 +00:00
Kim Morrison
5c06c79c15 chore: fix remainining discrepancies for change in grind pattern heuristics (#10347)
This PR is followup to the change in grind pattern heuristics from
#10342, typically resolving the discrepancy by writing out an explicit
`grind_pattern` for the intended pattern. The new behaviour is more
aggressive, because it selects smaller patterns.
2025-09-11 12:48:52 +00:00
Lean stage0 autoupdater
c8117a34c1 chore: update stage0 2025-09-11 12:27:01 +00:00
Sebastian Ullrich
a5f5d793d7 perf: implement jumping to native [export] decls in the interpreter (#10345)
This PR allows the interpreter to jump to native code of `[export]`
declarations, which can increase performance as well as the
effectiveness of `interpreter.prefer_native=true` during bootstrapping.
2025-09-11 11:41:16 +00:00
David Thrane Christiansen
61c46fd5f8 fix: mark doc parser tests as eol=lf (#10344)
This PR sets the eol Git attribute on docstring parser tests. This is to
stop them from failing on Windows due to line ending translation.
2025-09-11 11:19:01 +00:00
Joachim Breitner
e7d1cdd36a refactor: reimplement mkNoConfusionType in Lean (#10334)
This PR reimplements `mkNoConfusionType` in lean, thus removing the
remaining C code related to this construction.

Also uses the ctor elimination principles only when there are more than
three ctors.
2025-09-11 07:56:59 +00:00
Kim Morrison
dfcb5bb3a8 chore: remove a bad grind algebra instance (#10324)
This PR disables an unused instance that causes expensive typeclass
searches.
2025-09-11 06:44:47 +00:00
Kim Morrison
01ed345643 chore: more review of @[grind] annotations (#10340)
This PR completes the review of `@[grind]` annotations without a sigil
(e.g. `=` or `←`), replacing most of them with more specific annotations
or patterns.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2025-09-11 06:09:52 +00:00
Lean stage0 autoupdater
176fb1cf0e chore: update stage0 2025-09-11 06:13:42 +00:00
Leonardo de Moura
6b387da032 feat: new E-matching pattern inference for grind (#10342)
This PR implements a new E-matching pattern inference procedure that is
faithful to the behavior documented in the reference manual regarding
minimal indexable subexpressions. The old inference procedure was
failing to enforce this condition. For example, the manual documents
`[grind ->]` as follows

`[@grind →]` selects a multi-pattern from the hypotheses of the theorem.
In other words, `grind` will use the theorem for forwards reasoning.

To generate a pattern, it traverses the hypotheses of the theorem from
left to right. Each time it encounters a **minimal indexable
subexpression** which covers an argument which was not previously
covered, it adds that subexpression as a pattern, until all arguments
have been covered.

That said, the new procedure is currently disabled, and the following
option must be used to enable it.
```
set_option backward.grind.inferPattern false
```
Users can inspect differences between the old a new procedures using the
option
```
set_option backward.grind.checkInferPatternDiscrepancy true 
```
Example:
```lean
/--
warning: found discrepancy between old and new `grind` pattern inference procedures, old:
  [@List.length #2 (@toList _ #1 #0)]
new:
  [@toList #2 #1 #0]
use `set_option backward.grind.inferPattern true` to force old procedure
-/
#guard_msgs in
set_option backward.grind.checkInferPatternDiscrepancy true in
@[grind] theorem Vector.length_toList' (xs : Vector α n) : xs.toList.length = n := by sorry
```
2025-09-11 05:27:11 +00:00
Kim Morrison
c3667e2861 feat: upstream Function.Injective/Surjective (#10341)
This PR moves the definitions and basic facts about `Function.Injective`
and `Function.Surjective` up from Mathlib. We can do a better job of
arguing via injectivity in `grind` if these are available.
2025-09-11 04:04:46 +00:00
Leonardo de Moura
33266b23cd chore: use [grind =] (#10337) 2025-09-11 03:21:37 +00:00
Leonardo de Moura
a4a2bfa426 fix: minor issues in grind (#10339)
This PR fixes a few minor issues and applies a few cleanups.
2025-09-11 02:54:36 +00:00
Kim Morrison
b7520e7232 chore: cleanup grind annotations in List (#10338)
This PR updates `@[grind]` annotations which should be `@[grind =]`, for
robustness (and, presumably, in some fraction of cases the existing
heuristic for `@[grind]` is already too liberal).
2025-09-11 02:36:18 +00:00
Leonardo de Moura
0b84c3912e fix: over applied Grind.nestedProof marker (#10335)
This PR fixes the nested proof term detection in `grind`. It must check
whether the gadget `Grind.nestedProof` is over-applied.

closes #10327
2025-09-10 16:48:36 +00:00
Joachim Breitner
e96467f500 refactor: introduce SameCtorUtils (#10316)
This PR shares common functionality relate to equalities between same
constructors, and when these are type-correct. In particular it uses the
more complete logic from `mkInjectivityThm` also in other places, such
as `CasesOnSameCtor` and the deriving code for `BEq`, `DecidableEq`,
`Ord`, for more consistency and better error messages.
2025-09-10 14:32:58 +00:00
Sebastian Ullrich
bdab63048a doc: testing Lean while avoiding rebuilds for downstream projects (#10328) 2025-09-10 13:53:34 +00:00
Joachim Breitner
30a041902b refactor: implement mkNoConfusionImp in Lean (#10331)
This PR implements `mkNoConfusionImp` in Lean rather than in C. This
reduces our reliance on C, and may bring performance benefits from not
reducing `noConfusionType` during elaboration time (it still gets
reduced by the kernel when type-checking).
2025-09-10 13:51:23 +00:00
Eric Wieser
fbcad8f593 feat: allow separate handlers for literals and interpolations in expandInterpolatedStr (#6763)
This PR makes it possible to write custom interpolation notation which
treats interpolated `String`s specially.

Sometimes it is desirable for `let w := "world"; foo!"hello {w}"` and
`foo!"hello world"` to mean different things; for instance, if debugging
and wanting to show all interpolands with `repr`. The current approach
forces `hello` to also be rendered with `repr`, which is not desirable.

This doesn't modify any existing formatters.

Requested in [#lean4 > ✔ dbg_trace should use &#96;Repr&#96; instance @
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/.E2.9C.94.20dbg_trace.20should.20use.20.60Repr.60.20instance/near/495082575)

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-09-10 13:22:17 +00:00
Sebastian Ullrich
0a6bd5c0c6 chore: make Environment.evalConst [noinline] 2025-09-10 12:56:42 +02:00
Sebastian Ullrich
de2e935f30 fix: run @[init] declarations in declaration order (#10217)
This PR ensures `@[init]` declarations such as from `initialize` are run
in the order they were declared on import.

Fixes #10175
2025-09-10 09:52:10 +00:00
Sebastian Ullrich
57bce526f9 fix: further exporting control (#10261)
More proof generation nested in general metaprograms, uncovered by Shake
2025-09-10 09:14:11 +00:00
Sebastian Ullrich
b136906939 perf: do not publicly export docstrings (#10305)
This PR ensures changes to docstrings do not trigger downstream rebuilds
under the module system.
2025-09-10 09:10:04 +00:00
Lean stage0 autoupdater
f4c7a0d25c chore: update stage0 2025-09-10 07:49:18 +00:00
David Thrane Christiansen
3e2124bb48 feat: docstrings with Verso syntax (#10307)
This PR upstreams the Verso parser and adds preliminary support for
Verso in docstrings. This will allow the compiler to check examples and
cross-references in documentation.

After a `stage0` update, a follow-up PR will add the appropriate
attributes that allow the feature to be used. The parser tests from
Verso also remain to be upstreamed, and user-facing documentation will
be added once the feature has been used on more internals.
2025-09-10 07:03:57 +00:00
Leonardo de Moura
fc6a6cc4e2 fix: unnecessary NatModules in grind linarith (#10326)
This PR fixes a performance issue in `grind linarith`. It was creating
unnecessary `NatModule`/`IntModule` structures for commutative rings
without an order. This kind of type should be handled by `grind ring`
only.
2025-09-10 03:51:51 +00:00
Kim Morrison
bb61a2d481 chore: add grind test for numeral normalization (#10292) 2025-09-10 03:19:49 +00:00
Leonardo de Moura
2d8de4235d feat: model-based theory combination for ToInt types (#10325)
This PR implements model-based theory combination for types `A` which
implement the `ToInt` interface. Examples:
```lean
example {C : Type} (h : Fin 4 → C) (x : Fin 4)
    : 3 ≤ x → x ≤ 3 → h x = h (-1) := by
  grind

example {C : Type} (h : UInt8 → C) (x y z w : UInt8)
    : y + 1 + w ≤ x + w → x + w ≤ z → z ≤ y + w + 1 → h (x + w) = h (y + w + 1) := by
  grind

example {C : Type} (h : Fin 8 → C) (x y w r : Fin 8)
    : y + 1 + w ≤ r → r ≤ y + w + x → x = 1 → h r = h (y + w + 1) := by
  grind
```
2025-09-10 03:12:11 +00:00
Kim Morrison
a0ecff4610 chore: remove over-eager grind lemma eq_empty_of_append_eq_empty (#10162)
This PR removes `grind →` annotations that fire too often, unhelpfully.
It would be nice for `grind` to instantiate these lemmas, but only if
they already see `xs ++ ys` and `#[]` in the same equivalence class, not
just as soon as it sees `xs ++ ys`.

In the meantime, let's see what is using these.
2025-09-10 02:35:54 +00:00
Kim Morrison
923c3d10a2 feat: cutsat and grobner frontends for grind (#10322)
This PR introduces limited functionality frontends `cutsat` and
`grobner` for `grind`. We disable theorem instantiation (and case
splitting for `grobner`), and turn off all other solvers. Both still
allow `grind` configuration options, so for example one can use `cutsat
+ring` (or `grobner +cutsat`) to solve problems that require both.

For `cutsat`, it is helpful to instantiate a limited set of theorems
(e.g. `Nat.max_def`). Currently this isn't supported, but we intend to
add this later.
2025-09-10 02:26:52 +00:00
Leonardo de Moura
ac4c752608 fix: OfNat.ofNat grind canonicalizer (#10323)
This PR fixes the `grind` canonicalizer for `OfNat.ofNat` applications.
Example:
```lean
example {C : Type} (h : Fin 2 → C) :
    -- `0` in the first `OfNat.ofNat` is not a raw literal
    h (@OfNat.ofNat (Fin (1 + 1)) 0 Fin.instOfNat) = h 0 := by
  grind
```
2025-09-10 00:21:04 +00:00
Leonardo de Moura
4d2576362b fix: use local metavar ids in grind (#10321)
This PR ensures that the auxiliary temporary metavariable IDs created by
the E-matching module used in `grind` are not affected by what has been
executed before invoking `grind`. The goal is to increase `grind`’s
robustness.

For example, in the E-matching module we use `Expr.quickLt` to sort
candidates. `Expr.quickLt` depends on the `Expr` hash code, which in
turn depends on metavariable IDs. Thus, before this change, the initial
next metavariable ID at the time of `grind` invocation could affect the
order in which instances were generated, and consequently the `grind`
search.
2025-09-09 22:22:52 +00:00
Leonardo de Moura
f6a2c6d07c fix: bug at assignUnassignedLevelMVars in grind (#10320) 2025-09-09 21:45:06 +00:00
Kyle Miller
1a203c7fe5 feat: intermediate tactic info on simpa (#10309)
This PR modifies the `simpa` tactic so that in `simpa ... using e` there
is tactic info on the range `simpa ... using` that shows the simplified
goal.
2025-09-09 20:24:27 +00:00
Rob23oba
e75e6fbe9e chore: don't include redundant empty strings in string interpolation (#10269)
This PR changes the string interpolation procedure to omit redundant
empty parts. For example `s!"{1}{2}"` previously elaborated to `toString
"" ++ toString 1 ++ toString "" ++ toString 2 ++ toString ""` and now
elaborates to `toString 1 ++ toString 2`.
2025-09-09 14:27:26 +00:00
Copilot
d98b626633 doc: improve docstrings for simp!, simp_all!, dsimp! and autoUnfold (#9991)
- [x] Updated docstrings for `simp!`, `simp_all!`, `dsimp!` to use
user-friendly language
- [x] Updated docstrings for `autoUnfold` fields to use user-friendly
language
- [x] Fixed broken test by updating expected output for simp! hover
documentation
- [x] Replaced technical terms with clear language: "will unfold
applications of functions defined by pattern matching, when one of the
patterns applies"

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nomeata <148037+nomeata@users.noreply.github.com>
2025-09-09 13:34:16 +00:00
Paul Reichert
fd0177afe3 feat: add missing lemmas about insertMany and get? for container types (#10247)
This PR adds missing the lemmas `ofList_eq_insertMany_empty`,
`get?_eq_some_iff`, `getElem?_eq_some_iff` and `getKey?_eq_some_iff` to
all container types.
2025-09-09 13:27:43 +00:00
dependabot[bot]
757426b099 chore: CI: bump actions/checkout from 4 to 5 (#10219)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to
5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
<li>Prepare v5.0.0 release by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2238">actions/checkout#2238</a></li>
</ul>
<h2>⚠️ Minimum Compatible Runner Version</h2>
<p><strong>v2.327.1</strong><br />
<a
href="https://github.com/actions/runner/releases/tag/v2.327.1">Release
Notes</a></p>
<p>Make sure your runner is updated to this version or newer to use this
release.</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v4...v5.0.0">https://github.com/actions/checkout/compare/v4...v5.0.0</a></p>
<h2>v4.3.0</h2>
<h2>What's Changed</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
<li>Prepare release v4.3.0 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2237">actions/checkout#2237</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/motss"><code>@​motss</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li><a href="https://github.com/mouismail"><code>@​mouismail</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li><a href="https://github.com/benwells"><code>@​benwells</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li><a href="https://github.com/nebuk89"><code>@​nebuk89</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li><a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v4...v4.3.0">https://github.com/actions/checkout/compare/v4...v4.3.0</a></p>
<h2>v4.2.2</h2>
<h2>What's Changed</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v4.2.1...v4.2.2">https://github.com/actions/checkout/compare/v4.2.1...v4.2.2</a></p>
<h2>v4.2.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Jcambass"><code>@​Jcambass</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/1919">actions/checkout#1919</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v4.2.0...v4.2.1">https://github.com/actions/checkout/compare/v4.2.0...v4.2.1</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>V5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>V4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable
version. by <a href="https://github.com/jww3"><code>@​jww3</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkout#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkout#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkout#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkout#1695</a></li>
<li>README: Suggest <code>user.email</code> to be
<code>41898282+github-actions[bot]@users.noreply.github.com</code> by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1707">actions/checkout#1707</a></li>
</ul>
<h2>v4.1.4</h2>
<ul>
<li>Disable <code>extensions.worktreeConfig</code> when disabling
<code>sparse-checkout</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkout#1692</a></li>
<li>Add dependabot config by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkout#1688</a></li>
<li>Bump the minor-actions-dependencies group with 2 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkout#1693</a></li>
<li>Bump word-wrap from 1.2.3 to 1.2.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkout#1643</a></li>
</ul>
<h2>v4.1.3</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="08c6903cd8"><code>08c6903</code></a>
Prepare v5.0.0 release (<a
href="https://redirect.github.com/actions/checkout/issues/2238">#2238</a>)</li>
<li><a
href="9f265659d3"><code>9f26565</code></a>
Update actions checkout to use node 24 (<a
href="https://redirect.github.com/actions/checkout/issues/2226">#2226</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/checkout/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4&new-version=5)](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-09-09 11:53:35 +00:00
dependabot[bot]
b81ea5ee9c chore: CI: bump actions/download-artifact from 4 to 5 (#10220)
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/download-artifact/releases">actions/download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/407">actions/download-artifact#407</a></li>
<li>BREAKING fix: inconsistent path behavior for single artifact
downloads by ID by <a
href="https://github.com/GrantBirki"><code>@​GrantBirki</code></a> in <a
href="https://redirect.github.com/actions/download-artifact/pull/416">actions/download-artifact#416</a></li>
</ul>
<h2>v5.0.0</h2>
<h3>🚨 Breaking Change</h3>
<p>This release fixes an inconsistency in path behavior for single
artifact downloads by ID. <strong>If you're downloading single artifacts
by ID, the output path may change.</strong></p>
<h4>What Changed</h4>
<p>Previously, <strong>single artifact downloads</strong> behaved
differently depending on how you specified the artifact:</p>
<ul>
<li><strong>By name</strong>: <code>name: my-artifact</code> → extracted
to <code>path/</code> (direct)</li>
<li><strong>By ID</strong>: <code>artifact-ids: 12345</code> → extracted
to <code>path/my-artifact/</code> (nested)</li>
</ul>
<p>Now both methods are consistent:</p>
<ul>
<li><strong>By name</strong>: <code>name: my-artifact</code> → extracted
to <code>path/</code> (unchanged)</li>
<li><strong>By ID</strong>: <code>artifact-ids: 12345</code> → extracted
to <code>path/</code> (fixed - now direct)</li>
</ul>
<h4>Migration Guide</h4>
<h5> No Action Needed If:</h5>
<ul>
<li>You download artifacts by <strong>name</strong></li>
<li>You download <strong>multiple</strong> artifacts by ID</li>
<li>You already use <code>merge-multiple: true</code> as a
workaround</li>
</ul>
<h5>⚠️ Action Required If:</h5>
<p>You download <strong>single artifacts by ID</strong> and your
workflows expect the nested directory structure.</p>
<p><strong>Before v5 (nested structure):</strong></p>
<pre lang="yaml"><code>- uses: actions/download-artifact@v4
  with:
    artifact-ids: 12345
    path: dist
# Files were in: dist/my-artifact/
</code></pre>
<blockquote>
<p>Where <code>my-artifact</code> is the name of the artifact you
previously uploaded</p>
</blockquote>
<p><strong>To maintain old behavior (if needed):</strong></p>
<pre lang="yaml"><code>&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="634f93cb29"><code>634f93c</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/416">#416</a>
from actions/single-artifact-id-download-path</li>
<li><a
href="b19ff43027"><code>b19ff43</code></a>
refactor: resolve download path correctly in artifact download tests
(mainly ...</li>
<li><a
href="e262cbee4a"><code>e262cbe</code></a>
bundle dist</li>
<li><a
href="bff23f9308"><code>bff23f9</code></a>
update docs</li>
<li><a
href="fff8c148a8"><code>fff8c14</code></a>
fix download path logic when downloading a single artifact by id</li>
<li><a
href="448e3f862a"><code>448e3f8</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/407">#407</a>
from actions/nebuk89-patch-1</li>
<li><a
href="47225c44b3"><code>47225c4</code></a>
Update README.md</li>
<li>See full diff in <a
href="https://github.com/actions/download-artifact/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/download-artifact&package-manager=github_actions&previous-version=4&new-version=5)](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-09-09 11:53:09 +00:00
Leonardo de Moura
c75d37f76b fix: no model-based theory combination on instances (#10314)
This PR skips model based theory combination on instances.
2025-09-09 03:30:29 +00:00
Leonardo de Moura
dd87739fc2 feat: grind normalizers for natCast and intCast (#10313)
This PR adds missing `grind` normalization rules for `natCast` and
`intCast` Examples:
```
open Lean.Grind
variable (R : Type) (a b : R)

section CommSemiring
variable [CommSemiring R]

example (m n : Nat) : (m + n) • a = m • a + n • a := by grind
example (m n : Nat) : (m * n) • a = m • (n • a) := by grind

end CommSemiring

section CommRing
variable [CommRing R]

example (m n : Nat) : (m + n) • a = m • a + n • a := by grind
example (m n : Nat) : (m * n) • a = m • (n • a) := by grind
example (m n : Int) : (m * n) • (a * b) = (m • a) * (n • b) := by grind

end CommRing
```
2025-09-09 01:32:09 +00:00
Lean stage0 autoupdater
01e6928da0 chore: update stage0 2025-09-09 01:05:26 +00:00
Leonardo de Moura
e36d1925f1 refactor: grind cutsat as solver extension (#10312)
This PR uses the new solver extension framework to implement `grind
cutsat`. All satellite solvers have been migrated to the new framework.
2025-09-09 00:23:12 +00:00
Kim Morrison
f9b2e550bb feat: grind annotations for basic monad transformers (#10227)
This PR adds `@[grind]` annotations (nearly all `@[grind =]` annotations
parallel to existing `@[simp]`s) for `ReaderT`, `StateT`, `ExceptT`.
2025-09-08 23:51:55 +00:00
Leonardo de Moura
ed99ad63f3 refactor: grind offset as solver extension (#10311)
This PR uses the new solver extension framework to implement `grind
offset`.
2025-09-08 19:53:54 +00:00
Leonardo de Moura
eb337b820f refactor: grind linarith as solver extension (#10310)
This PR uses the new solver extension framework to implement `grind
linarith`.
2025-09-08 18:34:25 +00:00
Lean stage0 autoupdater
1dc72b1880 chore: update stage0 2025-09-08 19:04:07 +00:00
Cameron Zwarich
e86ab1b1db fix: make IO.RealWorld opaque (#9631)
This PR makes `IO.RealWorld` opaque. It also adds a new compiler -only
`lcRealWorld` constant to represent this type within the compiler. By
default, an opaque type definition is treated like `lcAny`, whereas we
want a more efficient representation. At the moment, this isn't a big
difference, but in the future we would like to completely erase
`IO.RealWorld` at runtime.
2025-09-08 18:12:19 +00:00
Leonardo de Moura
c34ea82bc2 refactor: grind ring as solver extension (#10308)
This PR uses the new solver extension framework to implement `grind
ring`.
2025-09-08 15:40:55 +00:00
Joachim Breitner
79051fb5c0 feat: simpler off-diagonal noConfusion construction using ctorIdx (#10300)
This PR offers an alternative `noConfusion` construction for the
off-diagonal use (i.e. for different constructors), based on comparing
the `.ctorIdx`. This should lead to faster type checking, as the kernel
only has to reduce `.ctorIdx` twice, instead of the complicate
`noConfusionType` construction.
2025-09-08 14:34:26 +00:00
Kyle Miller
81fe3b6d05 feat: pretty print sorry in "declaration uses 'sorry'" (#10034)
This PR changes the "declaration uses 'sorry'" error to pretty print an
actual `sorry` expression in the message. The effect is that the `sorry`
is hoverable and, if it's labeled, you can "go to definition" to see
where it came from.

The implementation prefers reporting synthetic sorries. These can appear
even if there are no error messages if a declaration refers to a
declaration that has elaboration errors. Users should focus on
elaboration errors before worrying about user-written `sorry`s.

In the future we could have some more precise logic for sorry reporting.
All the sorries in a declaration should be considered to be reported,
and we should not re-report sorries in later declarations. Some
elaborators use `warn.sorry` to avoid re-reporting sorries in auxiliary
declarations.
2025-09-08 12:14:42 +00:00
Lean stage0 autoupdater
05d6b8648c chore: update stage0 2025-09-08 10:52:50 +00:00
Wojciech Różowski
5c03ab9630 fix: unfolding order on predicates (#10245)
This PR changes the implementation of a function `unfoldPredRel` used in
(co)inductive predicate machinery, that unfolds pointwise order on
predicates to quantifications and implications. Previous implementation
relied on `withDeclsDND` that could not deal with types which depend on
each other. This caused the following example to fail:

```lean4
inductive infSeq_functor1.{u} {α : Type u} (r : α → α → Prop) (call : {α : Type u} → (r : α → α → Prop) → α → Prop) : α → Prop where
  | step : r a b → infSeq_functor1 r call b → infSeq_functor1 r call a

def infSeq1 (r : α → α → Prop) : α → Prop := infSeq_functor1 r (infSeq1)
  coinductive_fixpoint monotonicity by sorry

#check infSeq1.coinduct
```
Closes #10234.
2025-09-08 10:07:15 +00:00
Joachim Breitner
3e24d5dee8 fix: expose ctorIdx and per-constructor elims (#10301)
This PR exposes ctorIdx and per-constructor eliminators. Fixes #10299.
2025-09-08 10:04:19 +00:00
Cameron Zwarich
4a73532fbe chore: remove unnecessary parens (#10298) 2025-09-08 05:52:45 +00:00
Cameron Zwarich
f6cf54fb2f chore: remove lean/run/lcnf1.lean test (#10297)
This test involves re-running the compiler on decls that have already
been compiled, which can cause all sorts of issues. I just hit these
issues on a PR, so it's time to retire this test like others that hit
the same issues.
2025-09-08 05:51:40 +00:00
Leonardo de Moura
058f6008c0 fix: bug at Poly.combine_mul_k (#10296)
This PR fixes a bug in an auxiliary function used to construct proof
terms in `grind cutsat`.
2025-09-08 02:17:11 +00:00
Lean stage0 autoupdater
ab30577acb chore: update stage0 2025-09-08 01:54:53 +00:00
Leonardo de Moura
be1e090833 feat: grind solver extensions (part 2) (#10294)
This PR completes the `grind` solver extension design and ports the
`grind ac` solver to the new framework. Future PRs will document the API
and port the remaining solvers. An additional benefit of the new design
is faster build times.
2025-09-08 01:11:05 +00:00
Cameron Zwarich
6a8d7cc17c chore: remove instWPMonad instance and test that relies upon it (#10293)
The proof of the instWPMonad instance relies on the equality of any two
terms of type `IO.RealWorld`, which is only a side effect of the current
transparent definition. Ignoring the questions around the utility of
proving things about programs in `IO`, the semantic validity of this
instance in the intended model of the IO monad is also unclear.

I tried a few things to axiomatize this instance so it could be put into
the test file to preserve the one test section that relies on it, but I
was unsuccessful; everything I attempted caused errors.
2025-09-07 23:42:52 +00:00
Lean stage0 autoupdater
13795fb3ad chore: update stage0 2025-09-07 18:40:52 +00:00
Leonardo de Moura
612c7588d0 feat: grind solver extensions (#10290)
This PR adds infrastructure for registering new `grind` solvers. `grind`
already includes many solvers, and this PR is the first step toward
modularizing the design and supporting user-defined solvers.
2025-09-07 17:45:50 +00:00
Lean stage0 autoupdater
d70b619500 chore: update stage0 2025-09-07 17:55:11 +00:00
Markus Himmel
9402c307fe chore: reorganize Init imports around strings (#10289)
This PR reorganizes the import hierarchy so that
`Init.Data.String.Basic` can import `Init.Data.UInt.Bitwise` and
`Init.Data.Array.Lemmas`.
2025-09-07 17:09:14 +00:00
Lean stage0 autoupdater
1ab115648d chore: update stage0 2025-09-07 13:40:08 +00:00
Markus Himmel
aa0a31ae7d chore: prepare for untangling strings (#10288)
This PR prepares for a future reorganization of the import hierarchy so
that `Init.Data.String.Basic` can import `Init.Data.UInt.Bitwise` and
`Init.Data.Array.Lemmas`.
2025-09-07 12:58:23 +00:00
Markus Himmel
19bd0254c3 chore: move String.utf8EncodeChar to the prelude (#10264)
This PR moves `String.utf8EncodeChar` to the prelude to prepare for the
imminent redefinition of `String`.

The definition in the prelude uses modulo and division operations on
natural numbers. In `String.Extra`, a `csimp` lemma is provided, showing
that the new definition is equal to the previous one (which is now
called `utf8EncodeCharFast`) which uses bitwise operations on `UInt8`.
2025-09-07 12:42:53 +00:00
Marc Huisinga
0b3550f284 fix: textedit completions (#10286)
This PR fixes `textEdit`-based completions after they were accidentally
broken by the new serialization procedure in #10249.
2025-09-07 09:44:16 +00:00
Leonardo de Moura
5463e10ce4 test: grind ac diagnostics (#10284) 2025-09-07 02:15:56 +00:00
Leonardo de Moura
8fd8821b61 feat: grind ac module diagnostics (#10283)
This PR implements diagnostic information for the `grind ac` module. It
now displays the basis, normalized disequalities, and additional
properties detected for each associative operator.
2025-09-07 01:43:38 +00:00
Leonardo de Moura
975b6e758f feat: improve grind linarith counterexamples for NatModule (#10282)
This PR improves the counterexamples produced by `grind linarith` for
`NatModule`s. `grind` now hides occurrences of the auxiliary function
`Grind.IntModule.OfNatModule.toQ`.
2025-09-07 01:04:01 +00:00
Leonardo de Moura
a31eb94e5a feat: normalize NatModule equations (#10281)
This PR implements `NatModule` normalization when the `AddRightCancel`
instance is not available. Note that in this case, the embedding into
`IntModule` is not injective. Therefore, we use a custom normalizer,
similar to the `CommSemiring` normalizer used in the `grind ring`
module. Example:

```lean
open Lean Grind
example [NatModule α] (a b c : α)
    : 2•a + 2•(b + 2•c) + 3•a = 4•a + c + 2•b + 3•c + a := by
  grind
```
2025-09-07 00:20:10 +00:00
Leonardo de Moura
652868c308 feat: NatModule equation normalization theorem (#10280)
This PR adds the auxiliary theorem `Lean.Grind.Linarith.eq_normN` for
normalizing `NatModule` equations when the instance `AddRightCancel` is
not available.
2025-09-06 23:32:26 +00:00
Mac Malone
0d28e450c2 refactor: lake: mv verLit into the DSL namespace (#10276)
This PR moves the `verLit` syntax into the `Lake.DSL` namespace to be
consistent with other code found in `Lake.DSL`.
2025-09-06 21:14:15 +00:00
Joachim Breitner
a872cec0a7 refactor: use match decEq, not if h : in deriving DecidableEq (#10274)
This PR changes the implementation of the linear `DecidableEq`
implementation to use `match decEq` rather than `if h : ` to compare the
constructor tags. Otherwise, the “smart unfolding” machinery will not
let `rfl` decide that different constructors are different.
2025-09-06 21:00:34 +00:00
Leonardo de Moura
2ff41f43be feat: NatModule inequalities and equalities in grind linarith (#10278)
This PR adds support for `NatModule` equalities and inequalities in
`grind linarith`. Examples:
```lean
open Lean Grind Std

example [NatModule α] [LE α] [LT α] 
  [LawfulOrderLT α] [IsLinearOrder α] [OrderedAdd α] 
  (x y : α) : x ≤ y → 2 • x + y ≤ 3 • y := by
  grind

example [NatModule α] [AddRightCancel α] [LE α] [LT α] 
    [LawfulOrderLT α] [IsLinearOrder α] [OrderedAdd α] 
    (a b c d : α) : a ≤ b → a ≥ c + d → d ≤ 0 → d ≥ 0 → b = c → a = b := by
  grind
```
2025-09-06 20:52:09 +00:00
Leonardo de Moura
52a9fe3b67 feat: missing NatModule instances (#10277)
This PR adds the missing instances `IsPartialOrder`, `IsLinearPreorder`
and `IsLinearOrder` for `OfNatModule.Q α`.
2025-09-06 18:58:02 +00:00
Joachim Breitner
316ff35afd feat: deriving instances: use accessible names (#10271)
This PR changes the naming of the internal functions in deriving
instances like BEq to use accessible names. This is necessary to
reasonably easily prove things about these functions. For example after
`deriving BEq` for a type `T`, the implementation of `instBEqT` is in
`instBEqT.beq`.
2025-09-06 18:12:20 +00:00
Mac Malone
aaa0cf3cf6 refactor: lake: rm public syntax workarounds (#10275) 2025-09-06 17:33:36 +00:00
Joachim Breitner
8b09366c78 fix: casesOnSameCtor: export if not private (#10273)
This PR tries to do the right thing about the visibility of the
same-ctor-match-construct.
2025-09-06 16:32:10 +00:00
Sebastian Ullrich
5f75c55191 fix: do not apply private instances in public scope (#10260) 2025-09-06 15:34:12 +00:00
Sebastian Ullrich
752b53e936 feat: maxErrors option (#10262)
This PR adds a new option `maxErrors` that limits the number of errors
printed from a single `lean` run, defaulting to 100. Processing is
aborted when the limit is reached, but this is tracked only on a
per-command level.

Smaller values can be useful when making changes that break a lot of
files and would otherwise scroll the actual root failures out of the
terminal view.
2025-09-06 14:52:49 +00:00
Eric Wieser
3f671cca92 doc: add docstrings for tracing functions (#10169) 2025-09-06 09:29:24 +00:00
Leonardo de Moura
8735447d44 feat: infrastructure for NatModule in grind linarith (#10267)
This PR implements the infrastructure for supporting `NatModule` in
`grind linarith` and uses it to handle disequalities. Another PR will
add support for equalities and inequalities. Example:
```lean
open Lean Grind
variable (M : Type) [NatModule M] [AddRightCancel M]

example (x y : M) : 2 • x + 3 • y + x = 3 • (x + y) := by
  grind
```
2025-09-06 01:16:03 +00:00
Leonardo de Moura
1861cc6bbc fix: panic in grind ring (#10265)
This PR fixes a panic in `grind ring` exposed by #10242. `grind ring`
should not assume that all normalizations have been applied, because
some subterms cannot be rewritten by `simp` due to typing constraints.
Moreover, `grind` uses `preprocessLight` in a few places, and it skips
the simplifier/normalizer.

Closes #10242
2025-09-05 16:16:25 +00:00
Sebastian Ullrich
974c649e2e fix: meta structure/inductive should create meta ctor (#10263) 2025-09-05 14:00:55 +00:00
Paul Reichert
184f716da1 refactor: improve names in the range API (#10059)
This PR improves the names of definitions and lemmas in the polymorphic
range API. It also introduces a recommended spelling. For example, a
left-closed, right-open range is spelled `Rco` in analogy with Mathlib's
`Ico` intervals.
2025-09-05 13:10:05 +00:00
Lean stage0 autoupdater
3f7f1c87f6 chore: update stage0 2025-09-05 11:21:02 +00:00
Marc Huisinga
7ba0ae1f72 feat: improve auto-completion performance (#10249)
This PR speeds up auto-completion by a factor of ~3.5x through various
performance improvements in the language server. On one machine, with
`import Mathlib`, completing `i` used to take 3200ms and now instead
yields a result in 920ms.

Specifically, the following improvements are made:
- The watchdog process no longer de-serializes and re-serializes most
messages from the file worker before passing them on to the user - a
fast partial de-serialization procedure is now used to determine whether
the message needs to be de-serialized in full or not.
- `escapePart` is optimized to perform better on ASCII strings that do
not need escaping.
- `Json.compress` is optimized to allocate fewer objects.
- A faster JSON compression specifically for completion responses is
implemented that skips allocating `Json` altogether.
- The JSON compression has been moved to the task where we convert a
request response to `Json` so that converting to a string won't block
the output task of the FileWorker and so the `Json` value is not marked
as multi-threaded when we compress is, which drastically increases the
cost of reference-counting.
- The JSON representation of the `data?` field of each completion item
is optimized.
- Both the completion kind and the set of completion tags for each
imported completion item is now cached.
- The filtering of duplicate completion items is optimized.

Other adjustments:
- `LT UInt8` and `LE UInt8` are moved to Prelude so that they can be
used in `Init.Meta` for the name part escaping fast path.
- `Array.usize` is exposed since it was marked as `@[simp]`.
2025-09-05 08:55:49 +00:00
Cameron Zwarich
9923a8d9f8 chore: remove special case for extern constructors (#10257)
This is subsumed by the fix in #10256.
2025-09-05 06:08:45 +00:00
Cameron Zwarich
de38a16fa9 fix: use IR decls in toIR for applications without mono decls (#10256)
This PR corrects a mistake in `toIR` where it could over-apply a
function that has an IR decl but no mono decl.

Fixes #10181.
2025-09-05 05:32:19 +00:00
Cameron Zwarich
c0238e396c refactor: inline tryIrDecl? into its only caller (#10255)
This helper function was actually incorrectly named anyways.
2025-09-05 04:41:34 +00:00
Cameron Zwarich
c7cc398935 refactor: create a mkApplication helper for toIR (#10254) 2025-09-05 01:42:36 +00:00
Mac Malone
849bb770fd refactor: lake: split PackageConfig from Config.Package (#10253)
This PR moves the `PackageConfig` definition from `Lake.Config.Package`
into its own module. This enables a significant reduction in the `meta
import` tree of the `Lake.CLI.Translate` modules.
2025-09-04 23:15:37 +00:00
Leonardo de Moura
6cefbc4bb0 chore: fix typo (#10251) 2025-09-04 16:05:00 +00:00
Paul Reichert
9b6a4a7588 fix: solve two problems with LinearOrderPackage factories (#10250)
This PR fixes a bug in the `LinearOrderPackage.ofOrd` factory. If there
is a `LawfulEqOrd` instance available, it should automatically use it
instead of requiring the user to provide the `eq_of_compare` argument to
the factory. The PR also solves a hygiene-related problem making the
factories fail when `Std` is not open.
2025-09-04 15:27:09 +00:00
Sebastian Ullrich
47787dc1cb perf: rebuild leak on private match (#10246)
This PR prevents downstream rebuilds on changes to private `match`es
under the module system
2025-09-04 12:51:42 +00:00
Lean stage0 autoupdater
25ab3dd93d chore: update stage0 2025-09-04 08:22:20 +00:00
Kim Morrison
bbd45b13f4 chore: move omega internals to a namespace (#10243)
This PR moves some internal implementation details of `omega` out of the
`List` namespace. See [#mathlib4 > Naming: ne_zero vs nonzero @
💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Naming.3A.20ne_zero.20vs.20nonzero/near/537424328).
2025-09-04 06:32:02 +00:00
Kim Morrison
85f168bbd0 chore: add test cases for grind on Fin lemmas (#10241)
This PR adds some test cases for `grind` working with `Fin`. There are
many still failing tests in `tests/lean/grind/grind_fin.lean` which I'm
intending to triage and work on.
2025-09-04 04:28:29 +00:00
Marcus Rossel
89aed0931e feat: improve error message when passing local hypotheses to grind (#8891)
This PR improves the error message produced when passing (automatically
redundant) local hypotheses to `grind`.
2025-09-04 03:00:21 +00:00
Sebastian Ullrich
92d24e1c40 fix: Environment.realizeConst to replay realization map (#10238)
This PR fixes an issue with retrieving realized declarations after use
of Aesop uncovered by #10229
2025-09-03 22:16:40 +00:00
Leonardo de Moura
c15ee8a9f0 fix: universe polymorphic E-matching (#10239)
This PR fixes the E-matching procedure for theorems that contain
universe parameters not referenced by any regular parameter. This kind
of theorem seldom happens in practice, but we do have instances in the
standard library. Example:
```
@[simp, grind =] theorem Std.Do.SPred.down_pure {φ : Prop} : (⌜φ⌝ : SPred []).down = φ := rfl
```

closes #10233
2025-09-03 22:14:58 +00:00
Leonardo de Moura
320b02108b fix: grind canonicalizer (#10237)
This PR fixes a missing case in the `grind` canonicalizer. Some types
may include terms or propositions that are internalized later in the
`grind` state.

closes #10232
2025-09-03 18:08:48 +00:00
Rob23oba
80df86dfdd feat: add more MonoBind instances for monad transformers (#10230)
This PR adds `MonoBind` for more monad transformers. This allows using
`partial_fixpoint` for more complicated monads based on `Option` and
`EIO`. Example:
```lean-4
abbrev M := ReaderT String (StateT String.Pos Option)

def parseAll (x : M α) : M (List α) := do
  if (← read).atEnd (← get) then
    return []
  let val ← x
  let list ← parseAll x
  return val :: list
partial_fixpoint
```
2025-09-03 17:15:41 +00:00
Paul Reichert
fef390df08 perf: improve iterator/range benchmarks, use shortcut instances for Int ranges (#10197)
This PR is the result of analyzing the elaborator performance regression
introduced by #10005. It makes the `workspaceSymboldNewRanges` and
`iterators` benchmarks less noisy. It also replaces some range-related
instances for `Nat` with shortcuts to the general-purpose instances.
This is a trade-off between the ergonomics and the synthesis cost of
having general-purpose instances.
2025-09-03 15:47:52 +00:00
Sebastian Ullrich
37be918c50 perf: do not export EqnInfo for non-exposed defs (#10229) 2025-09-03 10:03:52 +00:00
Sebastian Ullrich
2efbe4ac36 feat: support visibility modifiers on syntax abbrevs (#10228)
Closes #10068
2025-09-03 07:53:29 +00:00
Eric Wieser
6d68aab56a feat: generalize universes in monadic operators for collections (#10224)
This PR generalizes the monadic operations for `HashMap`, `TreeMap`, and
`HashSet` to work for `m : Type u → Type v`.

This upstreams [a workaround from
Aesop](66a992130e/Aesop/Util/Basic.lean (L57-L66)),
and seems to continue a pattern already established in other files, such
as:
```lean
Array.forM.{u, v, w} {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Array α) (start : Nat := 0)
  (stop : Nat := as.size) : m PUnit
```
2025-09-03 07:24:14 +00:00
Joachim Breitner
ccb8568756 feat: linear-size DecidableEq instance (#10152)
This PR introduces an alternative construction for `DecidableEq`
instances that avoids the quadratic overhead of the default
construction.

The usual construction uses a `match` statement that looks at each pair
of constructors, and thus is necessarily quadratic in size. For
inductive data type with dozens of constructors or more, this quickly
becomes slow to process.

The new construction first compares the constructor tags (using the
`.ctorIdx` introduced in #9951), and handles the case of a differing
constructor tag quickly. If the constructor tags match, it uses the
per-constructor-eliminators (#9952) to create a linear-size instance. It
does so by creating a custom “matcher” for a parallel match on the data
types and the `h : x1.ctorIdx = x2.ctorIdx` assumption; this behaves
(and delaborates) like a normal `match` statement, but is implemented in
a bespoke way. This same-constructor-matcher will be useful for
implementing other instances as well.

The new construction produces less efficient code at the moment, so we
use it only for inductive types with 10 or more constructors by default.
The option `deriving.decEq.linear_construction_threshold` can be used to
adjust the threshold; set it to 0 to always use the new construction.
2025-09-03 06:31:49 +00:00
Leonardo de Moura
a4f6f391fe feat: equality propagation from AC module to grind core (#10223)
This PR implements equality propagation from the new AC module into the
`grind` core. Examples:

```lean
example {α β : Sort u} (f : α → β) (op : α → α → α) [Std.Associative op] [Std.Commutative op] 
    (a b c d : α) : op a (op b b) = op d c → f (op (op b a) (op b c)) = f (op c (op d c)) := by
  grind only

example (a b c : Nat) : min a (max b (max c 0)) = min (max c b) a := by
  grind -cutsat only

example {α β : Sort u} (bar : α → β) (op : α → α → α) [Std.Associative op] [Std.IdempotentOp op]
    (a b c d e f x y w : α) :
    op d (op x c) = op a b →
    op e (op f (op y w)) = op (op d a) (op b c) →
    bar (op d (op x c)) = bar (op e (op f (op y w))) := by
  grind only
```
2025-09-02 23:02:25 +00:00
Leonardo de Moura
dac61c406f feat: extra critical pairs for associative + idempotent operators in grind ac (#10221)
This PR adds the extra critical pairs to ensure the `grind ac` procedure
is complete when the operator is associative and idempotent, but not
commutative. Example:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.IdempotentOp op] (a b c d e f x y w : α)
    : op d (op x c) = op a b →
      op e (op f (op y w)) = op a (op b c) →
      op d (op x c) = op e (op f (op y w)) := by
  grind only

example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.IdempotentOp op] (a b c d e f x y w : α)
    : op a (op d x) = op b c →
      op e (op f (op y w)) = op a (op b c) →
      op a (op d x) = op e (op f (op y w)) := by
  grind only
```
2025-09-02 15:52:56 +00:00
Henrik Böving
db35f98b26 fix: make csimp equivalence criteria more strict (#10214)
This PR fixes #10213.
2025-09-02 14:36:08 +00:00
Leonardo de Moura
e6f50b0181 perf: EqCnstr.superposeWith (#10218)
This PR adds a small optimization for `EqCnstr.superposeWith`
It also adds a new test unrelated to the optimization.
2025-09-02 13:50:47 +00:00
Dax Fohl
2877196656 doc: fix broken "quickstart" and "supported editors" link (#8785)
The "supported editors" link in
https://github.com/leanprover/lean4/blob/master/doc/dev/index.md is
broken, as `setup.md` no longer exists in the repo. This PR changes the
link to point to the live Lean docs setup page at
https://docs.lean-lang.org/lean4/doc/setup.html#editing.

A similar fix for quickstart is included.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-09-02 12:45:04 +00:00
Aaron Liu
f748d1c4ef doc: fix typo in docstring for fieldIdxKind (#8814)
This PR fixes a typo in the docstring for `Lean.fieldIdxKind`, which was
missing a backtick.
2025-09-02 12:30:07 +00:00
Eric Wieser
848832dd61 chore: demote a panic to an exception in saveModuleData (#9127)
This PR makes `saveModuleData` throw an IO.Error instead of panicking,
if given something that cannot be serialized. This doesn't really matter
for saving modules, but is handy when writing tools to save auxiliary
date in olean files via Batteries' `pickle`.

The caller of this C++ function already is guarded in a `try`/`catch`
that promotes from a `lean::exception` to an `IO.userError`.

A simple test of this in the web editor is
```
import Batteries

#eval pickle "/tmp/foo.txt" fun x : Nat => x
```
which crashes before this change.

---------

Co-authored-by: Laurent Sartran <lsartran@google.com>
2025-09-02 12:25:45 +00:00
Henrik Böving
c5f2c192d6 fix: Selectable.one does not panic on empty array (#10216)
This PR fixes #10193.
2025-09-02 11:55:36 +00:00
Sebastian Ullrich
96c42b95fa chore: CI: reintroduce lost CTEST_OPTIONS (#10211) 2025-09-02 09:26:29 +00:00
Leonardo de Moura
d826474b14 feat: extra critical pairs for AC + idempotent operators in grind ac (#10208)
This PR adds the extra critical pairs to ensure the `grind ac` procedure
is complete when the operator is AC and idempotent. Example:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.Commutative op] [Std.IdempotentOp op] 
      (a b c d : α) : op a (op b b) = op d c → op (op b a) (op b c) = op c (op d c)  := by
  grind only
```
2025-09-02 04:24:22 +00:00
Kim Morrison
8d9d23b5bb feat: (approximate) inverses of dyadic rationals (#10194)
This PR adds the inverse of a dyadic rational, at a given precision, and
characterising lemmas. Also cleans up various parts of the `Int.DivMod`
and `Rat` APIs, and proves some characterising lemmas about
`Rat.toDyadic`.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-09-02 03:43:53 +00:00
Leonardo de Moura
c83237baf7 chore: cleanup superposeAC? (#10207)
This PR ensures `superposeAC?` and `superpose?` have similar signatures.
2025-09-02 01:55:20 +00:00
Leonardo de Moura
11f618ac49 feat: critical pairs (non commutative case) for grind ac (#10206)
This PR adds superposition for associative (but non-commutative)
operators in `grind ac`. Examples:
```lean
example {α} (op : α → α → α) [Std.Associative op] (a b c d : α)
   : op a b = c →
     op b a = d →
     op (op c a) (op b c) = op (op a d) (op d b) := by
  grind

example {α} (a b c d : List α)
   : a ++ b = c →
     b ++ a = d →
     c ++ a ++ b ++ c = a ++ d ++ d ++ b := by
  grind only
```
2025-09-02 00:58:49 +00:00
Leonardo de Moura
708f715efb feat: critical pairs for grind ac (#10205)
This PR adds superposition for associative and commutative operators in
`grind ac`. Examples:

```lean
example (a b c d e f g h : Nat) :
    max a b = max c d → max b e = max d f → max b g = max d h →
    max (max f d) (max c g) = max (max e (max d (max b (max c e)))) h := by
  grind -cutsat only

example {α} (op : α → α → α) [Std.Associative op] [Std.Commutative op] (a b c d : α)
    : op a b = op b c → op c c = op d c →
      op (op d a) (op b d) = op (op a a) (op b d) := by
  grind only
```
2025-09-01 23:17:09 +00:00
Joachim Breitner
b0506ee835 chore: remove bootstrap tricks from #9951 (#10203)
This PR removes bootstrap tricks from #9951.
2025-09-01 13:30:42 +00:00
Joachim Breitner
f1737737f0 perf: use matcher as splitter (#10184)
This PR avoids constructing the splitter if the matcher itself has the
right type. This happens whenever there are no overlaps.
2025-09-01 11:29:43 +00:00
Joachim Breitner
f3b1f054ef perf: prove match equations by rfl if possible (#10183)
This PR lets match equations be proved by `rfl` if possible, instead of
explicitly unfolding the LHS first. May lead to smaller proofs.
2025-09-01 11:19:55 +00:00
Joachim Breitner
94ea5fb3fd test: add test for #10195 (#10200)
This PR adds a test for #10195
2025-09-01 10:14:54 +00:00
Rob23oba
5b9567b144 fix: complete overhaul of structural recursion on inductives predicates (#9995)
This PR almost completely rewrites the inductive predicate recursion
algorithm; in particular `IndPredBelow` to function more consistently.
Historically, the `brecOn` generation through `IndPredBelow` has been
very error-prone -- this should be fixed now since the new algorithm is
very direct and doesn't rely on tactics or meta-variables at all.
Additionally, the new structural recursion procedure for inductive
predicates shares more code with regular structural recursion and thus
allows for mutual and nested recursion in the same way it was possible
with regular structural recursion. For example, the following works now:
```lean-4
mutual

inductive Even : Nat → Prop where
  | zero : Even 0
  | succ (h : Odd n) : Even n.succ

inductive Odd : Nat → Prop where
  | succ (h : Even n) : Odd n.succ

end

mutual

theorem Even.exists (h : Even n) : ∃ a, n = 2 * a :=
  match h with
  | .zero => ⟨0, rfl⟩
  | .succ h =>
    have ⟨a, ha⟩ := h.exists
    ⟨a + 1, congrArg Nat.succ ha⟩
termination_by structural h

theorem Odd.exists (h : Odd n) : ∃ a, n = 2 * a + 1 :=
  match h with
  | .succ h =>
    have ⟨a, ha⟩ := h.exists
    ⟨a, congrArg Nat.succ ha⟩
termination_by structural h

end
```

Closes #1672
Closes #10004
2025-09-01 08:17:58 +00:00
Leonardo de Moura
c4e5f57512 feat: proof terms for grind ac (#10189)
This PR implements the proof terms for the new `grind ac` module.
Examples:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] (a b c d : α)
    : op a (op b b) = op c d → op c (op d c) = op (op a b) (op b c) := by
  grind only

example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.Commutative op] (a b c d : α)
    : op a (op b b) = op d c → op (op b a) (op b c) = op c (op d c)  := by
  grind only

example {α : Sort u} (op : α → α → α) [Std.Associative op] [Std.Commutative op]
    (one : α) [Std.LawfulIdentity op one] (a b c d : α)
    : op a (op (op b one) b) = op d c → op (op b a) (op (op b one) c) = op (op c one) (op d c)  := by
  grind only
```

The `grind ac` module is not complete yet, we still need to implement
critical pair computation and fix the support for idempotent operators.
2025-08-31 04:10:10 +00:00
Leonardo de Moura
f376fd87d0 feat: AC disequality simplification (#10186)
This PR adds supports for simplifying disequalities in the `grind ac`
module.
2025-08-30 20:42:21 +00:00
Leonardo de Moura
8e7e55f2d5 doc: grind attribute modifiers (#10185)
This PR documents all `grind` attribute modifiers (e.g., `=`, `usr`,
`ext`, etc).
2025-08-30 16:12:50 +00:00
Kim Morrison
8789e5621b feat: missing Nat.fold(Rev)_add lemmas (#10182)
This PR adds lemmas about `Nat.fold` and `Nat.foldRev` on sums, to match
the existing theorems about `dfold` and `dfoldRev`.
2025-08-30 08:54:12 +00:00
Leonardo de Moura
fbf096510d chore: minimize number of public imports in grind (#10180) 2025-08-30 03:38:47 +00:00
Leonardo de Moura
18cc1cec80 fix: grind instance normalization (#10179)
This PR fixes `grind` instance normalization procedure.
Some modules in grind use builtin instances defined directly in core
(e.g., `cutsat`), while others synthesize them using `synthInstance`
(e.g., `ring`). This inconsistency is problematic, as it may introduce
mismatches and result in two different representations for the same
term. This PR fixes the issue.
2025-08-30 02:24:26 +00:00
Leonardo de Moura
404b00a584 fix: grind preprocessor (#10177)
This PR fixes a bug in the `grind` preprocessor exposed by #10160.

Closes #10160
2025-08-29 23:37:52 +00:00
Leonardo de Moura
50ddf85b07 feat: check grind ac invariants (#10176)
This PR adds code for checking invariants in the `grind ac` module, and
fixes the bugs exposed by them.
2025-08-29 22:36:39 +00:00
Sofia Rodrigues
9107d27368 fix: remove extend from async and await (#10173)
This PR removes the `extends Monad` from `MonadAwait` and `MonadAsync`
to avoid underdetermined instances.

The issue was discussed here: [#lean4 > Is
Std.Internal.IO.Async.MonadAsync.toMonad a bad
instance?](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Is.20Std.2EInternal.2EIO.2EAsync.2EMonadAsync.2EtoMonad.20a.20bad.20instance.3F)
2025-08-29 15:33:57 +00:00
Wojciech Rozowski
d51a5b920d feat: change delimiting of local attributes in implicit sections (#9968)
This PR modifies macros, which implement non-atomic definitions and
```$cmd1 in $cmd2``` syntax. These macros involve implicit scopes,
introduced through ```section``` and ```namespace``` commands. Since
sections or namespaces are designed to delimit local attributes, this
has led to unintuitive behaviour when applying local attributes to
definitions appearing in the above-mentioned contexts. This has been
causing the following examples to fail:
```lean4
axiom A : Prop

namespace ex1
open Nat in
@[local simp] axiom a : A ↔ True
example : A := by simp
end ex1

namespace ex2
@[local simp] axiom Foo.a : A ↔ True
example : A := by simp
end ex2
```
This PR adds an internal-only piece of syntax,
```InternalSyntax.end_local_scope```, that influences the
```ScopedEnvExtension.addLocalEntry``` used in implementing local
attributes, to avoid delimiting local entries in the current scope. This
command is used in the above-mentioned macros.

Closes [#9445](https://github.com/leanprover/lean4/issues/9445).

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-08-28 15:48:42 +00:00
Wojciech Rozowski
eb013fb90d fix: construction of CompleteLattice instance for eta-reduced definitions (#10144)
This PR changes the construction of a `CompleteLattice` instance on
predicates (maps intro `Prop`) inside of
`coinductive_fixpoint`/`inductive_fixpoint` machinery.

Consider a following endomap on predicates of the type ` α → Prop`:
```lean4
def DefFunctor (r : α → α → Prop) (infSeq : α → Prop) : α → Prop :=
   λ x : α => ∃ y, r x y ∧ infSeq y
```
The following eta-reduced expression failed to elaborate:
```lean4
def def1 (r : α → α → Prop) : α → Prop := DefFunctor r (def1 r)
  coinductive_fixpoint monotonicity sorry
```

At the same time, eta-expanded variant would elaborate correctly:
```lean4
def def2 (r : α → α → Prop) : α → Prop := fun x => DefFunctor r (def2 r) x
  coinductive_fixpoint monotonicity sorry
```

This PR fixes the above issue, by changing the way how `CompleteLattice`
instance on the space of predicates is constructed, to allow for the
eta-reduced case, as outlined above.
2025-08-28 12:27:53 +00:00
Kim Morrison
4c44fdb95f chore: remove grind annotations of List/Array/Vector.zip_map_left/right (#10163)
This PR removes some (hopefully) unnecessary `grind` annotations that
cause instantiation explosions.
2025-08-28 10:38:50 +00:00
Sebastian Ullrich
d63d1188cc chore: fix stdlib size benchmarks 2025-08-28 12:07:27 +02:00
Lean stage0 autoupdater
a31d686ed1 chore: update stage0 2025-08-28 09:45:24 +00:00
Kim Morrison
a62dabeb56 feat: nodup_keys theorems for maps (#10159)
This PR adds `nodup_keys` lemmas as corollaries of existing
`distinct_keys` to all `Map` variants.
2025-08-28 06:00:28 +00:00
Kim Morrison
d2eb1bc9f5 chore: review of failing grind tests (#10166)
This PR reviews the expected-to-fail-right-now tests for `grind`, moving
some (now passing) tests to the main test suite, updating some tests,
and adding some tests about normalisation of exponents.
2025-08-28 05:24:31 +00:00
Leonardo de Moura
38608a672e feat: simplify equations in grind AC module (#10165)
This PR adds support for equality simplification helper functions to the
`grind` AC module.
2025-08-28 03:54:09 +00:00
Leonardo de Moura
86425f655a feat: helper AC.Seq functions (#10164)
This PR adds helper functions for the `AC.Seq` type.
2025-08-28 02:16:52 +00:00
Sebastian Ullrich
9757a7be53 perf: do not export opaque bodies (#10119)
In particular, do not export `partial` bodies
2025-08-27 20:59:59 +00:00
Marc Huisinga
3ce69e4edb feat: re-enable Suggestion.messageData? (#10157)
Re-enables `Suggestion.messageData?` after it was deprecated in #9966
since it is needed for the workaround described in #10150. We will
hopefully be able to clean up with API once #10150 is properly fixed.
2025-08-27 16:23:02 +00:00
Leonardo de Moura
2dda33ddb2 chore: remove workaround (#10156) 2025-08-27 15:18:17 +00:00
Sebastian Ullrich
655a39ceb8 chore: improve error message on trying to access an identifier imported privately from the public scope (#10153) 2025-08-27 13:43:56 +00:00
Sebastian Ullrich
8d26a9e8b5 chore: revert public deriving workarounds (#10155) 2025-08-27 13:15:18 +00:00
Joachim Breitner
72e8970848 chore: benchmarks for deriving DecidableEq on large inductives (#10149)
This PR adds benchmarks for deriving `DecidableEq` on inductives with
many constructors. (Although at the moment, many is “many” as we timeout
for more than 30 or 40 constructors.)
2025-08-27 12:05:04 +00:00
Sebastian Ullrich
697ea0bc01 fix: Unicode path support for Lean Windows executables (#10133)
This PR fixes compatibility of Lean-generated executables with Unicode
file system paths on Windows

Fixes #2554
2025-08-27 11:28:55 +00:00
Sebastian Ullrich
4d5fb31dfb fix: where finally should enter the private scope (#10151)
This PR ensures `where finally` tactics can access private data under
the module system even when the corresponding holes are in the public
scope as long as all of them are of proposition types.
2025-08-27 11:27:40 +00:00
Sebastian Ullrich
43dc9f45d1 chore: CI: disable broken test on macOS x64 2025-08-27 13:14:32 +02:00
Lean stage0 autoupdater
dc1ddda473 chore: update stage0 2025-08-27 10:47:56 +00:00
Joachim Breitner
b5555052bd feat: T.ctor.elim single-constructor cases function (#9952)
This PR adds “non-branching case statements”: For each inductive
constructor `T.con` this adds a function `T.con.with` that is similar
`T.casesOn`, but has only one arm (the one for `con`), and an additional
`t.toCtorIdx = 12` assumption.

For example:
```lean
inductive Vec (α : Type) : Nat → Type where
  | nil : Vec α 0
  | cons {n} : α → Vec α n → Vec α (n + 1)

/--
info: @[reducible] protected def Vec.cons.elim.{u} : {α : Type} →
  {motive : (a : Nat) → Vec α a → Sort u} →
    {a : Nat} →
      (t : Vec α a) →
        t.ctorIdx = 1 → ({n : Nat} → (a : α) → (a_1 : Vec α n) → motive (n + 1) (Vec.cons a a_1)) → motive a t
-/
#guard_msgs in
#print sig Vec.cons.elim
```

This is a building block for non-quadratic implementations of `BEq` and
`DecidableEq` etc.

Builds on top of #9951.

The compiled code for a these functions could presumably, without
branching on the inductive value, directly access the fields. Achieving
this optimization (and achieving it without a quadratic compilation
cost) is not in scope for this PR.
2025-08-27 09:40:31 +00:00
Lean stage0 autoupdater
e4ca32174c chore: update stage0 2025-08-27 09:58:40 +00:00
Sebastian Ullrich
d06fff0f13 chore: CI: use restored ccache cache in update-stage0 2025-08-27 11:44:46 +02:00
Sebastian Ullrich
e74e9694fe feat: revamp and unify visibility/exposure handling in deriving handlers (#10148)
Visibility is now handled implicitly for all deriving handlers by
adjusting section visibility according to the presence of private types
while removing exposition on presence of private constructors can be
opted in on a per-handler level via the new combinator
`withoutExposeFromCtors`.

Fixes #10062 #10063 #10064 #10065
2025-08-27 09:10:24 +00:00
thorimur
5bb7818355 feat: allow position reporting in #guard_msgs (#10125)
This PR allows `#guard_msgs` to report the relative positions of logged
messages with the config option `(positions := true)`.

Closes #8265
2025-08-27 06:47:34 +00:00
Kyle Miller
5bc42bf5ca fix: pretty print dot notation for private definitions on public types (#10122)
This PR adds support for pretty printing using generalized field
notation (dot notation) for private definitions on public types. It also
modifies dot notation elaboration to resolve names after removing the
private prefix, which enables using dot notation for private definitions
on private imported types.

It won't pretty print with dot notation for definitions on inaccessible
private types from other modules.

Closes #7297
2025-08-27 03:30:52 +00:00
Leonardo de Moura
aaec0f584c feat: ac normalization in grind (#10146)
This PR implements the basic infrastructure for the new procedure
handling AC operators in grind. It already supports normalizing
disequalities. Future PRs will add support for simplification using
equalities, and computing critical pairs. Examples:
```lean
example {α : Sort u} (op : α → α → α) [Std.Associative op] (a b c : α)
    : op a (op b c) = op (op a b) c := by
  grind only

example {α : Sort u} (op : α → α → α) (u : α) [Std.Associative op] [Std.LawfulIdentity op u] (a b c : α)
    : op a (op b c) = op (op a b) (op c u) := by
  grind only

example {α : Type u} (op : α → α → α) (u : α) [Std.Associative op] [Std.Commutative op] 
    [Std.IdempotentOp op] [Std.LawfulIdentity op u] (a b c : α)
    : op (op a a) (op b c) = op (op (op b a) (op (op u b) b)) c := by
  grind only

example {α} (as bs cs : List α) : as ++ (bs ++ cs) = ((as ++ []) ++ bs) ++ (cs ++ []) := by
  grind only

example (a b c : Nat) : max a (max b c) = max (max b 0) (max a c) ∧ min a b = min b a := by
  grind only [cases Or]
```
2025-08-27 03:28:30 +00:00
Mac Malone
db3fb47109 refactor: port more of shell.cpp to Lean (#10086)
This PR ports more of the post-initialization C++ shell code to Lean.

All that remains is the initialization of the profiler and task manager.
As initialization tasks rather than main shell code, they were left in
C++ (where the rest of the initialization code currently is).

The `max_memory` and `timeout` Lean options used by the the `--memory`
and `--timeout` command-line options are now properly registered. The
server defaults for max memory and max heartbeats (timeout) were removed
as they were not actually used (because the `server` option that was
checked was neither set nor exists).

This PR also makes better use of the module system in `Shell.lean` and
fixes a minor bug in a previous port where the file name check was
dependent on building the `.ilean` rather than the `.c` file (as was
originally the case).

Fixes #9879.
2025-08-26 20:02:42 +00:00
Joachim Breitner
c83674bdff chore: revert use of macro_inline for ctorIdx (#10141)
This PR reverts the `macro_inline` part of #10135.
2025-08-26 18:07:49 +00:00
Leonardo de Moura
2652cc18b8 chore: error messages consistency (#10143)
This PR standardizes error messages by quoting names with backticks. The
changes were automated, so some cases may still be missing.
2025-08-26 17:55:43 +00:00
Lean stage0 autoupdater
62e00fb5a0 chore: update stage0 2025-08-26 17:42:03 +00:00
Marc Huisinga
2324c0939d chore: add private getUtf8Byte' to Init.Meta (#10140)
This PR adds a private `Lean.Name.getUtf8Byte'` to `Init.Meta` for a
future PR that optimizes `Lean.Name.escapePart`.
`Lean.Name.getUtf8Byte'` should be replaced with `String.getUtf8Byte`
once the string refactor is through.
2025-08-26 16:54:02 +00:00
Sebastian Ullrich
425bebe99e chore: further split libleanshared on Windows to avoid symbol limit (#10136)
Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-08-26 16:01:57 +00:00
Lean stage0 autoupdater
a0613f4d12 chore: update stage0 2025-08-26 16:01:23 +00:00
Sebastian Ullrich
298bd10f54 perf: do not cause compiler.small to export IR bodies unless the Expr body is already being exported (#10002) 2025-08-26 15:12:08 +00:00
Sebastian Ullrich
6810d31602 chore: CI: cache again on failure (#10137) 2025-08-26 14:47:05 +00:00
Luisa Cicolini
3e11f27ff4 feat: add fast circuit for unsigned multiplication overflow detection fastUmulOverflow_eq and surrounding definitions (#7858)
This PR implements the fast circuit for overflow detection in unsigned
multiplication used by Bitwuzla and proposed in:
https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=987767

The theorem is based on three definitions: 
* `uppcRec`: the unsigned parallel prefix circuit for the bits until a
certain `i`
* `aandRec`: the conjunction between the parallel prefix circuit at of
the first operand until a certain `i` and the `i`-th bit in the second
operand
* `resRec`: the preliminary overflow flag computed with these two
definitions
To establish the correspondence between these definitiions and their
meaning in `Nat`, we rely on `clz` and `clzAuxRec` definitions.
Therefore, this PR contains the `clz`- and `clzAuxRec`-related
infrastructure that was necessary to get the proofs through.

An additional change this PR contains is the moving of `### Count
leading zeros` section in `BitVec.Lemmas` downwards. In fact, some of
the proofs I wrote required introducing `Bitvec.toNat_lt_iff` and
`BitVec.le_toNat_iff` which I believe should live in the `Inequalities`
section. Therefore, to put these in the appropriate section, I decided
to move the whole `clz` section downwards (while it's small and
relatively self contained. Specifically, the theorems I moved are:
`clzAuxRec_zero`, `clzAuxRec_succ`, `clzAuxRec_eq_clzAuxRec_of_le`,
`clzAuxRec_eq_clzAuxRec_of_getLsbD_false`.
 
The fast circuit is not yet the default one in the bitblaster, as it's
performance is not yet competitive due to some missing rewrites that
bitwuzla supports but are not in Lean yet.
 
co-authored-by: @bollu

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2025-08-26 13:21:23 +00:00
Kim Morrison
a78a34bbd7 chore: replace Lean.Grind internal preorder classes with the classes from Std (#10129)
This PR replaces the interim order typeclasses used by `Grind` with the
new publicly available classes in `Std`.
2025-08-26 13:18:22 +00:00
Joachim Breitner
0803f1e77e perf: ctorIdx for single-constructor inductives: no casesOn, macro_inline (#10135)
This PR lets the `ctorIdx` definition for single constructor inductives
avoid the pointless `.casesOn`, and uses `macro_inline` to avoid
compiling the function and wasting symbols.
2025-08-26 13:00:10 +00:00
Kim Morrison
9e47edd0df feat: lemmas about rounding dyadics (#10138)
This PR adds lemmas about the `Dyadic.roundUp` and `Dyadic.roundDown`
operations.
2025-08-26 12:31:40 +00:00
Kim Morrison
0f1174d097 chore: use SMul rather than HMul in grind algebra typeclasses (#10095)
This PR modifies the `grind` algebra typeclasses to use `SMul x y`
instead of `HMul x y y`.
2025-08-26 12:23:37 +00:00
Marc Huisinga
f180eee7bf feat: use widget message for "try this" (#9966)
This PR adjusts the "try this" widget to be rendered as a widget message
under 'Messages', not a separate widget under a 'Suggestions' section.
The main benefit of this is that the message of the widget is not
duplicated between 'Messages' and 'Suggestions'.

Since widget message suggestions were already implemented by @jrr6 for
the new hint infrastructure, this PR replaces the old "try this"
implementation with the new hint infrastructure. In doing so, the
`style?` field of suggestions is deprecated, since the hint
infrastructure highlights hints using diff colors, and `style?` also
never saw much use downstream. Additionally, since the message and the
suggestion are now the same component, the `messageData?` field of
suggestions is deprecated as well. Notably, the "Try this:" message
string now also contains a newline and indentation to separate the
suggestion from the rest of the message more clearly and the `postInfo?`
field of the suggestion is now part of the message.

Finally, this PR changes the diff colors used by the hint infrastructure
to be more color-blindness-friendly (insertions are now blue, not green,
and text that remains unchanged is now using the editor foreground color
instead of blue).

### Breaking changes
Tests that use `#guard_msgs` to test the "Try this:" message may need to
be adjusted for the new formatting of the message.
2025-08-26 12:15:32 +00:00
Sebastian Ullrich
6a3fc281ad chore: CI: use Namespace.so checkout action for Linux Lake (#10103) 2025-08-26 09:19:58 +00:00
Lean stage0 autoupdater
06e9f4735a chore: update stage0 2025-08-26 09:46:07 +00:00
Joachim Breitner
0f5f2df11f fix: FunInd: handle let-vars-in-match-better (#10134)
This PR makes the generation of functional induction principles more
robust when the user `let`-binds a variable that is then `match`'ed on.
Fixes #10132.
2025-08-26 08:56:00 +00:00
Joachim Breitner
aa0cf78d93 chore: create .toCtorIdx alias only for enumeration types (#10130)
This PR creates the deprecated `.toCtorIdx` alias only for enumeration
types, which are the types that used to have this function. No need
generating an alias for types that never had it. Should reduce the
number of symbols in the standard library.
2025-08-26 08:33:37 +00:00
Sebastian Ullrich
4f94972ff1 chore: avoid panic in addDocString on partial elaboration (#10131) 2025-08-26 08:16:27 +00:00
Joachim Breitner
37dd26966b fix: rcases: avoid inflating case names with single constructor names (#9918)
This PR prevents `rcases` and `obtain` from creating absurdly long case
tag names when taking single constructor types (like `Exists`) apart.
Fixes #6550

The change does not affect `cases` and `induction`, it seems (where the
user might be surprised to not address the single goal with a name),
because I make the change in Lean/`Meta/Tactic/Induction.lean`, not
`Lean/Elab/Tactic/Induction.lean`. Yes, that's confusing.
2025-08-26 07:56:32 +00:00
Leonardo de Moura
1feac1ae92 chore: simplify grind import graph (#10128) 2025-08-26 06:34:44 +00:00
Leonardo de Moura
3ff195f7b2 refactor: grind build times (#10127) 2025-08-26 06:01:50 +00:00
Leonardo de Moura
5478dcf373 refactor: grind build times (#10126) 2025-08-26 04:06:37 +00:00
Kim Morrison
ad3e975178 feat: dyadic rationals (#9993)
This PR defines the dyadic rationals, showing they are an ordered ring
embedding into the rationals. We will use this for future interval
arithmetic tactics.

Many thanks to @Rob23oba, who did most of the implementation work here.

---------

Co-authored-by: Rob23oba <robin.arnez@web.de>
2025-08-26 03:49:39 +00:00
Leonardo de Moura
cd9865b26b refactor: grind build times (#10124) 2025-08-26 01:05:18 +00:00
Leonardo de Moura
8c4db341dd chore: use ofConstName in error messages (#10121) 2025-08-25 23:20:36 +00:00
Kim Morrison
a6a02fe6b9 chore: reduce Int imports on the critical path (#10123)
This PR shortens the rebuild critical path at
https://speed.lean-lang.org/lean4-out/cbf3814a565f7188f830f365453fb0bdd66d6175/
2025-08-25 23:07:02 +00:00
Kyle Miller
741347281c fix: dot notation for recursive invocation of private definitions (#10120)
This PR fixes an issue where private definitions recursively invoked
using generalized field notation (dot notation) would give an "invalid
field" errors. It also fixes an issue where "invalid field notation"
errors would pretty print the name of the declaration with a `_private`
prefix.

Closes #10044
2025-08-25 22:55:08 +00:00
Kim Morrison
a06e6e7f4d chore: make UInt.Lemmas a private import of String.Extra (#10115)
This PR makes the `Init.Data.UInt.Lemmas` import into
`Init.Data.String.Extra` private; previously this import was on the
rebuild critical path.
2025-08-25 16:46:22 +00:00
Lean stage0 autoupdater
505d5c6013 chore: update stage0 2025-08-25 17:01:52 +00:00
Joachim Breitner
13e8cb5a3a perf: reorder DiscrTree.Key constructors (#10110)
this PR reorders the `DiscrTree.Key` constructors to match the order
given in the manually written `DiscrTree.Key.ctorIdx`. This allows us to
use the auto-generated one, and moreover lets this code benefit from
special compiler support for `.ctorIdx`, once that lands.
2025-08-25 16:13:43 +00:00
Marc Huisinga
2107f45991 chore: revert #10111 (#10118)
Identical to #10052. #10116 fixed the underlying cause of test
flakiness, so this PR should hopefully be good-to-go now.
2025-08-25 15:45:03 +00:00
Marc Huisinga
a72f9429ea test: sort messages (#10116)
This PR normalizes the published diagnostics in the test runner so that
messages published out of order (due to parallelism) cannot cause test
failures. Clients can handle out-of-order messages just fine.
2025-08-25 15:08:11 +00:00
Sebastian Ullrich
321af0e02b fix: public structures with private field types under the module system (#10109)
Fixes #10099
2025-08-25 14:48:23 +00:00
Joachim Breitner
1718ca21cd feat: deprecate .toCtorIdx for .ctorIdx (#10113)
This PR deprecates `.toCtorIdx` for the more naturally named `.ctorIdx`
(and updates the standard library).
2025-08-25 14:32:05 +00:00
Sebastian Ullrich
f4ce319f1b chore: minimize Lean.Expr import (#10112) 2025-08-25 13:35:21 +00:00
Marc Huisinga
340c3da6ae chore: revert #10052 (#10111)
Potential suspect for flaky test failure.
2025-08-25 11:29:21 +00:00
Lean stage0 autoupdater
afbf52896f chore: update stage0 2025-08-25 11:31:26 +00:00
Joachim Breitner
afcf52e623 feat: .ctorIdx for all inductives (#9951)
This PR generates `.ctorIdx` functions for all inductive types, not just
enumeration types. This can be a building block for other constructions
(`BEq`, `noConfusion`) that are size-efficient even for large
inductives.

It also renames it from `.toCtorIdx` to `.ctorIdx`, which is the more
idiomatic naming.
The old name exists as an alias, with a deprecation attribute to be
added after the next
stage0 update.

These functions can arguably compiled down to a rather efficient tag
lookup, rather than a `case` statement. This is future work (but
hopefully near future).

For a fair number of basic types the compiler is not able to compile a
function using `casesOn` until further definitions have been defined.
This therefore (ab)uses the `genInjectivity` flag and
`gen_injective_theorems%` command to also control the generation of this
construct.

For (slightly) more efficient kernel reduction one could use `.rec`
rather than `.casesOn`. I did not do that yet, also because it
complicates compilation.
2025-08-25 10:47:06 +00:00
Sebastian Ullrich
3c40ea2733 chore: revert automatically exposing derived instances (#10101)
Heed surrounding `@[expose]` instead
2025-08-25 08:55:10 +00:00
Marc Huisinga
c95100e8fd fix: de-prioritize PartialTermInfo in hover info selection (#10047)
This PR ensures that hovering over `match` displays the type of the
match.
2025-08-25 08:47:14 +00:00
Marc Huisinga
be4651a772 fix: don't block fileworker with lake setup-file (#10052)
This PR fixes a bug that caused the Lean server process tree to survive
the closing of VS Code.

The cause of this issue was that the file worker main task was blocked
on waiting for the result of `lake setup-file` because the blocking call
was lifted outside of the dedicated server task that was supposed to
contain it by the compiler.
2025-08-25 08:47:01 +00:00
Rob23oba
797985e319 feat: upstream several Rat lemmas from mathlib (#10077)
This PR upstreams lemmas about `Rat` from `Mathlib.Data.Rat.Defs` and
`Mathlib.Algebra.Order.Ring.Unbundled.Rat`, specifically enough to get
`Lean.Grind.Field Rat` and `Lean.Grind.OrderedRing Rat`. In addition to
the lemmas, instances for `Inv Rat`, `Pow Rat Nat` and `Pow Rat Int`
have been upstreamed.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2025-08-25 06:02:27 +00:00
Kim Morrison
c9f08de7b3 feat: Lean.Grind.AddCommGroup instance for Rat (#10107)
This PR adds the `Lean.Grind.AddCommGroup` instance for `Rat`.
2025-08-25 05:15:26 +00:00
Leonardo de Moura
9be2eab93d feat: associative operator detection in grind (#10105)
This PR adds support for detecting associative operators in `grind`. The
new AC module also detects whether the operator is commutative,
idempotent, and whether it has a neutral element. The information is
cached.
2025-08-25 03:07:16 +00:00
Leonardo de Moura
cc5ff2afb1 test: grind cutsat (#10106) 2025-08-25 03:04:32 +00:00
Kim Morrison
5651192fa2 chore: fix error in Grind/Arith/Linear/StructId (#10096)
I'm unsure how this was ever working. Is this dead code?
2025-08-25 01:09:23 +00:00
Sebastian Ullrich
5ccea92a09 chore: revert "chore: CI: use Namespace.so checkout action for Linux Lake" (#10102)
Reverts leanprover/lean4#10100 pending a fix for running it on master
2025-08-24 15:58:25 +00:00
Sebastian Ullrich
3fc3f5d240 chore: CI: use Namespace.so checkout action for Linux Lake (#10100)
~1min ~> ~10s
2025-08-24 15:24:50 +00:00
Lean stage0 autoupdater
dca16fb58c chore: update stage0 2025-08-24 15:22:55 +00:00
Sebastian Ullrich
20d66250df feat: optional @[expose] specifier on deriving classes (#10060)
This PR allows for more fine-grained control over what derived instances
have exposed definitions under the module system: handlers should not
expose their implementation unless either the deriving item or a
surrounding section is marked with `@[expose]`. Built-in handlers to be
updated after a stage 0 update.
2025-08-24 14:42:18 +00:00
Kim Morrison
47632f27f6 chore: protect some theorems in the Rat namespace (#10097) 2025-08-24 10:51:47 +00:00
Leonardo de Moura
dfdd682c01 feat: AC theorems for grind (#10093)
This PR adds background theorems for a new solver to be implemented in
`grind` that will support associative and commutative operators.
2025-08-24 05:02:37 +00:00
Cameron Zwarich
8e828216e5 perf: inline dependent instances into specialized decls (#10080) 2025-08-24 00:12:27 +00:00
Lean stage0 autoupdater
92037b5b1b chore: update stage0 2025-08-23 23:33:05 +00:00
Mac Malone
a93e315e72 fix: include all of Lake.Util in core build (#10090)
This PR adds the modules in `Lake.Util` to core's Lake configuration to
ensure all utilities are built. With the module system port, they were
no longer all transitively imported.

Specifically, `Lake.Util.Lock` is unused because Lake does not currently
use a lock file for the build.
2025-08-23 22:55:39 +00:00
Mac Malone
902484988e fix: include Lake.Load in core build (#10087)
This PR adds `Lake.Load` to core's Lake configuration to ensure it is
built. With the module system port, it was no longer transitively
imported.
2025-08-23 21:52:15 +00:00
David Thrane Christiansen
c9727c2d19 feat: add a stop position field to the parser (#10043)
This PR allows Lean's parser to run with a final position prior to the
end of the string, so it can be invoked on a sub-region of the input.

This has applications in Verso proper, which parses Lean syntax in
contexts such as code blocks and docstrings, and it is a prerequisite to
parsing the contents of Lean docstrings.
2025-08-23 18:29:51 +00:00
Kyle Miller
0d9b7fb6b8 feat: adds rawIdent parser alias (#10085)
This PR adds a parser alias for the `rawIdent` parser, so that it can be
used in `syntax` declarations in `Init`.
2025-08-23 17:24:33 +00:00
Kyle Miller
db43de7b9d feat: add enter [in patt] syntax (#10081)
This PR adds `enter [in patt]` syntax. The implementation will come in a
followup PR, and it will stand for `pattern patt`.
2025-08-23 17:16:53 +00:00
Sebastian Ullrich
17f76f3bd7 chore: CI: recover dropped install suffixes (#10076) 2025-08-23 15:15:26 +00:00
Kim Morrison
6f69715f0a feat: replace Std.Internal.Rat (#9979)
This PR replaces `Std.Internal.Rat` with the new public `Rat` upstreamed
from Batteries.

The time library was depending on some defeqs which are no longer true,
so I have inserted some casts.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
Co-authored-by: Sofia Rodrigues <sofia@algebraic.dev>
2025-08-23 12:07:01 +00:00
Kim Morrison
2b34d5b899 feat: more Int lemmas in preparation for dyadics (#10075)
This PR contains lemmas about `Int` (minor amendments for BitVec and
Nat) that are being used in preparing the dyadics. This is all work of
@Rob23oba, which I'm pulling out of #9993 early to keep that one
manageable.
2025-08-23 04:59:59 +00:00
Cameron Zwarich
0881a8872b chore: use dotted constructor names (#10074) 2025-08-23 04:52:19 +00:00
Cameron Zwarich
91a2de1e1e chore: use forallTelescope rather than forallTelescopeReducing (#10073) 2025-08-23 04:07:23 +00:00
Cameron Zwarich
4a7def9e5f chore: use Expr.fvarId! and FVarId.getType (#10072) 2025-08-23 04:04:57 +00:00
Cameron Zwarich
dc5766d27a fix: eliminate infinite loop in toLCNF's expandNoConfusionMajor (#10070)
This PR fixes the compilation of `noConfusion` by repairing an oversight
made when porting this code from the old compiler. The old compiler only
repeatedly expanded the major for each non-`Prop` field of the inductive
under consideration, mirroring the construction of `noConfusion` itself,
whereas the new compiler erroneously counted all fields.

Fixes #9971.
2025-08-23 02:18:51 +00:00
Leonardo de Moura
a63d483258 feat: pow support in grind cutsat (#10071)
This PR improves support for `a^n` in `grind cutsat`. For example, if
`cutsat` discovers that `a` and `b` are equal to numerals, it now
propagates the equality. This PR is similar to #9996, but `a^b`.
Example:

```lean
example (n : Nat) : n = 2 → 2 ^ (n+1) = 8 := by
  grind
```

With #10022, it also improves the support for `BitVec n` when `n` is not
numeral. Example:

```lean
example {n m : Nat} (x : BitVec n)
    : 2 ≤ n → n ≤ m → m = 2 → x = 0 ∨ x = 1 ∨ x = 2 ∨ x = 3 := by
  grind
```
2025-08-23 01:55:05 +00:00
Mac Malone
1f9bba9d39 refactor: lake: use module (#9749)
This PR refactors the Lake codebase to use the new module system
throughout. Every module in `Lake` is now a `module`.

As this was already a large-scale refactor, a general cleanup of the
code has also been bundled in.

This PR also uses workarounds for currently outstanding module system
issues: #10061, #10062, #10063, #10064, #10065, #10067, and #10068.

**Breaking change:** Since the module system encourages a
`private`-by-default design, the Lake API has switched from its previous
`public`-by-default approach. As such, many definitions that were
previously public are now private. The newly private definitions are not
expected to have had significant user use, Nonetheless, important use
cases could be missed. If a key API is now inaccessible but seems like
it should be public, users are encouraged to report this as an issue on
GitHub.
2025-08-22 23:02:33 +00:00
Leonardo de Moura
5daf65ec56 feat: add helper theorems for NatModule (#10069)
This PR adds helper theorems to support `NatModule` in `grind linarith`.
2025-08-22 20:36:05 +00:00
Paul Reichert
9d4665a0bf perf: quick fix for range elaboration performance (#10066)
This PR reverts parts of #10005 that surprisingly turned out to cause a
performance regression in the benchmarks. The slowdown seems to be
related to elaboration, not inefficiencies in the generated code. This
is just a quick fix. I will take a closer look in a week.
2025-08-22 20:30:13 +00:00
Lean stage0 autoupdater
6df94385c5 chore: update stage0 2025-08-22 17:52:06 +00:00
David Thrane Christiansen
82932ec86a feat: add stop position to parser (#10057)
This PR adds a stop position field to parser input contexts, allowing
the parser to be instructed to stop parsing prior to the end of a file.

This is step 1, prior to a stage0 update, to make run-time data
structures sufficiently compatible to avoid segfaults. After the update,
the actual code to stop parsing can be merged.
2025-08-22 17:04:04 +00:00
Lean stage0 autoupdater
3d7d35b588 chore: update stage0 2025-08-22 16:03:06 +00:00
Sebastian Ullrich
fb23d7b45d chore: make parseQuotWithCurrentStage do what it says under prefer_native (#10058)
Switch on the interpreter when entering quotations under this option
2025-08-22 15:14:43 +00:00
Paul Reichert
f12177d01e feat: introduce Int range notation (#10045)
This PR implements the necessary typeclasses so that range notation
works for integers. For example, `((-2)...3).toList = [-2, -1, 0, 1, 2]
: List Int`.
2025-08-22 14:41:39 +00:00
Sebastian Ullrich
68654c231b doc: fix examples link 2025-08-22 16:28:01 +02:00
Lean stage0 autoupdater
2adc21f28b chore: update stage0 2025-08-22 14:07:57 +00:00
Sebastian Ullrich
0528696bbe fix: deriving DecidableEq under the module system (#10030)
Fixes #9839
2025-08-22 13:18:31 +00:00
Sebastian Ullrich
51bba5338a perf: make macro scope numbering less dependent on surrounding context (#10027)
This PR changes macro scope numbering from per-module to per-command,
ensuring that unrelated changes to other commands do not affect macro
scopes generated by a command, which improves `prefer_native` hit rates
on bootstrapping as well as avoids further rebuilds under the module
system.

In detail, instead of always using the current module name as a macro
scope prefix, each command now introduces a new macro scope prefix
(called "context") of the shape `<main module>._hygCtx_<uniq>` where
`uniq` is a `UInt32` derived from the command but automatically
incremented in case of conflicts (which must be local to the current
module). In the current implementation, `uniq` is the hash of the
declaration name, if any, or else the hash of the full command's syntax.
Thus, it is always independent of syntactic changes to other commands
(except in case of hash conflicts, which should only happen in practice
for syntactically identical commands) and, in the case of declarations,
also independent of syntactic changes to any private parts of the
declaration.
2025-08-22 13:16:02 +00:00
Sebastian Ullrich
561a4510b3 fix: auto params on private structure fields (#10053) 2025-08-22 12:49:37 +00:00
Sebastian Ullrich
0e8838df3b chore: avoid confusing public import all combination (#10051) 2025-08-22 12:04:42 +00:00
Kim Morrison
385daa99a8 chore: cleanup in Data/Rat (#10050)
This PR fixes some naming issues in Data/Rat/Lemmas, and upstreams the
eliminator `numDenCasesOn` and its relatives.
2025-08-22 12:00:19 +00:00
Kim Morrison
7595bc0791 feat: theorems about Nat/Int/Rat needed for dyadics (#10049)
This PR adds some background material needed for introducing the dyadic
rationals in #9993.
2025-08-22 11:45:16 +00:00
Rob23oba
9c6b698227 perf: fast version of Nat.log2 (#10046)
This PR replaces the implementation of `Nat.log2` with a version that
reduces faster.
The new version can handle:
```lean-4
example : Nat.log2 (1 <<< 500) = 500 := rfl
```
2025-08-22 11:32:00 +00:00
Henrik Böving
962ba9649c perf: try to reduce amount of code generated by HashMaps (#9941)
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-08-22 11:24:11 +00:00
Sebastian Ullrich
8c8a6021af perf: use constant macro scope in elabArrow (#10048)
Shrinks .olean size by avoiding variation in this very frequent but
mostly unused name
2025-08-22 11:16:05 +00:00
3314 changed files with 58864 additions and 22998 deletions

6
.gitattributes vendored
View File

@@ -4,3 +4,9 @@ RELEASES.md merge=union
stage0/** binary linguist-generated
# The following file is often manually edited, so do show it in diffs
stage0/src/stdlib_flags.h -binary -linguist-generated
# These files should not have line endings translated on Windows, because
# it throws off parser tests. Later lines override earlier ones, so the
# runner code is still treated as ordinary text.
tests/lean/docparse/* eol=lf
tests/lean/docparse/*.lean eol=auto
tests/lean/docparse/*.sh eol=auto

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: actionlint
uses: raven-actions/actionlint@v2
with:

View File

@@ -36,7 +36,7 @@ jobs:
include: ${{fromJson(inputs.config)}}
# complete all jobs
fail-fast: false
runs-on: ${{ matrix.os }}
runs-on: ${{ endsWith(matrix.os, '-with-cache') && fromJSON(format('["{0}", "nscloud-git-mirror-1gb"]', matrix.os)) || matrix.os }}
defaults:
run:
shell: ${{ matrix.shell || 'nix develop -c bash -euxo pipefail {0}' }}
@@ -69,10 +69,16 @@ jobs:
brew install ccache tree zstd coreutils gmp libuv
if: runner.os == 'macOS'
- name: Checkout
uses: actions/checkout@v4
if: (!endsWith(matrix.os, '-with-cache'))
uses: actions/checkout@v5
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Namespace Checkout
if: endsWith(matrix.os, '-with-cache')
uses: namespacelabs/nscloud-checkout-action@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Open Nix shell once
run: true
if: runner.os == 'Linux'
@@ -169,7 +175,9 @@ jobs:
# Should be done as early as possible and in particular *before* "Check rebootstrap" which
# changes the state of stage1/
- name: Save Cache
if: steps.restore-cache.outputs.cache-hit != 'true'
# Caching on cancellation created some mysterious issues perhaps related to improper build
# shutdown
if: steps.restore-cache.outputs.cache-hit != 'true' && !cancelled()
uses: actions/cache/save@v4
with:
# NOTE: must be in sync with `restore` above
@@ -215,13 +223,13 @@ jobs:
path: pack/*
- name: Lean stats
run: |
build/stage1/bin/lean --stats src/Lean.lean -Dexperimental.module=true
build/$TARGET_STAGE/bin/lean --stats src/Lean.lean -Dexperimental.module=true
if: ${{ !matrix.cross }}
- name: Test
id: test
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
- name: Test Summary
uses: test-summary/action@v2

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}

View File

@@ -8,7 +8,7 @@ jobs:
check-stage0-on-queue:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
filter: blob:none

View File

@@ -54,7 +54,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
- name: Set Nightly
@@ -182,12 +182,10 @@ jobs:
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16" : "ubuntu-latest",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"check-level": 0,
"test": true,
"check-rebootstrap": level >= 1,
@@ -202,8 +200,6 @@ jobs:
"os": "ubuntu-latest",
"check-level": 2,
"CMAKE_PRESET": "reldebug",
// exclude seriously slow/stackoverflowing tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress|3807'"
},
// TODO: suddenly started failing in CI
/*{
@@ -225,8 +221,7 @@ jobs:
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
"CTEST_OPTIONS": "-E 'leanlaketest_hello'", // started failing from unpack
},
{
"name": "macOS aarch64",
@@ -242,8 +237,6 @@ jobs:
// See "Linux release" for release job levels; Grove is not a concern here
"check-level": isPr ? 0 : 2,
"secondary": isPr,
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Windows",
@@ -252,13 +245,9 @@ jobs:
"check-level": 2,
"shell": "msys2 {0}",
"CMAKE_OPTIONS": "-G \"Unix Makefiles\"",
// for reasons unknown, interactivetests are flaky on Windows
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
{
"name": "Linux aarch64",
@@ -269,8 +258,6 @@ jobs:
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
// not compatible with `prepare-llvm` currently
"CMAKE_OPTIONS": "-DUSE_LAKE=OFF",
},
// Started running out of memory building expensive modules, a 2GB heap is just not that much even before fragmentation
//{
@@ -299,6 +286,12 @@ jobs:
// "CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_tempfile.lean\\.|leanruntest_libuv\\.lean\""
// }
];
for (const job of matrix) {
if (job["prepare-llvm"]) {
// `USE_LAKE` is not compatible with `prepare-llvm` currently
job["CMAKE_OPTIONS"] = (job["CMAKE_OPTIONS"] ? job["CMAKE_OPTIONS"] + " " : "") + "-DUSE_LAKE=OFF";
}
}
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
matrix = matrix.filter((job) => level >= job["check-level"]);
core.setOutput('matrix', matrix.filter((job) => !job["secondary"]));
@@ -370,7 +363,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
path: artifacts
- name: Release
@@ -395,12 +388,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# needed for tagging
fetch-depth: 0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
path: artifacts
- name: Prepare Nightly Release

View File

@@ -6,7 +6,7 @@ jobs:
check-lean-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Verify .lean files start with a copyright header.
run: |

View File

@@ -395,7 +395,7 @@ jobs:
# Checkout the Batteries repository with all branches
- name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }}
@@ -454,7 +454,7 @@ jobs:
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: leanprover-community/mathlib4-nightly-testing
token: ${{ secrets.MATHLIB4_BOT }}
@@ -524,7 +524,7 @@ jobs:
# Checkout the reference manual repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: leanprover/reference-manual
token: ${{ secrets.MANUAL_PR_BOT }}

View File

@@ -19,11 +19,15 @@ concurrency:
jobs:
update-stage0:
runs-on: nscloud-ubuntu-22.04-amd64-8x16
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
CCACHE_MAXSIZE: 400M
steps:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"
@@ -70,10 +74,14 @@ jobs:
restore-keys: |
Linux Lake-build-v3
- if: env.should_update_stage0 == 'yes'
run: cmake --preset release
# sync options with `Linux Lake` to ensure cache reuse
run: |
mkdir -p build
cmake --preset release -B build -DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: make -j$NPROC -C build/release update-stage0-commit
run: |
make -j$NPROC -C build update-stage0-commit
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: git show --stat

View File

@@ -2,19 +2,19 @@ This is the repository for **Lean 4**.
# About
- [Quickstart](https://lean-lang.org/documentation/setup/)
- [Quickstart](https://lean-lang.org/install/)
- [Homepage](https://lean-lang.org)
- [Theorem Proving Tutorial](https://lean-lang.org/theorem_proving_in_lean4/)
- [Functional Programming in Lean](https://lean-lang.org/functional_programming_in_lean/)
- [Documentation Overview](https://lean-lang.org/documentation/)
- [Documentation Overview](https://lean-lang.org/learn/)
- [Language Reference](https://lean-lang.org/doc/reference/latest/)
- [Release notes](RELEASES.md) starting at v4.0.0-m3
- [Examples](https://lean-lang.org/documentation/examples/)
- [Examples](https://lean-lang.org/examples/)
- [External Contribution Guidelines](CONTRIBUTING.md)
# Installation
See [Setting Up Lean](https://lean-lang.org/documentation/setup/).
See [Install Lean](https://lean-lang.org/install/).
# Contributing

View File

@@ -8,7 +8,7 @@ You should not edit the `stage0` directory except using the commands described i
## Development Setup
You can use any of the [supported editors](../setup.md) for editing the Lean source code.
You can use any of the [supported editors](https://lean-lang.org/install/manual/) for editing the Lean source code.
Please see below for specific instructions for VS Code.
### Dev setup using elan
@@ -99,3 +99,19 @@ on to `nightly-with-manual` branch. (It is fine to force push after rebasing.)
CI will generate a branch of the reference manual called `lean-pr-testing-NNNN`
in `leanprover/reference-manual`. This branch uses the toolchain for your PR,
and will report back to the Lean PR with results from Mathlib CI.
### Avoiding rebuilds for downstream projects
If you want to test changes to Lean on downstream projects and would like to avoid rebuilding modules you have already built/fetched using the project's configured Lean toolchain, you can often do so as long as your build of Lean is close enough to that Lean toolchain (compatible .olean format including structure of all relevant environment extensions).
To override the toolchain without rebuilding for a single command, for example `lake build` or `lake lean`, you can use the prefix
```
LEAN_GITHASH=$(lean --githash) lake +lean4 ...
```
Alternatively, use
```
export LEAN_GITHASH=$(lean --githash)
export ELAN_TOOLCHAIN=lean4
```
to persist these changes for the lifetime of the current shell, which will affect any processes spawned from it such as VS Code started via `code .`.
If you use a setup where you cannot directly start your editor from the command line, such as VS Code Remote, you might want to consider using [direnv](https://direnv.net/) together with an editor extension for it instead so that you can put the lines above into `.envrc`.

View File

@@ -1,6 +1,6 @@
These are instructions to set up a working development environment for those who wish to make changes to Lean itself. It is part of the [Development Guide](../dev/index.md).
We strongly suggest that new users instead follow the [Quickstart](../quickstart.md) to get started using Lean, since this sets up an environment that can automatically manage multiple Lean toolchain versions, which is necessary when working within the Lean ecosystem.
We strongly suggest that new users instead follow the [Installation Instructions](https://lean-lang.org/install/) to get started using Lean, since this sets up an environment that can automatically manage multiple Lean toolchain versions, which is necessary when working within the Lean ecosystem.
Requirements
------------

View File

@@ -37,6 +37,15 @@
"isDefault": true
}
},
{
"label": "build-old",
"type": "shell",
"command": "make -C build/release -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4) LAKE_EXTRA_ARGS=--old",
"problemMatcher": [],
"group": {
"kind": "build"
}
},
{
"label": "test",
"type": "shell",

View File

@@ -97,5 +97,36 @@ macro "#analyzeEMatchTheorems" : command => `(
#analyzeEMatchTheorems
-- -- We can analyze specific theorems using commands such as
set_option trace.grind.ematch.instance true in
run_meta analyzeEMatchTheorem ``List.filterMap_some {}
set_option trace.grind.ematch.instance true
-- 1. grind immediately sees `(#[] : Array α) = ([] : List α).toArray` but probably this should be hidden.
-- 2. `Vector.toArray_empty` keys on `Array.mk []` rather than `#v[].toArray`
-- I guess we could add `(#[].extract _ _).extract _ _` as a stop pattern.
run_meta analyzeEMatchTheorem ``Array.extract_empty {}
-- Neither `Option.bind_some` nor `Option.bind_fun_some` fire, because the terms appear inside
-- lambdas. So we get crazy things like:
-- `fun x => ((some x).bind some).bind fun x => (some x).bind fun x => (some x).bind some`
-- We could consider replacing `filterMap_some` with
-- `filterMap g (filterMap f xs) = filterMap (f >=> g) xs`
-- to avoid the lambda that `grind` struggles with, but this would require more API around the fish.
run_meta analyzeEMatchTheorem ``Array.filterMap_some {}
-- Not entirely certain what is wrong here, but certainly
-- `eq_empty_of_append_eq_empty` is firing too often.
-- Ideally we could instantiate this is we fine `xs ++ ys` in the same equivalence class,
-- note just as soon as we see `xs ++ ys`.
-- I've tried removing this in https://github.com/leanprover/lean4/pull/10162
run_meta analyzeEMatchTheorem ``Array.range'_succ {}
-- Perhaps the same story here.
run_meta analyzeEMatchTheorem ``Array.range_succ {}
-- `zip_map_left` and `zip_map_right` are bad grind lemmas,
-- checking if they can be removed in https://github.com/leanprover/lean4/pull/10163
run_meta analyzeEMatchTheorem ``Array.zip_map {}
-- It seems crazy to me that as soon as we have `0 >>> n = 0`, we instantiate based on the
-- pattern `0 >>> n >>> m` by substituting `0` into `0 >>> n` to produce the `0 >>> n >>> n`.
-- I don't think any forbidden subterms can help us here. I don't know what to do. :-(
run_meta analyzeEMatchTheorem ``Int.zero_shiftRight {}

View File

@@ -5,6 +5,7 @@ Merge a tag into a branch on a GitHub repository.
This script checks if a specified tag can be merged cleanly into a branch and performs
the merge if possible. If the merge cannot be done cleanly, it prints a helpful message.
Merge conflicts in the lean-toolchain file are automatically resolved by accepting the incoming changes.
Usage:
python3 merge_remote.py <org/repo> <branch> <tag>
@@ -58,6 +59,32 @@ def clone_repo(repo, temp_dir):
return True
def get_conflicted_files():
"""Get list of files with merge conflicts."""
result = run_command("git diff --name-only --diff-filter=U", check=False)
if result.returncode == 0:
return result.stdout.strip().split('\n') if result.stdout.strip() else []
return []
def resolve_lean_toolchain_conflict(tag):
"""Resolve lean-toolchain conflict by accepting incoming (tag) changes."""
print("Resolving lean-toolchain conflict by accepting incoming changes...")
# Accept theirs (incoming) version for lean-toolchain
result = run_command(f"git checkout --theirs lean-toolchain", check=False)
if result.returncode != 0:
print("Failed to resolve lean-toolchain conflict")
return False
# Add the resolved file
add_result = run_command("git add lean-toolchain", check=False)
if add_result.returncode != 0:
print("Failed to stage resolved lean-toolchain")
return False
return True
def check_and_merge(repo, branch, tag, temp_dir):
"""Check if tag can be merged into branch and perform the merge if possible."""
# Change to the temporary directory
@@ -98,12 +125,37 @@ def check_and_merge(repo, branch, tag, temp_dir):
# Try merging the tag directly
print(f"Merging {tag} into {branch}...")
merge_result = run_command(f"git merge {tag} --no-edit", check=False)
if merge_result.returncode != 0:
print(f"Cannot merge {tag} cleanly into {branch}.")
print("Merge conflicts would occur. Aborting merge.")
run_command("git merge --abort")
return False
# Check which files have conflicts
conflicted_files = get_conflicted_files()
if conflicted_files == ['lean-toolchain']:
# Only lean-toolchain has conflicts, resolve it
print("Merge conflict detected only in lean-toolchain.")
if resolve_lean_toolchain_conflict(tag):
# Continue the merge with the resolved conflict
print("Continuing merge with resolved lean-toolchain...")
continue_result = run_command(f"git commit --no-edit", check=False)
if continue_result.returncode != 0:
print("Failed to complete merge after resolving lean-toolchain")
run_command("git merge --abort")
return False
else:
print("Failed to resolve lean-toolchain conflict")
run_command("git merge --abort")
return False
else:
# Other files have conflicts, or unable to determine
if conflicted_files:
print(f"Cannot merge {tag} cleanly into {branch}.")
print(f"Merge conflicts in: {', '.join(conflicted_files)}")
else:
print(f"Cannot merge {tag} cleanly into {branch}.")
print("Merge conflicts would occur.")
print("Aborting merge.")
run_command("git merge --abort")
return False
print(f"Pushing changes to remote...")
push_result = run_command(f"git push origin {branch}")

View File

@@ -52,6 +52,7 @@ def sort_sections_order():
return [
"Language",
"Library",
"Tactics",
"Compiler",
"Pretty Printing",
"Documentation",

View File

@@ -1,4 +1,11 @@
repositories:
- name: lean4-cli
url: https://github.com/leanprover/lean4-cli
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: batteries
url: https://github.com/leanprover-community/batteries
toolchain-tag: true
@@ -7,6 +14,13 @@ repositories:
bump-branch: true
dependencies: []
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: lean4checker
url: https://github.com/leanprover/lean4checker
toolchain-tag: true
@@ -21,20 +35,6 @@ repositories:
branch: master
dependencies: []
- name: lean4-cli
url: https://github.com/leanprover/lean4-cli
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: plausible
url: https://github.com/leanprover-community/plausible
toolchain-tag: true
@@ -96,6 +96,15 @@ repositories:
- import-graph
- plausible
- name: cslib
url: https://github.com/leanprover/cslib
toolchain-tag: true
stable-branch: true
branch: main
bump-branch: true
dependencies:
- mathlib4
- name: repl
url: https://github.com/leanprover-community/repl
toolchain-tag: true
@@ -103,3 +112,11 @@ repositories:
branch: master
dependencies:
- mathlib4
- name: lean-fro.org
url: https://github.com/leanprover/lean-fro.org
toolchain-tag: false
stable-branch: false
branch: master
dependencies:
- verso

View File

@@ -377,6 +377,33 @@ def execute_release_steps(repo, version, config):
except subprocess.CalledProcessError as e:
print(red("Tests failed, but continuing with PR creation..."))
print(red(f"Test error: {e}"))
elif repo_name == "lean-fro.org":
# Update lean-toolchain in examples/hero
print(blue("Updating examples/hero/lean-toolchain..."))
docs_toolchain = repo_path / "examples" / "hero" / "lean-toolchain"
with open(docs_toolchain, "w") as f:
f.write(f"leanprover/lean4:{version}\n")
print(green(f"Updated examples/hero/lean-toolchain to leanprover/lean4:{version}"))
print(blue("Running `lake update`..."))
run_command("lake update", cwd=repo_path, stream_output=True)
print(blue("Running `lake update` in examples/hero..."))
run_command("lake update", cwd=repo_path / "examples" / "hero", stream_output=True)
elif repo_name == "cslib":
print(blue("Updating lakefile.toml..."))
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
print(blue("Updating docs/lakefile.toml..."))
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path / "docs")
# Update lean-toolchain in docs
print(blue("Updating docs/lean-toolchain..."))
docs_toolchain = repo_path / "docs" / "lean-toolchain"
with open(docs_toolchain, "w") as f:
f.write(f"leanprover/lean4:{version}\n")
print(green(f"Updated docs/lean-toolchain to leanprover/lean4:{version}"))
run_command("lake update", cwd=repo_path, stream_output=True)
elif dependencies:
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
run_command("lake update", cwd=repo_path, stream_output=True)

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 24)
set(LEAN_VERSION_MINOR 25)
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'")
@@ -469,6 +469,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
string(APPEND LEANSHARED_2_LINKER_FLAGS " -install_name @rpath/libleanshared_2.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
@@ -502,7 +503,7 @@ endif()
# are already loaded) and probably fail unless we set up LD_LIBRARY_PATH.
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# import libraries created by the stdlib.make targets
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_2 -lleanshared_1 -lleanshared")
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
# The second flag is necessary to even *load* dylibs without resolved symbols, as can happen
# if a Lake `extern_lib` depends on a symbols defined by the Lean library but is loaded even
@@ -589,7 +590,7 @@ endif()
add_subdirectory(initialize)
add_subdirectory(shell)
# to be included in `leanshared` but not the smaller `leanshared_1` (as it would pull
# to be included in `leanshared` but not the smaller `leanshared_*` (as it would pull
# in the world)
add_library(leaninitialize STATIC $<TARGET_OBJECTS:initialize>)
set_target_properties(leaninitialize PROPERTIES
@@ -714,6 +715,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
)
add_custom_target(leanshared ALL
DEPENDS Init_shared leancpp
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared_2${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared_1${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX}
)
@@ -734,7 +736,7 @@ else()
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
VERBATIM)
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared_2 -lleanshared_1 -lleanshared")
endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")

View File

@@ -42,5 +42,8 @@ public import Init.While
public import Init.Syntax
public import Init.Internal
public import Init.Try
public meta import Init.Try -- make sure `Try.Config` can be evaluated anywhere
public import Init.BinderNameHint
public import Init.Task
public import Init.MethodSpecsSimp
public import Init.LawfulBEqTactics

View File

@@ -19,8 +19,8 @@ variable {ε σ α : Type u}
instance [ToString ε] [ToString α] : ToString (Result ε σ α) where
toString
| Result.ok a _ => "ok: " ++ toString a
| Result.error e _ => "error: " ++ toString e
| Result.ok a _ => String.Internal.append "ok: " (toString a)
| Result.error e _ => String.Internal.append "error: " (toString e)
instance [Repr ε] [Repr α] : Repr (Result ε σ α) where
reprPrec

View File

@@ -147,7 +147,7 @@ class LawfulMonad (m : Type u → Type v) [Monad m] : Prop extends LawfulApplica
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
attribute [simp] pure_bind bind_assoc bind_pure_comp
attribute [grind] pure_bind
attribute [grind <=] pure_bind
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
change x >>= (fun a => pure (id a)) = x

View File

@@ -7,8 +7,10 @@ module
prelude
public import Init.Control.Lawful.Basic
public import all Init.Control.Except
public import all Init.Control.State
public import Init.Control.Except
import all Init.Control.Except
public import Init.Control.State
import all Init.Control.State
public import Init.Control.StateRef
public import Init.Ext
@@ -20,23 +22,24 @@ open Function
namespace ExceptT
@[ext] theorem ext {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
@[ext, grind ext] theorem ext {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
simp [run] at h
assumption
@[simp] theorem run_pure [Monad m] (x : α) : run (pure x : ExceptT ε m α) = pure (Except.ok x) := rfl
@[simp, grind =] theorem run_pure [Monad m] (x : α) : run (pure x : ExceptT ε m α) = pure (Except.ok x) := rfl
@[simp] theorem run_lift [Monad.{u, v} m] (x : m α) : run (ExceptT.lift x : ExceptT ε m α) = (Except.ok <$> x : m (Except ε α)) := rfl
@[simp, grind =] theorem run_lift [Monad.{u, v} m] (x : m α) : run (ExceptT.lift x : ExceptT ε m α) = (Except.ok <$> x : m (Except ε α)) := rfl
@[simp] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl
@[simp, grind =] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl
@[simp] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by
@[simp, grind =] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by
simp [ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont]
@[simp] theorem bind_throw [Monad m] [LawfulMonad m] (f : α ExceptT ε m β) : (throw e >>= f) = throw e := by
@[simp, grind =] theorem bind_throw [Monad m] [LawfulMonad m] (f : α ExceptT ε m β) : (throw e >>= f) = throw e := by
simp [throw, throwThe, MonadExceptOf.throw, bind, ExceptT.bind, ExceptT.bindCont, ExceptT.mk]
theorem run_bind [Monad m] (x : ExceptT ε m α)
@[grind =]
theorem run_bind [Monad m] (x : ExceptT ε m α) (f : α ExceptT ε m β)
: run (x >>= f : ExceptT ε m β)
=
run x >>= fun
@@ -44,10 +47,10 @@ theorem run_bind [Monad m] (x : ExceptT ε m α)
| Except.error e => pure (Except.error e) :=
rfl
@[simp] theorem lift_pure [Monad m] [LawfulMonad m] (a : α) : ExceptT.lift (pure a) = (pure a : ExceptT ε m α) := by
@[simp, grind =] theorem lift_pure [Monad m] [LawfulMonad m] (a : α) : ExceptT.lift (pure a) = (pure a : ExceptT ε m α) := by
simp [ExceptT.lift, pure, ExceptT.pure]
@[simp] theorem run_map [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α)
@[simp, grind =] theorem run_map [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α)
: (f <$> x).run = Except.map f <$> x.run := by
simp [Functor.map, ExceptT.map, bind_pure_comp]
apply bind_congr
@@ -111,28 +114,28 @@ instance : LawfulFunctor (Except ε) := inferInstance
namespace ReaderT
@[ext] theorem ext {x y : ReaderT ρ m α} (h : ctx, x.run ctx = y.run ctx) : x = y := by
@[ext, grind ext] theorem ext {x y : ReaderT ρ m α} (h : ctx, x.run ctx = y.run ctx) : x = y := by
simp [run] at h
exact funext h
@[simp] theorem run_pure [Monad m] (a : α) (ctx : ρ) : (pure a : ReaderT ρ m α).run ctx = pure a := rfl
@[simp, grind =] theorem run_pure [Monad m] (a : α) (ctx : ρ) : (pure a : ReaderT ρ m α).run ctx = pure a := rfl
@[simp] theorem run_bind [Monad m] (x : ReaderT ρ m α) (f : α ReaderT ρ m β) (ctx : ρ)
@[simp, grind =] theorem run_bind [Monad m] (x : ReaderT ρ m α) (f : α ReaderT ρ m β) (ctx : ρ)
: (x >>= f).run ctx = x.run ctx >>= λ a => (f a).run ctx := rfl
@[simp] theorem run_mapConst [Monad m] (a : α) (x : ReaderT ρ m β) (ctx : ρ)
@[simp, grind =] theorem run_mapConst [Monad m] (a : α) (x : ReaderT ρ m β) (ctx : ρ)
: (Functor.mapConst a x).run ctx = Functor.mapConst a (x.run ctx) := rfl
@[simp] theorem run_map [Monad m] (f : α β) (x : ReaderT ρ m α) (ctx : ρ)
@[simp, grind =] theorem run_map [Monad m] (f : α β) (x : ReaderT ρ m α) (ctx : ρ)
: (f <$> x).run ctx = f <$> x.run ctx := rfl
@[simp] theorem run_monadLift [MonadLiftT n m] (x : n α) (ctx : ρ)
@[simp, grind =] theorem run_monadLift [MonadLiftT n m] (x : n α) (ctx : ρ)
: (monadLift x : ReaderT ρ m α).run ctx = (monadLift x : m α) := rfl
@[simp] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} n β n β) (x : ReaderT ρ m α) (ctx : ρ)
@[simp, grind =] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} n β n β) (x : ReaderT ρ m α) (ctx : ρ)
: (monadMap @f x : ReaderT ρ m α).run ctx = monadMap @f (x.run ctx) := rfl
@[simp] theorem run_read [Monad m] (ctx : ρ) : (ReaderT.read : ReaderT ρ m ρ).run ctx = pure ctx := rfl
@[simp, grind =] theorem run_read [Monad m] (ctx : ρ) : (ReaderT.read : ReaderT ρ m ρ).run ctx = pure ctx := rfl
@[simp] theorem run_seq {α β : Type u} [Monad m] (f : ReaderT ρ m (α β)) (x : ReaderT ρ m α) (ctx : ρ)
: (f <*> x).run ctx = (f.run ctx <*> x.run ctx) := rfl
@@ -173,38 +176,39 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (StateRefT' ω σ m) :=
namespace StateT
@[ext] theorem ext {x y : StateT σ m α} (h : s, x.run s = y.run s) : x = y :=
@[ext, grind ext] theorem ext {x y : StateT σ m α} (h : s, x.run s = y.run s) : x = y :=
funext h
@[simp] theorem run'_eq [Monad m] (x : StateT σ m α) (s : σ) : run' x s = (·.1) <$> run x s :=
@[simp, grind =] theorem run'_eq [Monad m] (x : StateT σ m α) (s : σ) : run' x s = (·.1) <$> run x s :=
rfl
@[simp] theorem run_pure [Monad m] (a : α) (s : σ) : (pure a : StateT σ m α).run s = pure (a, s) := rfl
@[simp, grind =] theorem run_pure [Monad m] (a : α) (s : σ) : (pure a : StateT σ m α).run s = pure (a, s) := rfl
@[simp] theorem run_bind [Monad m] (x : StateT σ m α) (f : α StateT σ m β) (s : σ)
@[simp, grind =] theorem run_bind [Monad m] (x : StateT σ m α) (f : α StateT σ m β) (s : σ)
: (x >>= f).run s = x.run s >>= λ p => (f p.1).run p.2 := by
simp [bind, StateT.bind, run]
@[simp] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
@[simp, grind =] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
simp [Functor.map, StateT.map, run, bind_pure_comp]
@[simp] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
@[simp, grind =] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
@[simp] theorem run_set [Monad m] (s s' : σ) : (set s' : StateT σ m PUnit).run s = pure (, s') := rfl
@[simp, grind =] theorem run_set [Monad m] (s s' : σ) : (set s' : StateT σ m PUnit).run s = pure (, s') := rfl
@[simp] theorem run_modify [Monad m] (f : σ σ) (s : σ) : (modify f : StateT σ m PUnit).run s = pure (, f s) := rfl
@[simp, grind =] theorem run_modify [Monad m] (f : σ σ) (s : σ) : (modify f : StateT σ m PUnit).run s = pure (, f s) := rfl
@[simp] theorem run_modifyGet [Monad m] (f : σ α × σ) (s : σ) : (modifyGet f : StateT σ m α).run s = pure ((f s).1, (f s).2) := by
@[simp, grind =] theorem run_modifyGet [Monad m] (f : σ α × σ) (s : σ) : (modifyGet f : StateT σ m α).run s = pure ((f s).1, (f s).2) := by
simp [modifyGet, MonadStateOf.modifyGet, StateT.modifyGet, run]
@[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
@[simp, grind =] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
@[grind =]
theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp, grind =] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ) :
@[simp, grind =] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ) :
(monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by

View File

@@ -6,12 +6,18 @@ Authors: Quang Dao, Paul Reichert
module
prelude
public import all Init.Control.Option
public import all Init.Control.Except
public import all Init.Control.ExceptCps
public import all Init.Control.StateRef
public import all Init.Control.StateCps
public import all Init.Control.Id
public import Init.Control.Option
import all Init.Control.Option
public import Init.Control.Except
import all Init.Control.Except
public import Init.Control.ExceptCps
import all Init.Control.ExceptCps
public import Init.Control.StateRef
import all Init.Control.StateRef
public import Init.Control.StateCps
import all Init.Control.StateCps
public import Init.Control.Id
import all Init.Control.Id
public import Init.Control.Lawful.MonadLift.Lemmas
public import Init.Control.Lawful.Instances

View File

@@ -290,13 +290,17 @@ macro "right" : conv => `(conv| rhs)
/-- `intro` traverses into binders. Synonym for `ext`. -/
macro "intro" xs:(ppSpace colGt binderIdent)* : conv => `(conv| ext $xs*)
syntax enterArg := binderIdent <|> argArg
syntax enterPattern := "in " (occs)? term
syntax enterArg := binderIdent <|> argArg <|> enterPattern
/-- `enter [arg, ...]` is a compact way to describe a path to a subterm.
It is a shorthand for other conv tactics as follows:
* `enter [i]` is equivalent to `arg i`.
* `enter [@i]` is equivalent to `arg @i`.
* `enter [x]` (where `x` is an identifier) is equivalent to `ext x`.
* `enter [in e]` (where `e` is a term) is equivalent to `pattern e`.
Occurrences can be specified with `enter [in (occs := ...) e]`.
For example, given the target `f (g a (fun x => x b))`, `enter [1, 2, x, 1]`
will traverse to the subterm `b`. -/
syntax (name := enter) "enter" " [" withoutPosition(enterArg,+) "]" : conv

View File

@@ -1580,6 +1580,7 @@ instance {p q : Prop} [d : Decidable (p ↔ q)] : Decidable (p = q) :=
gen_injective_theorems% Array
gen_injective_theorems% BitVec
gen_injective_theorems% ByteArray
gen_injective_theorems% Char
gen_injective_theorems% DoResultBC
gen_injective_theorems% DoResultPR
@@ -2546,7 +2547,3 @@ class Irrefl (r : αα → Prop) : Prop where
irrefl : a, ¬r a a
end Std
/-- Deprecated alias for `XorOp`. -/
@[deprecated XorOp (since := "2025-07-30")]
abbrev Xor := XorOp

View File

@@ -51,5 +51,6 @@ public import Init.Data.Range.Polymorphic
public import Init.Data.Slice
public import Init.Data.Order
public import Init.Data.Rat
public import Init.Data.Dyadic
public section

View File

@@ -9,7 +9,8 @@ prelude
public import Init.Data.Array.Mem
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Count
public import all Init.Data.List.Attach
public import Init.Data.List.Attach
import all Init.Data.List.Attach
public section
@@ -120,7 +121,7 @@ theorem pmap_eq_map {p : α → Prop} {f : α → β} {xs : Array α} (H) :
theorem pmap_congr_left {p q : α Prop} {f : a, p a β} {g : a, q a β} (xs : Array α) {H₁ H₂}
(h : a xs, (h₁ h₂), f a h₁ = g a h₂) : pmap f xs H₁ = pmap g xs H₂ := by
cases xs
simp only [mem_toArray] at h
simp only [List.mem_toArray] at h
simp only [List.pmap_toArray, mk.injEq]
rw [List.pmap_congr_left _ h]
@@ -193,14 +194,14 @@ theorem attachWith_map_subtype_val {p : α → Prop} {xs : Array α} (H : ∀ a
(xs.attachWith p H).map Subtype.val = xs := by
cases xs; simp
@[simp, grind]
@[simp, grind ]
theorem mem_attach (xs : Array α) : x, x xs.attach
| a, h => by
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
rcases this with _, _, m, rfl
exact m
@[simp, grind]
@[simp, grind =]
theorem mem_attachWith {xs : Array α} {q : α Prop} (H) (x : {x // q x}) :
x xs.attachWith q H x.1 xs := by
cases xs
@@ -211,12 +212,13 @@ theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {xs H b} :
b pmap f xs H (a : _) (h : a xs), f a (H a h) = b := by
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
@[grind]
theorem mem_pmap_of_mem {p : α Prop} {f : a, p a β} {xs H} {a} (h : a xs) :
f a (H a h) pmap f xs H := by
rw [mem_pmap]
exact a, h, rfl
grind_pattern mem_pmap_of_mem => _ pmap f xs H, a xs
@[simp, grind =]
theorem size_pmap {p : α Prop} {f : a, p a β} {xs H} : (pmap f xs H).size = xs.size := by
cases xs; simp
@@ -344,7 +346,7 @@ theorem foldl_attach {xs : Array α} {f : β → α → β} {b : β} :
xs.attach.foldl (fun acc t => f acc t.1) b = xs.foldl f b := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
List.foldl_toArray', mem_toArray, List.foldl_subtype]
List.foldl_toArray', List.mem_toArray, List.foldl_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
@@ -363,7 +365,7 @@ theorem foldr_attach {xs : Array α} {f : α → β → β} {b : β} :
xs.attach.foldr (fun t acc => f t.1 acc) b = xs.foldr f b := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
List.foldr_toArray', mem_toArray, List.foldr_subtype]
List.foldr_toArray', List.mem_toArray, List.foldr_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
@@ -705,7 +707,7 @@ and simplifies these to the function directly taking the value.
{f : { x // p x } Array β} {g : α Array β} (hf : x h, f x, h = g x) :
(xs.flatMap f) = xs.unattach.flatMap g := by
cases xs
simp only [List.flatMap_toArray, List.unattach_toArray,
simp only [List.flatMap_toArray, List.unattach_toArray,
mk.injEq]
rw [List.flatMap_subtype]
simp [hf]

View File

@@ -10,11 +10,11 @@ public import Init.WFTactics
public import Init.Data.Nat.Basic
public import Init.Data.Fin.Basic
public import Init.Data.UInt.BasicAux
public import Init.Data.Repr
public import Init.Data.ToString.Basic
public import Init.GetElem
public import all Init.Data.List.ToArrayImpl
public import all Init.Data.Array.Set
public import Init.Data.List.ToArrayImpl
import all Init.Data.List.ToArrayImpl
public import Init.Data.Array.Set
import all Init.Data.Array.Set
public section
@@ -40,11 +40,11 @@ namespace Array
/-! ### Preliminary theorems -/
@[simp, grind] theorem size_set {xs : Array α} {i : Nat} {v : α} (h : i < xs.size) :
@[simp, grind =] theorem size_set {xs : Array α} {i : Nat} {v : α} (h : i < xs.size) :
(set xs i v h).size = xs.size :=
List.length_set ..
@[simp, grind] theorem size_push {xs : Array α} (v : α) : (push xs v).size = xs.size + 1 :=
@[simp, grind =] theorem size_push {xs : Array α} (v : α) : (push xs v).size = xs.size + 1 :=
List.length_concat ..
theorem ext {xs ys : Array α}
@@ -108,13 +108,19 @@ instance : Membership α (Array α) where
theorem mem_def {a : α} {as : Array α} : a as a as.toList :=
fun | .mk h => h, Array.Mem.mk
@[simp, grind =] theorem mem_toArray {a : α} {l : List α} : a l.toArray a l := by
@[simp, grind =] theorem _root_.List.mem_toArray {a : α} {l : List α} : a l.toArray a l := by
simp [mem_def]
@[simp, grind] theorem getElem_mem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i] xs := by
@[deprecated List.mem_toArray (since := "2025-09-04")]
theorem mem_toArray {a : α} {l : List α} : a l.toArray a l :=
List.mem_toArray
@[simp] theorem getElem_mem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i] xs := by
rw [Array.mem_def, getElem_toList]
apply List.getElem_mem
grind_pattern getElem_mem => xs[i] xs
@[simp, grind =] theorem emptyWithCapacity_eq {α n} : @emptyWithCapacity α n = #[] := rfl
@[simp] theorem mkEmpty_eq {α n} : @mkEmpty α n = #[] := rfl
@@ -132,7 +138,7 @@ theorem toList_toArray {as : List α} : as.toArray.toList = as := rfl
@[deprecated toList_toArray (since := "2025-02-17")]
abbrev _root_.Array.toList_toArray := @List.toList_toArray
@[simp, grind] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
@[simp, grind =] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
@[deprecated size_toArray (since := "2025-02-17")]
abbrev _root_.Array.size_toArray := @List.size_toArray
@@ -162,7 +168,7 @@ This is a low-level version of `Array.size` that directly queries the runtime sy
representation of arrays. While this is not provable, `Array.usize` always returns the exact size of
the array since the implementation only supports arrays of size less than `USize.size`.
-/
@[extern "lean_array_size", simp]
@[extern "lean_array_size", simp, expose]
def usize (xs : @& Array α) : USize := xs.size.toUSize
/--
@@ -197,7 +203,7 @@ Examples:
def pop (xs : Array α) : Array α where
toList := xs.toList.dropLast
@[simp, grind] theorem size_pop {xs : Array α} : xs.pop.size = xs.size - 1 := by
@[simp, grind =] theorem size_pop {xs : Array α} : xs.pop.size = xs.size - 1 := by
match xs with
| [] => rfl
| a::as => simp [pop, Nat.succ_sub_succ_eq_sub, size]
@@ -441,7 +447,7 @@ def swapAt! (xs : Array α) (i : Nat) (v : α) : α × Array α :=
swapAt xs i v
else
have : Inhabited (α × Array α) := (v, xs)
panic! ("index " ++ toString i ++ " out of bounds")
panic! String.Internal.append (String.Internal.append "index " (toString i)) " out of bounds"
/--
Returns the first `n` elements of an array. The resulting array is produced by repeatedly calling
@@ -2167,7 +2173,7 @@ instance {α : Type u} [Repr α] : Repr (Array α) where
reprPrec xs _ := Array.repr xs
instance [ToString α] : ToString (Array α) where
toString xs := "#" ++ toString xs.toList
toString xs := String.Internal.append "#" (toString xs.toList)
end Array

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Nat.Linear
public import Init.NotationExtra

View File

@@ -8,7 +8,8 @@ module
prelude
public import Init.Data.List.TakeDrop
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public section

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Count

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.BEq
public import Init.Data.List.Nat.BEq
public import Init.ByCases

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Erase
public import Init.Data.List.Nat.Basic
@@ -90,7 +91,7 @@ theorem mem_of_mem_eraseP {xs : Array α} : a ∈ xs.eraseP p → a ∈ xs := by
rcases xs with xs
simpa using List.mem_of_mem_eraseP
@[simp, grind] theorem mem_eraseP_of_neg {xs : Array α} (pa : ¬p a) : a xs.eraseP p a xs := by
@[simp, grind =] theorem mem_eraseP_of_neg {xs : Array α} (pa : ¬p a) : a xs.eraseP p a xs := by
rcases xs with xs
simpa using List.mem_eraseP_of_neg pa
@@ -239,7 +240,7 @@ theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a ∈ xs.erase b) : a
rcases xs with xs
simpa using List.mem_of_mem_erase (by simpa using h)
@[simp, grind] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {xs : Array α} (ab : a b) :
@[simp, grind =] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {xs : Array α} (ab : a b) :
a xs.erase b a xs :=
erase_eq_eraseP b xs mem_eraseP_of_neg (mt eq_of_beq ab.symm)
@@ -270,7 +271,7 @@ theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
(xs ++ ys).erase a = if a xs then xs.erase a ++ ys else xs ++ ys.erase a := by
rcases xs with xs
rcases ys with ys
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
simp only [List.append_toArray, List.erase_toArray, List.erase_append, List.mem_toArray]
split <;> simp
@[grind =]

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.List.Nat.Find
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.Array.Range
@@ -26,11 +27,11 @@ open Nat
/-! ### findSome? -/
@[simp, grind] theorem findSome?_empty : (#[] : Array α).findSome? f = none := rfl
@[simp, grind] theorem findSome?_push {xs : Array α} : (xs.push a).findSome? f = (xs.findSome? f).or (f a) := by
@[simp, grind =] theorem findSome?_empty : (#[] : Array α).findSome? f = none := rfl
@[simp, grind =] theorem findSome?_push {xs : Array α} : (xs.push a).findSome? f = (xs.findSome? f).or (f a) := by
cases xs; simp [List.findSome?_append]
@[grind]
@[grind =]
theorem findSome?_singleton {a : α} {f : α Option β} : #[a].findSome? f = f a := by
simp
@@ -227,11 +228,12 @@ theorem mem_of_find?_eq_some {xs : Array α} (h : find? p xs = some a) : a ∈ x
simp at h
simpa using List.mem_of_find?_eq_some h
@[grind]
theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h xs := by
cases xs
simp [List.get_find?_mem]
grind_pattern get_find?_mem => (xs.find? p).get h
@[simp, grind =] theorem find?_filter {xs : Array α} (p q : α Bool) :
(xs.filter p).find? q = xs.find? (fun a => p a q a) := by
cases xs; simp
@@ -394,7 +396,6 @@ theorem findIdx_singleton {a : α} {p : α → Bool} :
#[a].findIdx p = if p a then 0 else 1 := by
simp
@[grind ]
theorem findIdx_of_getElem?_eq_some {xs : Array α} (w : xs[xs.findIdx p]? = some y) : p y := by
rcases xs with xs
exact List.findIdx_of_getElem?_eq_some (by simpa using w)
@@ -727,7 +728,7 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
cases xs
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
rw [findFinIdx?_congr List.unattach_toArray]
simp only [Option.map_map, Function.comp_def, Fin.cast_trans]
simp only [Option.map_map, Function.comp_def, Fin.cast_cast]
simp [Array.size]
/-! ### idxOf

File diff suppressed because it is too large Load Diff

View File

@@ -70,8 +70,8 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
rw [cons_lex_cons.forIn'_congr_aux Std.PRange.toList_eq_match rfl (fun _ _ _ => rfl)]
simp only [Std.PRange.SupportsUpperBound.IsSatisfied, bind_pure_comp, map_pure]
rw [cons_lex_cons.forIn'_congr_aux (if_pos (by omega)) rfl (fun _ _ _ => rfl)]
simp only [Std.PRange.toList_open_eq_toList_closed_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.ClosedOpen.toList_succ_succ,
simp only [Std.PRange.toList_Rox_eq_toList_Rcx_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_Rco_succ_succ,
Option.get_some, List.forIn'_cons, List.size_toArray, List.length_cons, List.length_nil,
Nat.lt_add_one, getElem_append_left, List.getElem_toArray, List.getElem_cons_zero]
cases lt a b

View File

@@ -6,11 +6,13 @@ Authors: Mario Carneiro, Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.Array.OfFn
public import all Init.Data.List.MapIdx
public import Init.Data.List.MapIdx
import all Init.Data.List.MapIdx
public section

View File

@@ -6,8 +6,10 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.List.Control
public import all Init.Data.Array.Basic
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.List.Monadic
@@ -165,7 +167,7 @@ theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β
(h : a m b, f a (by simpa [w] using m) b = g a m b) :
forIn' as b f = forIn' bs b' g := by
cases as <;> cases bs
simp only [mk.injEq, mem_toArray, List.forIn'_toArray] at w h
simp only [mk.injEq, List.mem_toArray, List.forIn'_toArray] at w h
exact List.forIn'_congr w hb h
/--

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Monadic
public import Init.Data.List.OfFn

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.List.Nat.Perm
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public section

View File

@@ -7,8 +7,10 @@ module
prelude
public import Init.Data.Array.Lemmas
public import all Init.Data.Array.Basic
public import all Init.Data.Array.OfFn
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.OfFn
import all Init.Data.Array.OfFn
public import Init.Data.Array.MapIdx
public import Init.Data.Array.Zip
public import Init.Data.List.Nat.Range
@@ -114,7 +116,7 @@ theorem range'_eq_append_iff : range' s n = xs ++ ys ↔ ∃ k, k ≤ n ∧ xs =
@[simp] theorem find?_range'_eq_some {s n : Nat} {i : Nat} {p : Nat Bool} :
(range' s n).find? p = some i p i i range' s n j, s j j < i !p j := by
rw [ List.toArray_range']
simp only [List.find?_toArray, mem_toArray]
simp only [List.find?_toArray, List.mem_toArray]
simp [List.find?_range'_eq_some]
@[simp] theorem find?_range'_eq_none {s n : Nat} {p : Nat Bool} :

View File

@@ -8,7 +8,8 @@ module
prelude
public import Init.Data.Array.Basic
public import all Init.Data.Array.Subarray
public import Init.Data.Array.Subarray
import all Init.Data.Array.Subarray
public import Init.Omega
public section

View File

@@ -6,7 +6,8 @@ Authors: Markus Himmel
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.TakeDrop

View File

@@ -6,7 +6,8 @@ Authors: Kim Morrison
module
prelude
public import all Init.Data.Array.Basic
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.TakeDrop
public import Init.Data.List.Zip
@@ -230,11 +231,9 @@ theorem zip_map {f : αγ} {g : β → δ} {as : Array α} {bs : Array β}
cases bs
simp [List.zip_map]
@[grind _=_]
theorem zip_map_left {f : α γ} {as : Array α} {bs : Array β} :
zip (as.map f) bs = (zip as bs).map (Prod.map f id) := by rw [ zip_map, map_id]
@[grind _=_]
theorem zip_map_right {f : β γ} {as : Array α} {bs : Array β} :
zip as (bs.map f) = (zip as bs).map (Prod.map id f) := by rw [ zip_map, map_id]

View File

@@ -9,7 +9,7 @@ prelude
public import Init.Data.Fin.Basic
public import Init.Data.Nat.Bitwise.Lemmas
public import Init.Data.Nat.Power2
public import Init.Data.Int.Bitwise
public import Init.Data.Int.Bitwise.Basic
public import Init.Data.BitVec.BasicAux
@[expose] public section
@@ -206,10 +206,13 @@ Converts a bitvector into a fixed-width hexadecimal number with enough digits to
If `n` is `0`, then one digit is returned. Otherwise, `⌊(n + 3) / 4⌋` digits are returned.
-/
-- If we ever want to prove something about this, we can avoid having to use the opaque
-- `Internal` string functions by moving this definition out to a separate file that can live
-- downstream of `Init.Data.String.Basic`.
protected def toHex {n : Nat} (x : BitVec n) : String :=
let s := (Nat.toDigits 16 x.toNat).asString
let t := (List.replicate ((n+3) / 4 - s.length) '0').asString
t ++ s
let t := (List.replicate ((n+3) / 4 - String.Internal.length s) '0').asString
String.Internal.append t s
/-- `BitVec` representation. -/
protected def BitVec.repr (a : BitVec n) : Std.Format :=
@@ -871,4 +874,7 @@ def clzAuxRec {w : Nat} (x : BitVec w) (n : Nat) : BitVec w :=
/-- Count the number of leading zeros. -/
def clz (x : BitVec w) : BitVec w := clzAuxRec x (w - 1)
/-- Count the number of trailing zeros. -/
def ctz (x : BitVec w) : BitVec w := (x.reverse).clz
end BitVec

View File

@@ -21,13 +21,6 @@ namespace BitVec
section Nat
/--
The bitvector with value `i mod 2^n`.
-/
@[expose, match_pattern]
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat (2^n) i
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
/-- Return the bound in terms of toNat. -/

View File

@@ -6,11 +6,14 @@ Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix, Siddharth Bhat
module
prelude
public import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Bitwise.Basic
import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Mod
public import all Init.Data.Int.DivMod
public import Init.Data.Int.DivMod
import all Init.Data.Int.DivMod
public import Init.Data.Int.LemmasAux
public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Decidable
public import Init.Data.BitVec.Lemmas
public import Init.Data.BitVec.Folds
@@ -2152,4 +2155,238 @@ theorem shiftLeft_add_eq_shiftLeft_or {x y : BitVec w} :
(y <<< x) + x = (y <<< x) ||| x := by
rw [BitVec.add_comm, add_shiftLeft_eq_or_shiftLeft, or_comm]
/- ### Fast Circuit For Unsigned Overflow Detection -/
/-!
# Note [Fast Unsigned Multiplication Overflow Detection]
The fast unsigned multiplication overflow detection circuit is described in
`Efficient integer multiplication overflow detection circuits` (https://ieeexplore.ieee.org/abstract/document/987767).
With this circuit, the computation of the overflow flag for the unsigned multiplication of
two bitvectors `x` and `y` with bitwidth `w` requires:
· extending the operands by `1` bit and performing the multiplication with the extended operands,
· computing the preliminary overflow flag, which describes whether `x` and `y` together have at most
`w - 2` leading zeros.
If the most significant bit of the extended operands' multiplication is `true` or if the
preliminary overflow flag is `true`, overflow happens.
In particular, the conditions check two different cases:
· if the most significant bit of the extended operands' multiplication is `true`, the result of the
multiplication 2 ^ w ≤ x.toNat * y.toNat < 2 ^ (w + 1),
· if the preliminary flag is true, then 2 ^ (w + 1) ≤ x.toNat * y.toNat.
The computation of the preliminary overflow flag `resRec` relies on two quantities:
· `uppcRec`: the unsigned parallel prefix circuit for the bits until a certain `i`,
· `aandRec`: the conjunction between the parallel prefix circuit at of the first operand until a certain `i`
and the `i`-th bit in the second operand.
-/
/--
`uppcRec` is the unsigned parallel prefix, `x.uppcRec s = true` iff `x.toNat` is greater or equal
than `2 ^ (w - 1 - (s - 1))`.
-/
def uppcRec {w} (x : BitVec w) (s : Nat) (hs : s < w) : Bool :=
match s with
| 0 => x.msb
| i + 1 => x[w - 1 - i] || uppcRec x i (by omega)
/-- The unsigned parallel prefix of `x` at `s` is `true` if and only if x interpreted
as a natural number is greater or equal than `2 ^ (w - 1 - (s - 1))`. -/
@[simp]
theorem uppcRec_true_iff (x : BitVec w) (s : Nat) (h : s < w) :
uppcRec x s h 2 ^ (w - 1 - (s - 1)) x.toNat := by
rcases w with _|w
· omega
· induction s
· case succ.zero =>
simp only [uppcRec, msb_eq_true_iff_two_mul_ge, Nat.pow_add, Nat.pow_one,
Nat.mul_comm (2 ^ w) 2, ge_iff_le, Nat.add_one_sub_one, zero_le, Nat.sub_eq_zero_of_le,
Nat.sub_zero]
apply Nat.mul_le_mul_left_iff (by omega)
· case succ.succ s ihs =>
simp only [uppcRec, or_eq_true, ihs, Nat.add_one_sub_one]
have := Nat.pow_le_pow_of_le (a := 2) ( n := (w - s)) (m := (w - (s - 1))) (by omega) (by omega)
constructor
· intro h'
rcases h' with h'|h'
· apply ge_two_pow_of_testBit h'
· omega
· intro h'
by_cases hbit: x[w - s]
· simp [hbit]
· have := BitVec.le_toNat_iff_getLsbD_eq_true (x := x) (i := w - s) (by omega)
simp only [h', true_iff] at this
obtain k, hk := this
by_cases hwk : w - s + k < w + 1
· by_cases hk' : 0 < k
· have hle := ge_two_pow_of_testBit hk
have hpowle := Nat.pow_le_pow_of_le (a := 2) ( n := (w - (s - 1))) (m := (w - s + k)) (by omega) (by omega)
omega
· rw [getLsbD_eq_getElem (by omega)] at hk
simp [hbit, show k = 0 by omega] at hk
· simp_all
/--
Conjunction for fast umulOverflow circuit
-/
def aandRec (x y : BitVec w) (s : Nat) (hs : s < w) : Bool :=
y[s] && uppcRec x s (by omega)
/--
Preliminary overflow flag for fast umulOverflow circuit as introduced in
`Efficient integer multiplication overflow detection circuits` (https://ieeexplore.ieee.org/abstract/document/987767).
-/
def resRec (x y : BitVec w) (s : Nat) (hs : s < w) (hslt : 0 < s) : Bool :=
match hs0 : s with
| 0 => by omega
| s' + 1 =>
match hs' : s' with
| 0 => aandRec x y 1 (by omega)
| s'' + 1 =>
(resRec x y s' (by omega) (by omega)) || (aandRec x y s (by omega))
/-- The preliminary overflow flag is true for a certain `s` if and only if the conjunction returns true at
any `k` smaller than or equal to `s`. -/
theorem resRec_true_iff (x y : BitVec w) (s : Nat) (hs : s < w) (hs' : 0 < s) :
resRec x y s hs hs' = true (k : Nat), (h : k s), (_ : 0 < k), aandRec x y k (by omega) := by
unfold resRec
rcases s with _|s
· omega
· rcases s
· case zero =>
constructor
· intro ha
exists 1, by omega, by omega
· intro hr
obtain k, hk, hk', hk'' := hr
simp only [show k = 1 by omega] at hk''
exact hk''
· case succ s =>
induction s
· case zero =>
unfold resRec
simp only [Nat.zero_add, Nat.reduceAdd, or_eq_true]
constructor
· intro h
rcases h with h|h
· exists 1, by omega, by omega
· exists 2, by omega, by omega
· intro h
obtain k, hk, hk', hk'' := h
have h : k = 1 k = 2 := by omega
rcases h with h|h
<;> simp only [h] at hk''
<;> simp [hk'']
· case succ s ihs =>
specialize ihs (by omega) (by omega)
unfold resRec
simp only [or_eq_true, ihs]
constructor
· intro h
rcases h with h|h
· obtain k, hk, hk', hk'' := h
exists k, by omega, by omega
· exists s + 1 + 1 + 1, by omega, by omega
· intro h
obtain k, hk, hk', hk'' := h
by_cases h' : x.aandRec y (s + 1 + 1 + 1) (by omega) = true
· simp [h']
· simp only [h', false_eq_true, _root_.or_false]
by_cases h'' : k s + 1 + 1
· exists k, h'', by omega
· have : k = s + 1 + 1 + 1 := by omega
simp_all
/-- If the sum of the leading zeroes of two bitvecs with bitwidth `w` is less than or equal to
(`w - 2`), then the preliminary overflow flag is true and their unsigned multiplication overflows.
The explanation is in `Efficient integer multiplication overflow detection circuits`
https://ieeexplore.ieee.org/abstract/document/987767
-/
theorem resRec_of_clz_le {x y : BitVec w} (hw : 1 < w) (hx : x 0#w) (hy : y 0#w):
(clz x).toNat + (clz y).toNat w - 2 resRec x y (w - 1) (by omega) (by omega) := by
intro h
rw [resRec_true_iff]
exists (w - 1 - y.clz.toNat), by omega, by omega
simp only [aandRec]
by_cases hw0 : w - 1 - y.clz.toNat = 0
· have := clz_lt_iff_ne_zero.mpr (by omega)
omega
· simp only [and_eq_true, getLsbD_true_clz_of_ne_zero (x := y) (by omega) (by omega),
getElem_of_getLsbD_eq_true, uppcRec_true_iff,
show w - 1 - (w - 1 - y.clz.toNat - 1) = y.clz.toNat + 1 by omega, _root_.true_and]
exact Nat.le_trans (Nat.pow_le_pow_of_le (a := 2) (n := y.clz.toNat + 1)
(m := w - 1 - x.clz.toNat) (by omega) (by omega))
(BitVec.two_pow_sub_clz_le_toNat_of_ne_zero (x := x) (by omega) (by omega))
/--
Complete fast overflow detection circuit for unsigned multiplication.
-/
theorem fastUmulOverflow (x y : BitVec w) :
umulOverflow x y = if hw : w 1 then false
else (setWidth (w + 1) x * setWidth (w + 1) y)[w] || x.resRec y (w - 1) (by omega) (by omega) := by
rcases w with _|_|w
· simp [of_length_zero, umulOverflow]
· have hx : x.toNat 1 := by omega
have hy : y.toNat 1 := by omega
have := Nat.mul_le_mul (n₁ := x.toNat) (m₁ := y.toNat) (n₂ := 1) (m₂ := 1) hx hy
simp [umulOverflow]
omega
· by_cases h : umulOverflow x y
· simp only [h, Nat.reduceLeDiff, reduceDIte, Nat.add_one_sub_one, true_eq, or_eq_true]
simp only [umulOverflow, ge_iff_le, decide_eq_true_eq] at h
by_cases h' : x.toNat * y.toNat < 2 ^ (w + 1 + 1 + 1)
· have hlt := BitVec.getElem_eq_true_of_lt_of_le
(x := (setWidth (w + 1 + 1 + 1) x * setWidth (w + 1 + 1 + 1) y))
(k := w + 1 + 1) (by omega)
simp only [toNat_mul, toNat_setWidth, Nat.lt_add_one, toNat_mod_cancel_of_lt,
Nat.mod_eq_of_lt (a := x.toNat * y.toNat) (b := 2 ^ (w + 1 + 1 + 1)) (by omega), h', h,
forall_const] at hlt
simp [hlt]
· by_cases hsw : (setWidth (w + 1 + 1 + 1) x * setWidth (w + 1 + 1 + 1) y)[w + 1 + 1] = true
· simp [hsw]
· simp only [hsw, false_eq_true, _root_.false_or]
have := Nat.two_pow_pos (w := w + 1 + 1)
have hltx := BitVec.toNat_lt_two_pow_sub_clz (x := x)
have hlty := BitVec.toNat_lt_two_pow_sub_clz (x := y)
have := Nat.mul_ne_zero_iff (m := y.toNat) (n := x.toNat)
simp only [ne_eq, show ¬x.toNat * y.toNat = 0 by omega, not_false_eq_true,
true_iff] at this
obtain hxz,hyz := this
apply resRec_of_clz_le (x := x) (y := y) (by omega) (by simp [toNat_eq]; exact hxz) (by simp [toNat_eq]; exact hyz)
by_cases hzxy : x.clz.toNat + y.clz.toNat w
· omega
· by_cases heq : w + 1 - y.clz.toNat = 0
· by_cases heq' : w + 1 + 1 - y.clz.toNat = 0
· simp [heq', hyz] at hlty
· simp only [show y.clz.toNat = w + 1 by omega, Nat.add_sub_cancel_left,
Nat.pow_one] at hlty
simp only [show y.toNat = 1 by omega, Nat.mul_one, Nat.not_lt] at h'
omega
· by_cases w + 1 < y.clz.toNat
· omega
· simp only [Nat.not_lt] at h'
have := Nat.mul_lt_mul'' (a := x.toNat) (b := y.toNat) (c := 2 ^ (w + 1 + 1 - x.clz.toNat)) (d := 2 ^ (w + 1 + 1 - y.clz.toNat)) hltx hlty
simp only [ Nat.pow_add] at this
have := Nat.pow_le_pow_of_le (a := 2) (n := w + 1 + 1 - x.clz.toNat + (w + 1 + 1 - y.clz.toNat)) (m := w + 1 + 1 + 1)
(by omega) (by omega)
omega
· simp only [h, Nat.reduceLeDiff, reduceDIte, Nat.add_one_sub_one, false_eq, or_eq_false_iff]
simp only [umulOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h
and_intros
· simp only [ getLsbD_eq_getElem, getLsbD_eq_getMsbD, Nat.lt_add_one, decide_true,
Nat.add_one_sub_one, Nat.sub_self, msb_eq_getMsbD_zero, Bool.true_and,
msb_eq_false_iff_two_mul_lt, toNat_mul, toNat_setWidth, toNat_mod_cancel_of_lt]
rw [Nat.mod_eq_of_lt (by omega),Nat.pow_add (m := w + 1 + 1) (n := 1)]
simp [Nat.mul_comm 2 (x.toNat * y.toNat), h]
· apply Classical.byContradiction
intro hcontra
simp only [not_eq_false, resRec_true_iff, exists_prop, exists_and_left] at hcontra
obtain k,hk,hk',hk'' := hcontra
simp only [aandRec, and_eq_true, uppcRec_true_iff, Nat.add_one_sub_one] at hk''
obtain hky, hkx := hk''
have hyle := two_pow_le_toNat_of_getElem_eq_true (x := y) (i := k) (by omega) hky
have := Nat.mul_le_mul (n₁ := 2 ^ (w + 1 - (k - 1))) (m₁ := 2 ^ k) (n₂ := x.toNat) (m₂ := y.toNat) hkx hyle
simp [ Nat.pow_add, show w + 1 - (k - 1) + k = w + 1 + 1 by omega] at this
omega
end BitVec

View File

@@ -6,7 +6,9 @@ Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth B
module
prelude
public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
import Init.Data.Int.Bitwise.Lemmas
public section
@@ -17,7 +19,7 @@ theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsbD i := rfl
@[simp, grind =] theorem getLsbD_ofFin (x : Fin (2^n)) (i : Nat) :
getLsbD (BitVec.ofFin x) i = x.val.testBit i := rfl
@[simp, grind] theorem getLsbD_of_ge (x : BitVec w) (i : Nat) (ge : w i) : getLsbD x i = false := by
@[simp, grind =] theorem getLsbD_of_ge (x : BitVec w) (i : Nat) (ge : w i) : getLsbD x i = false := by
let x, x_lt := x
simp only [getLsbD_ofFin]
apply Nat.testBit_lt_two_pow

View File

@@ -6,7 +6,8 @@ Authors: Joe Hendrix, Harun Khan
module
prelude
public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Lemmas
public import Init.Data.Nat.Lemmas
public import Init.Data.Fin.Iterate

View File

@@ -7,8 +7,10 @@ module
prelude
public import Init.Data.Bool
public import all Init.Data.BitVec.Basic
public import all Init.Data.BitVec.BasicAux
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.BasicAux
import all Init.Data.BitVec.BasicAux
public import Init.Data.Fin.Lemmas
public import Init.Data.Nat.Lemmas
public import Init.Data.Nat.Div.Lemmas
@@ -35,7 +37,7 @@ namespace BitVec
@[simp] theorem getElem_ofFin (x : Fin (2^n)) (i : Nat) (h : i < n) :
(BitVec.ofFin x)[i] = x.val.testBit i := rfl
@[simp, grind] theorem getMsbD_of_ge (x : BitVec w) (i : Nat) (ge : w i) : getMsbD x i = false := by
@[simp, grind =] theorem getMsbD_of_ge (x : BitVec w) (i : Nat) (ge : w i) : getMsbD x i = false := by
rw [getMsbD]
simp only [Bool.and_eq_false_imp, decide_eq_true_eq]
omega
@@ -120,7 +122,7 @@ theorem getElem_of_getLsbD_eq_true {x : BitVec w} {i : Nat} (h : x.getLsbD i = t
This normalized a bitvec using `ofFin` to `ofNat`.
-/
theorem ofFin_eq_ofNat : @BitVec.ofFin w (Fin.mk x lt) = BitVec.ofNat w x := by
simp only [BitVec.ofNat, Fin.ofNat, lt, Nat.mod_eq_of_lt]
simp only [BitVec.ofNat, Fin.Internal.ofNat_eq_ofNat, Fin.ofNat, lt, Nat.mod_eq_of_lt]
/-- Prove nonequality of bitvectors in terms of nat operations. -/
theorem toNat_ne_iff_ne {n} {x y : BitVec n} : x.toNat y.toNat x y := by
@@ -297,7 +299,7 @@ theorem length_pos_of_ne {x y : BitVec w} (h : x ≠ y) : 0 < w :=
theorem ofFin_ofNat (n : Nat) :
ofFin (no_index (OfNat.ofNat n : Fin (2^w))) = OfNat.ofNat n := by
simp only [OfNat.ofNat, Fin.ofNat, BitVec.ofNat]
simp only [OfNat.ofNat, Fin.Internal.ofNat_eq_ofNat, Fin.ofNat, BitVec.ofNat]
-- We use a `grind_pattern` as `@[grind]` will not use the `no_index` term.
grind_pattern ofFin_ofNat => ofFin (OfNat.ofNat n : Fin (2^w))
@@ -508,6 +510,18 @@ theorem getElem_ofBool {b : Bool} {h : i < 1}: (ofBool b)[i] = b := by
@[simp] theorem zero_eq_one_iff (w : Nat) : (0#w = 1#w) (w = 0) := by
rw [ one_eq_zero_iff, eq_comm]
/-- A bitvector is equal to 0#w if and only if all bits are `false` -/
theorem zero_iff_eq_false {x: BitVec w} :
x = 0#w i, x.getLsbD i = false := by
rcases w with _|w
· simp [of_length_zero]
· constructor
· intro hzero
simp [hzero]
· intro hfalse
ext j hj
simp [ getLsbD_eq_getElem, hfalse]
/-! ### msb -/
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsbD]
@@ -1086,6 +1100,10 @@ theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.coe_castLE, val_toFin,
Nat.mod_eq_of_lt (by apply BitVec.toNat_lt_twoPow_of_le p)]
theorem toNat_setWidth_of_le {w w' : Nat} {b : BitVec w} (h : w w') : (b.setWidth w').toNat = b.toNat := by
rw [BitVec.toNat_setWidth, Nat.mod_eq_of_lt]
exact BitVec.toNat_lt_twoPow_of_le h
/-! ## extractLsb -/
@[simp, grind =]
@@ -1273,6 +1291,17 @@ theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
ext i hi
omega
theorem extractLsb'_setWidth_of_le {b : BitVec w} {start len w' : Nat} (h : start + len w') :
(b.setWidth w').extractLsb' start len = b.extractLsb' start len := by
ext i h_i
simp
omega
theorem setWidth_extractLsb'_of_le {c : BitVec w} (h : len₁ len₂) :
(c.extractLsb' start len₂).setWidth len₁ = c.extractLsb' start len₁ := by
ext i hi
simp [show i < len₂ by omega]
/-! ### allOnes -/
@[simp, grind =] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
@@ -1516,6 +1545,12 @@ theorem extractLsb_and {x : BitVec w} {hi lo : Nat} :
@[simp, grind =] theorem ofNat_and {x y : Nat} : BitVec.ofNat w (x &&& y) = BitVec.ofNat w x &&& BitVec.ofNat w y :=
eq_of_toNat_eq (by simp [Nat.and_mod_two_pow])
theorem and_or_distrib_left {x y z : BitVec w} : x &&& (y ||| z) = (x &&& y) ||| (x &&& z) :=
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_left])
theorem and_or_distrib_right {x y z : BitVec w} : (x ||| y) &&& z = (x &&& z) ||| (y &&& z) :=
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_right])
/-! ### xor -/
@[simp, grind =] theorem toNat_xor (x y : BitVec v) :
@@ -2166,6 +2201,10 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
have := lt_of_getLsbD ha
omega
theorem setWidth_ushiftRight_eq_extractLsb {b : BitVec w} : (b >>> w').setWidth w'' = b.extractLsb' w' w'' := by
ext i hi
simp
/-! ### ushiftRight reductions from BitVec to Nat -/
@[simp, grind =]
@@ -2192,8 +2231,7 @@ theorem sshiftRight_eq_of_msb_false {x : BitVec w} {s : Nat} (h : x.msb = false)
apply BitVec.eq_of_toNat_eq
rw [BitVec.sshiftRight_eq, BitVec.toInt_eq_toNat_cond]
have hxbound : 2 * x.toNat < 2 ^ w := BitVec.msb_eq_false_iff_two_mul_lt.mp h
simp only [hxbound, reduceIte, Int.natCast_shiftRight, ofInt_natCast,
toNat_ofNat, toNat_ushiftRight]
simp only [hxbound, reduceIte, toNat_ushiftRight]
replace hxbound : x.toNat >>> s < 2 ^ w := by
rw [Nat.shiftRight_eq_div_pow]
exact Nat.lt_of_le_of_lt (Nat.div_le_self ..) x.isLt
@@ -2957,10 +2995,9 @@ theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
(x.extractLsb' start₁ (len + len)).cast (by omega) := by
x.extractLsb' start₁ (len + len) := by
ext i h
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
Nat.not_lt]
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, ite_eq_left_iff, Nat.not_lt]
intro hi
congr 1
omega
@@ -3072,6 +3109,51 @@ theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
simp [extractLsb'_append_eq_ite, show ¬ start < w by omega]
theorem extractLsb'_append_eq_left {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' w' w = a := by
simp [BitVec.extractLsb'_append_eq_of_le]
theorem extractLsb'_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' 0 w' = b := by
simp [BitVec.extractLsb'_append_eq_of_add_le]
theorem setWidth_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).setWidth w' = b := by
ext i hi
simp [getLsbD_append, hi]
theorem append_left_inj {s₁ s₂ : BitVec w} (t : BitVec w') : s₁ ++ t = s₂ ++ t s₁ = s₂ := by
refine fun h => ?_, fun h => h rfl
ext i hi
simpa [getElem_append, dif_neg] using congrArg (·[i + w']'(by omega)) h
theorem append_right_inj (s : BitVec w) {t₁ t₂ : BitVec w'} : s ++ t₁ = s ++ t₂ t₁ = t₂ := by
refine fun h => ?_, fun h => h rfl
ext i hi
simpa [getElem_append, hi] using congrArg (·[i]) h
theorem setWidth_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} :
(b ++ b').setWidth w'' = (b.setWidth w'' <<< w') ||| b'.setWidth w'' := by
ext i hi
simp only [getElem_setWidth, getElem_or, getElem_shiftLeft]
rw [getLsbD_append]
split <;> simp_all
theorem setWidth_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} :
(b ++ b' ++ b'').setWidth w''' = (b.setWidth w''' <<< (w' + w'')) ||| (b'.setWidth w''' <<< w'') ||| b''.setWidth w''' := by
rw [BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
theorem setWidth_append_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} {b''' : BitVec w'''} :
(b ++ b' ++ b'' ++ b''').setWidth w'''' = (b.setWidth w'''' <<< (w' + w'' + w''')) ||| (b'.setWidth w'''' <<< (w'' + w''')) |||
(b''.setWidth w'''' <<< w''') ||| b'''.setWidth w'''' := by
simp only [BitVec.setWidth_append_eq_shiftLeft_setWidth_or, BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
theorem and_setWidth_allOnes (w' w : Nat) (b : BitVec (w' + w)) :
b &&& (BitVec.allOnes w).setWidth (w' + w) = 0#w' ++ b.setWidth w := by
ext i hi
simp only [getElem_and, getElem_setWidth, getLsbD_allOnes]
rw [BitVec.getElem_append]
split <;> simp_all
/-! ### rev -/
@[grind =]
@@ -4028,6 +4110,9 @@ instance instLawfulOrderLT : LawfulOrderLT (BitVec n) := by
apply LawfulOrderLT.of_le
simpa using fun _ _ => BitVec.lt_asymm
theorem length_pos_of_lt {b b' : BitVec w} (h : b < b') : 0 < w :=
length_pos_of_ne (BitVec.ne_of_lt h)
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y x % y < y := by
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
apply Nat.mod_lt
@@ -4099,6 +4184,14 @@ theorem lt_of_msb_false_of_msb_true {x y : BitVec w} (hx : x.msb = false) (hy :
simp
omega
theorem lt_add_one {b : BitVec w} (h : b allOnes w) : b < b + 1 := by
simp only [ne_eq, toNat_inj, toNat_allOnes] at h
simp only [BitVec.lt_def, ofNat_eq_ofNat, toNat_add, toNat_ofNat, Nat.add_mod_mod]
rw [Nat.mod_eq_of_lt]
· exact Nat.lt_add_one _
· have := b.toNat_lt_twoPow_of_le (Nat.le_refl _)
omega
/-! ### udiv -/
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
@@ -5244,7 +5337,7 @@ theorem replicate_succ' {x : BitVec w} :
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
simp [replicate_append_self]
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
theorem setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
apply BitVec.eq_of_toNat_eq
rw [toNat_setWidth]
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
@@ -5253,6 +5346,14 @@ theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y)
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
theorem setWidth_setWidth_eq_self {a : BitVec w} {w' : Nat} (h : a < BitVec.twoPow w w') : (a.setWidth w').setWidth w = a := by
by_cases hw : w' < w
· simp only [toNat_eq, toNat_setWidth]
rw [Nat.mod_mod_of_dvd' (Nat.pow_dvd_pow _ (Nat.le_of_lt hw)), Nat.mod_eq_of_lt]
rwa [BitVec.lt_def, BitVec.toNat_twoPow_of_lt hw] at h
· rw [BitVec.lt_def, BitVec.toNat_twoPow_of_le (by omega)] at h
simp at h
/-! ### intMin -/
@[grind =]
@@ -5766,39 +5867,24 @@ theorem msb_replicate {n w : Nat} {x : BitVec w} :
simp only [BitVec.msb, getMsbD_replicate, Nat.zero_mod]
cases n <;> cases w <;> simp
/-! ### Count leading zeros -/
theorem clzAuxRec_zero (x : BitVec w) :
x.clzAuxRec 0 = if x.getLsbD 0 then BitVec.ofNat w (w - 1) else BitVec.ofNat w w := by rfl
theorem clzAuxRec_succ (x : BitVec w) :
x.clzAuxRec (n + 1) = if x.getLsbD (n + 1) then BitVec.ofNat w (w - 1 - (n + 1)) else BitVec.clzAuxRec x n := by rfl
theorem clzAuxRec_eq_clzAuxRec_of_le (x : BitVec w) (h : w - 1 n) :
x.clzAuxRec n = x.clzAuxRec (w - 1) := by
let k := n - (w - 1)
rw [show n = (w - 1) + k by omega]
induction k
case zero => simp
case succ k ihk =>
simp [show w - 1 + (k + 1) = (w - 1 + k) + 1 by omega, clzAuxRec_succ, ihk,
show x.getLsbD (w - 1 + k + 1) = false by simp only [show w w - 1 + k + 1 by omega, getLsbD_of_ge]]
theorem clzAuxRec_eq_clzAuxRec_of_getLsbD_false {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
x.clzAuxRec n = x.clzAuxRec (n + k) := by
induction k
case zero => simp
case succ k ihk =>
simp only [show n + (k + 1) = (n + k) + 1 by omega, clzAuxRec_succ]
by_cases hxn : x.getLsbD (n + k + 1)
· have : ¬ (i : Nat), n < i x.getLsbD i = false := by
simp only [Classical.not_forall, Bool.not_eq_false]
exists n + k + 1
simp [show n < n + k + 1 by omega, hxn]
contradiction
· simp only [hxn, Bool.false_eq_true, reduceIte]
exact ihk
@[simp]
theorem reverse_eq_zero_iff {x : BitVec w} :
x.reverse = 0#w x = 0#w := by
constructor
· intro hrev
ext i hi
rw [ getLsbD_eq_getElem, getLsbD_eq_getMsbD, getLsbD_reverse]
simp [hrev]
· intro hzero
ext i hi
rw [ getLsbD_eq_getElem, getLsbD_eq_getMsbD, getMsbD_reverse]
simp [hi, hzero]
@[simp]
theorem reverse_reverse_eq {x : BitVec w} :
x.reverse.reverse = x := by
ext k hk
rw [getElem_reverse, getMsbD_reverse, getLsbD_eq_getElem]
/-! ### Inequalities (le / lt) -/
@@ -5848,6 +5934,433 @@ theorem sle_eq_ule {x y : BitVec w} : x.sle y = (x.msb != y.msb ^^ x.ule y) := b
theorem sle_eq_ule_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) : x.sle y = x.ule y := by
simp [BitVec.sle_eq_ule, h]
/-- A bitvector interpreted as a natural number is greater than or equal to `2 ^ i` if and only if
there exists at least one bit with `true` value at position `i` or higher. -/
theorem le_toNat_iff_getLsbD_eq_true {x : BitVec w} (hi : i < w ) :
(2 ^ i x.toNat) ( k, x.getLsbD (i + k) = true) := by
rcases w with _|w
· simp [of_length_zero]
· constructor
· intro hle
apply Classical.byContradiction
intros hcontra
let x' := setWidth (i + 1) x
have hx' : setWidth (i + 1) x = x' := by rfl
have hcast : w - i + (i + 1) = w + 1 := by omega
simp only [not_exists, Bool.not_eq_true] at hcontra
have hx'' : x = BitVec.cast hcast (0#(w - i) ++ x') := by
ext j
by_cases hj : j < i + 1
· simp only [ hx', getElem_cast, getElem_append, hj, reduceDIte, getElem_setWidth]
rw [getLsbD_eq_getElem]
· simp only [getElem_cast, getElem_append, hj, reduceDIte, getElem_zero]
let j' := j - i
simp only [show j = i + j' by omega]
apply hcontra
have : x'.toNat < 2 ^ i := by
apply Nat.lt_pow_two_of_testBit (n := i) x'.toNat
intro j hj
let j' := j - i
specialize hcontra j'
have : x'.getLsbD (i + j') = x.getLsbD (i + j') := by
subst x'
simp [hcontra]
simp [show j = i + j' by omega, testBit_toNat, this, hcontra]
have : x'.toNat = x.toNat := by
have := BitVec.setWidth_eq_append (w := (w + 1)) (v := i + 1) (x := x')
specialize this (by omega)
rw [toNat_eq, toNat_setWidth, Nat.mod_eq_of_lt (by omega)] at this
simp [hx'']
omega
· intro h
obtain k, hk := h
by_cases hk' : i + k < w + 1
· have := Nat.ge_two_pow_of_testBit hk
have := Nat.pow_le_pow_of_le (a := 2) (n := i) (m := i + k) (by omega) (by omega)
omega
· simp [show w + 1 i + k by omega] at hk
/-- A bitvector interpreted as a natural number is strictly smaller than `2 ^ i` if and only if
all bits at position `i` or higher are false. -/
theorem toNat_lt_iff_getLsbD_eq_false {x : BitVec w} (i : Nat) (hi : i < w) :
x.toNat < 2 ^ i ( k, x.getLsbD (i + k) = false) := by
constructor
· intro h
apply Classical.byContradiction
intro hcontra
simp only [Classical.not_forall, Bool.not_eq_false] at hcontra
obtain k, hk := hcontra
have hle := Nat.ge_two_pow_of_testBit hk
by_cases hlt : i + k < w
· have := Nat.pow_le_pow_of_le (a := 2) (n := i) (m := i + k) (by omega) (by omega)
omega
· simp [show w i + k by omega] at hk
· intro h
apply Classical.byContradiction
intro hcontra
simp [BitVec.le_toNat_iff_getLsbD_eq_true (x := x) (i := i) hi, h] at hcontra
/-- If a bitvector interpreted as a natural number is strictly smaller than `2 ^ (k + 1)` and greater than or
equal to 2 ^ k, then the bit at position `k` must be `true` -/
theorem getElem_eq_true_of_lt_of_le {x : BitVec w} (hk' : k < w) (hlt: x.toNat < 2 ^ (k + 1)) (hle : 2 ^ k x.toNat) :
x[k] = true := by
have := le_toNat_iff_getLsbD_eq_true (x := x) (i := k) hk'
simp only [hle, true_iff] at this
obtain k',hk' := this
by_cases hkk' : k + k' < w
· have := Nat.ge_two_pow_of_testBit hk'
by_cases hzk' : k' = 0
· simp [hzk'] at hk'; exact hk'
· have := Nat.pow_lt_pow_of_lt (a := 2) (n := k) (m := k + k') (by omega) (by omega)
have := Nat.pow_le_pow_of_le (a := 2) (n := k + 1) (m := k + k') (by omega) (by omega)
omega
· simp [show w k + k' by omega] at hk'
theorem not_lt_iff {b : BitVec w} : ~~~b < b 0 < w b.msb = true := by
refine fun h => ?_, fun hw, hb => ?_
· have := length_pos_of_lt h
exact this, by rwa [ ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)] at h
· rwa [ ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)]
/-! ### Count leading zeros -/
theorem clzAuxRec_zero (x : BitVec w) :
x.clzAuxRec 0 = if x.getLsbD 0 then BitVec.ofNat w (w - 1) else BitVec.ofNat w w := by rfl
theorem clzAuxRec_succ (x : BitVec w) :
x.clzAuxRec (n + 1) = if x.getLsbD (n + 1) then BitVec.ofNat w (w - 1 - (n + 1)) else BitVec.clzAuxRec x n := by rfl
theorem clzAuxRec_eq_clzAuxRec_of_le {x : BitVec w} (h : w - 1 n) :
x.clzAuxRec n = x.clzAuxRec (w - 1) := by
let k := n - (w - 1)
rw [show n = (w - 1) + k by omega]
induction k
· case zero => simp
· case succ k ihk =>
simp [show w - 1 + (k + 1) = (w - 1 + k) + 1 by omega, clzAuxRec_succ, ihk,
show x.getLsbD (w - 1 + k + 1) = false by simp only [show w w - 1 + k + 1 by omega, getLsbD_of_ge]]
theorem clzAuxRec_eq_clzAuxRec_of_getLsbD_false {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
x.clzAuxRec n = x.clzAuxRec (n + k) := by
induction k
· case zero => simp
· case succ k ihk =>
simp only [show n + (k + 1) = (n + k) + 1 by omega, clzAuxRec_succ]
by_cases hxn : x.getLsbD (n + k + 1)
· have : ¬ (i : Nat), n < i x.getLsbD i = false := by
simp only [Classical.not_forall, Bool.not_eq_false]
exists n + k + 1
simp [show n < n + k + 1 by omega, hxn]
contradiction
· simp only [hxn, Bool.false_eq_true, reduceIte]
exact ihk
theorem clzAuxRec_le {x : BitVec w} (n : Nat) :
clzAuxRec x n w := by
have := Nat.lt_pow_self (a := 2) (n := w) (by omega)
rcases w with _|w
· simp [of_length_zero]
· induction n
· case zero =>
simp only [clzAuxRec_zero]
by_cases hx0 : x.getLsbD 0
· simp only [hx0, Nat.add_one_sub_one, reduceIte, natCast_eq_ofNat, ofNat_le_ofNat,
Nat.mod_two_pow_self, ge_iff_le, Nat.mod_eq_of_lt (a := w) (b := 2 ^ (w + 1)) (by omega)]
omega
· simp only [hx0, Bool.false_eq_true, reduceIte, natCast_eq_ofNat, BitVec.le_refl]
· case succ n ihn =>
simp only [clzAuxRec_succ, Nat.add_one_sub_one, natCast_eq_ofNat, ge_iff_le]
by_cases hxn : x.getLsbD (n + 1)
· simp [hxn, Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^(w + 1)) (by omega)]
omega
· simp only [hxn, Bool.false_eq_true, reduceIte]
exact ihn
theorem clzAuxRec_eq_iff_of_getLsbD_false {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
x.clzAuxRec n = BitVec.ofNat w w j, j n x.getLsbD j = false := by
rcases w with _|w
· simp [of_length_zero]
· have := Nat.lt_pow_self (a := 2) (n := w + 1)
induction n
· case zero =>
simp only [clzAuxRec_zero, Nat.zero_lt_succ, getLsbD_eq_getElem, Nat.add_one_sub_one,
ite_eq_right_iff, Nat.le_zero_eq, forall_eq]
by_cases hx0 : x.getLsbD 0
· simp [hx0, toNat_eq, toNat_ofNat, Nat.mod_eq_of_lt (a := w) (b := 2 ^ (w + 1)) (by omega)]
· simp only [Nat.zero_lt_succ, getLsbD_eq_getElem, Bool.not_eq_true] at hx0
simp [hx0]
· case succ n ihn =>
simp only [clzAuxRec_succ, Nat.add_one_sub_one]
by_cases hxn : x.getLsbD (n + 1)
· simp only [hxn, reduceIte, toNat_eq, toNat_ofNat,
Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^ (w + 1)) (by omega), Nat.mod_two_pow_self,
show ¬w - (n + 1) = w + 1 by omega, false_iff, Classical.not_forall,
Bool.not_eq_false]
exists n + 1, by omega
· have : (i : Nat), n < i x.getLsbD i = false := by
intro i hi
by_cases hi' : i = n + 1
· simp [hi', hxn]
· apply h; omega
specialize ihn this
simp only [Bool.not_eq_true] at ihn hxn
simp only [hxn, Bool.false_eq_true, reduceIte, ihn]
constructor
<;> intro h' j hj
<;> (by_cases hj' : j = n + 1; simp [hj', hxn]; (apply h'; omega))
theorem clz_le {x : BitVec w} :
clz x w := by
unfold clz
rcases w with _|w
· simp [of_length_zero]
· exact clzAuxRec_le (n := w)
@[simp]
theorem clz_eq_iff_eq_zero {x : BitVec w} :
clz x = w x = 0#w := by
rcases w with _|w
· simp [clz, of_length_zero]
· simp only [clz, Nat.add_one_sub_one, natCast_eq_ofNat, zero_iff_eq_false]
rw [clzAuxRec_eq_iff_of_getLsbD_false (x := x) (n := w) (w := w + 1) (by intros i hi; simp [show w + 1 i by omega])]
constructor
· intro h i
by_cases i w
· apply h; omega
· simp [show w + 1 i by omega]
· intro h j hj
apply h
theorem clzAuxRec_eq_zero_iff {x : BitVec w} (h : i, n < i x.getLsbD i = false) (hw : 0 < w) :
(x.clzAuxRec n).toNat = 0 x[w - 1] = true := by
have := Nat.lt_pow_self (a := 2) (n := w)
induction n
· case zero =>
simp only [clzAuxRec_zero]
by_cases hw1 : w - 1 = 0
· by_cases hx0 : x.getLsbD 0
· simp [hw1, hx0]
· simp [hw1, show ¬ w = 0 by omega, hx0, getLsbD_eq_getElem]
· by_cases hx0 : x.getLsbD 0
· simp only [hx0, reduceIte, toNat_ofNat,
Nat.mod_eq_of_lt (a := w - 1) (b := 2 ^ w) (by omega), show ¬w - 1 = 0 by omega, false_iff,
Bool.not_eq_true]
specialize h (w - 1) (by omega)
exact h
· simp [hx0, show ¬ w = 0 by omega]
specialize h (w - 1) (by omega)
exact h
· case succ n ihn =>
by_cases hxn : x.getLsbD (n + 1)
· simp only [clzAuxRec_succ, hxn, reduceIte, toNat_ofNat]
rw [Nat.mod_eq_of_lt (by omega)]
by_cases hwn : w - 1 - (n + 1) = 0
· have := lt_of_getLsbD hxn
simp only [show w - 1 = n + 1 by omega, Nat.sub_self, true_iff]
exact hxn
· simp only [hwn, false_iff, Bool.not_eq_true]
specialize h (w - 1) (by omega)
exact h
· simp only [clzAuxRec_succ, hxn, Bool.false_eq_true, reduceIte]
apply ihn
intro i hi
by_cases hi : i = n + 1
· simp [hi, hxn]
· apply h; omega
theorem clz_eq_zero_iff {x : BitVec w} (hw : 0 < w) :
(clz x).toNat = 0 2 ^ (w - 1) x.toNat := by
simp only [clz, clzAuxRec_eq_zero_iff (x := x) (n := w - 1) (by intro i hi; simp [show w i by omega]) hw]
by_cases hxw : x[w - 1]
· simp [hxw, two_pow_le_toNat_of_getElem_eq_true (x := x) (i := w - 1) (by omega) hxw]
· simp only [hxw, Bool.false_eq_true, false_iff, Nat.not_le]
simp only [ getLsbD_eq_getElem, msb_eq_getLsbD_last, Bool.not_eq_true] at hxw
exact toNat_lt_of_msb_false hxw
/-- The number of leading zeroes is strictly less than the bitwidth iff the bitvector is nonzero. -/
theorem clz_lt_iff_ne_zero {x : BitVec w} :
clz x < w x 0#w := by
have hle := clz_le (x := x)
have heq := clz_eq_iff_eq_zero (x := x)
constructor
· intro h
simp only [natCast_eq_ofNat, BitVec.ne_of_lt (x := x.clz) (y := BitVec.ofNat w w) h,
false_iff] at heq
simp only [ne_eq, heq, not_false_eq_true]
· intro h
simp only [natCast_eq_ofNat, h, iff_false] at heq
apply BitVec.lt_of_le_ne (x := x.clz) (y := BitVec.ofNat w w) hle heq
theorem getLsbD_false_of_clzAuxRec {x : BitVec w} (h : i, n < i x.getLsbD i = false) :
j, x.getLsbD (w - (x.clzAuxRec n).toNat + j) = false := by
rcases w with _|w
· simp
· have := Nat.lt_pow_self (a := 2) (n := w + 1)
induction n
· case zero =>
intro j
simp only [clzAuxRec_zero, Nat.zero_lt_succ, getLsbD_eq_getElem, Nat.add_one_sub_one]
by_cases hx0 : x[0]
· specialize h (1 + j) (by omega)
simp [h, hx0, Nat.mod_eq_of_lt (a := w) (b := 2 ^ (w + 1)) (by omega)]
· simp only [hx0, Bool.false_eq_true, reduceIte, toNat_ofNat, Nat.mod_two_pow_self,
Nat.sub_self, Nat.zero_add]
by_cases hj0 : j = 0
· simp [hj0, hx0]
· specialize h j (by omega)
exact h
· case succ n ihn =>
intro j
by_cases hxn : x.getLsbD (n + 1)
· have := lt_of_getLsbD hxn
specialize h (n + j + 1 + 1) (by omega)
simp [h, clzAuxRec_succ, hxn, Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^ (w + 1)) (by omega),
show (w + 1 - (w - (n + 1)) + j) = n + j + 1 + 1 by omega]
· simp only [clzAuxRec_succ, hxn, Bool.false_eq_true, reduceIte]
apply ihn
intro i hi
by_cases hin : i = n + 1
· simp [hin, hxn]
· specialize h i (by omega)
exact h
theorem getLsbD_true_of_eq_clzAuxRec_of_ne_zero {x : BitVec w} (hx : ¬ x = 0#w) (hn : i, n < i x.getLsbD i = false) :
x.getLsbD (w - 1 - (x.clzAuxRec n).toNat) = true := by
rcases w with _|w
· simp [of_length_zero] at hx
· have := Nat.lt_pow_self (a := 2) (n := w + 1)
induction n
· case zero =>
by_cases hx0 : x[0]
· simp only [Nat.add_one_sub_one, clzAuxRec_zero, Nat.zero_lt_succ, getLsbD_eq_getElem, hx0,
reduceIte, toNat_ofNat, Nat.mod_eq_of_lt (a := w) (b := 2 ^(w + 1)) (by omega), show w - w = 0 by omega]
· simp only [zero_iff_eq_false, Classical.not_forall, Bool.not_eq_false] at hx
obtain m,hm := hx
specialize hn m
by_cases hm0 : m = 0
· simp [hm0, hx0] at hm
· simp [show 0 < m by omega, hm] at hn
· case succ n ihn =>
by_cases hxn : x.getLsbD (n + 1)
· have := lt_of_getLsbD hxn
simp [clzAuxRec_succ, hxn, toNat_ofNat, Nat.mod_eq_of_lt (a := w - (n + 1)) (b := 2 ^ (w + 1)) (by omega),
show w - (w - (n + 1)) = n + 1 by omega]
· simp only [Nat.add_one_sub_one, clzAuxRec_succ, hxn, Bool.false_eq_true, reduceIte]
simp only [Nat.add_one_sub_one] at ihn
apply ihn
intro j hj
by_cases hjn : j = n + 1
· simp [hjn, hxn]
· specialize hn j (by omega)
exact hn
theorem getLsbD_true_clz_of_ne_zero {x : BitVec w} (hw : 0 < w) (hx : x 0#w) :
x.getLsbD (w - 1 - (clz x).toNat) = true := by
unfold clz
apply getLsbD_true_of_eq_clzAuxRec_of_ne_zero (x := x) (n := w - 1) (by omega)
intro i hi
simp [show w i by omega]
/-- A nonzero bitvector is lower-bounded by its leading zeroes. -/
theorem two_pow_sub_clz_le_toNat_of_ne_zero {x : BitVec w} (hw : 0 < w) (hx : x 0#w) :
2 ^ (w - 1 - (clz x).toNat) x.toNat := by
by_cases hc0 : x.clz.toNat = 0
· simp [hc0, clz_eq_zero_iff (x := x) hw]
· have hclz := getLsbD_true_clz_of_ne_zero (x := x) hw hx
rw [getLsbD_eq_getElem (by omega)] at hclz
have hge := Nat.ge_two_pow_of_testBit hclz
push_cast at hge
exact hge
/-- A bitvector is upper bounded by the number of leading zeroes. -/
theorem toNat_lt_two_pow_sub_clz {x : BitVec w} :
x.toNat < 2 ^ (w - (clz x).toNat) := by
rcases w with _|w
· simp [of_length_zero]
· unfold clz
have hlt := toNat_lt_iff_getLsbD_eq_false (x := x)
have hzero := clzAuxRec_eq_zero_iff (x := x) (n := w) (by intro i hi; simp [show w + 1 i by omega]) (by omega)
simp only [Nat.add_one_sub_one] at hzero
by_cases hxw : x[w]
· simp only [hxw, iff_true] at hzero
simp only [Nat.add_one_sub_one, hzero, Nat.sub_zero, gt_iff_lt]
omega
· simp only [hxw, Bool.false_eq_true, iff_false] at hzero
rw [hlt]
· intro k
apply getLsbD_false_of_clzAuxRec (x := x) (n := w)
intro i hi
by_cases hiw : i = w
· simp [hiw, hxw]
· simp [show w + 1 i by omega]
· simp; omega
theorem clz_eq_reverse_ctz {x : BitVec w} :
x.clz = (x.reverse).ctz := by
simp [ctz]
/-! ### Count trailing zeros -/
theorem ctz_eq_reverse_clz {x : BitVec w} :
x.ctz = (x.reverse).clz := by
simp [ctz]
/-- The number of trailing zeroes is strictly less than the bitwidth iff the bitvector is nonzero. -/
@[simp]
theorem ctz_lt_iff_ne_zero {x : BitVec w} :
ctz x < w x 0#w := by
simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq]
rw [show BitVec.ofNat w w = w by simp, reverse_eq_zero_iff (x := x)]
apply clz_lt_iff_ne_zero (x := x.reverse)
/-- If a bitvec is different than zero the bits at indexes lower than `ctz x` are false. -/
theorem getLsbD_false_of_lt_ctz {x : BitVec w} (hi : i < x.ctz.toNat) :
x.getLsbD i = false := by
rw [getLsbD_eq_getMsbD, getLsbD_reverse]
have hiff := ctz_lt_iff_ne_zero (x := x)
by_cases hzero : x = 0#w
· simp [hzero, getLsbD_reverse]
· simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq, hzero, not_false_eq_true,
iff_true] at hiff
simp only [ctz] at hi
have hi' : i < w := by simp [BitVec.lt_def] at hiff; omega
simp only [hi', decide_true, Bool.true_and]
have : (x.reverse.clzAuxRec (w - 1)).toNat w := by
rw [show ((x.reverse.clzAuxRec (w - 1)).toNat w) =
((x.reverse.clzAuxRec (w - 1)).toNat (BitVec.ofNat w w).toNat) by simp, le_def]
apply clzAuxRec_le (x := x.reverse) (n := w - 1)
let j := (x.reverse.clzAuxRec (w - 1)).toNat - 1 - i
rw [show w - 1 - i = w - (x.reverse.clzAuxRec (w - 1)).toNat + j by
subst j
rw [Nat.sub_sub (n := (x.reverse.clzAuxRec (w - 1)).toNat),
Nat.add_sub_assoc (by exact Nat.one_add_le_iff.mpr hi)]
omega]
have hfalse : (i : Nat), w - 1 < i x.reverse.getLsbD i = false := by
intros i hj
simp [show w i by omega]
exact getLsbD_false_of_clzAuxRec (x := x.reverse) (n := w - 1) hfalse (j := j)
/-- If a bitvec is different than zero, the bit at index `ctz x`, i.e., the first bit after the
trailing zeros, is true. -/
theorem getLsbD_true_ctz_of_ne_zero {x : BitVec w} (hx : x 0#w) :
x.getLsbD (ctz x).toNat = true := by
simp only [ctz_eq_reverse_clz, clz]
rw [getLsbD_eq_getMsbD, getLsbD_reverse]
have := ctz_lt_iff_ne_zero (x := x)
simp only [ctz_eq_reverse_clz, clz, natCast_eq_ofNat, lt_def, toNat_ofNat, Nat.mod_two_pow_self,
ne_eq] at this
simp only [this, hx, not_false_eq_true, decide_true, Bool.true_and]
have hnotrev : ¬x.reverse = 0#w := by simp [reverse_eq_zero_iff, hx]
apply getLsbD_true_of_eq_clzAuxRec_of_ne_zero (x := x.reverse) (n := w - 1) hnotrev
intro i hi
simp [show w i by omega]
/-- A nonzero bitvector is lower-bounded by its trailing zeroes. -/
theorem two_pow_ctz_le_toNat_of_ne_zero {x : BitVec w} (hx : x 0#w) :
2 ^ (ctz x).toNat x.toNat := by
have hclz := getLsbD_true_ctz_of_ne_zero (x := x) hx
exact Nat.ge_two_pow_of_testBit hclz
/-! ### Deprecations -/
set_option linter.missingDocs false

View File

@@ -10,7 +10,6 @@ public import Init.NotationExtra
public section
namespace Bool
/--

View File

@@ -7,5 +7,8 @@ module
prelude
public import Init.Data.ByteArray.Basic
public import Init.Data.ByteArray.Bootstrap
public import Init.Data.ByteArray.Extra
public import Init.Data.ByteArray.Lemmas
public section

View File

@@ -6,21 +6,16 @@ Author: Leonardo de Moura
module
prelude
public import Init.Data.Array.Basic
public import Init.Data.Array.DecidableEq
public import Init.Data.UInt.Basic
public import all Init.Data.UInt.BasicAux
public import Init.Data.UInt.BasicAux
import all Init.Data.UInt.BasicAux
public import Init.Data.Option.Basic
public import Init.Data.Array.Extract
@[expose] public section
universe u
structure ByteArray where
data : Array UInt8
attribute [extern "lean_byte_array_mk"] ByteArray.mk
attribute [extern "lean_byte_array_data"] ByteArray.data
namespace ByteArray
deriving instance BEq for ByteArray
@@ -30,29 +25,15 @@ attribute [ext] ByteArray
instance : DecidableEq ByteArray :=
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
@[extern "lean_mk_empty_byte_array"]
def emptyWithCapacity (c : @& Nat) : ByteArray :=
{ data := #[] }
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
def empty : ByteArray := emptyWithCapacity 0
instance : Inhabited ByteArray where
default := empty
instance : EmptyCollection ByteArray where
emptyCollection := ByteArray.empty
@[extern "lean_byte_array_push"]
def push : ByteArray UInt8 ByteArray
| bs, b => bs.push b
@[extern "lean_byte_array_size"]
def size : (@& ByteArray) Nat
| bs => bs.size
@[extern "lean_sarray_size", simp]
def usize (a : @& ByteArray) : USize :=
a.size.toUSize
@@ -106,11 +87,31 @@ def copySlice (src : @& ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff le
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
a.copySlice b empty 0 (e - b)
protected def append (a : ByteArray) (b : ByteArray) : ByteArray :=
protected def fastAppend (a : ByteArray) (b : ByteArray) : ByteArray :=
-- we assume that `append`s may be repeated, so use asymptotic growing; use `copySlice` directly to customize
b.copySlice 0 a a.size b.size false
instance : Append ByteArray := ByteArray.append
@[simp]
theorem size_data {a : ByteArray} :
a.data.size = a.size := rfl
@[csimp]
theorem append_eq_fastAppend : @ByteArray.append = @ByteArray.fastAppend := by
funext a b
ext1
apply Array.ext'
simp [ByteArray.fastAppend, copySlice, size_data, - Array.append_assoc]
-- Needs to come after the `csimp` lemma
instance : Append ByteArray where
append := ByteArray.append
@[simp]
theorem append_eq {a b : ByteArray} : a.append b = a ++ b := rfl
@[simp]
theorem fastAppend_eq {a b : ByteArray} : a.fastAppend b = a ++ b := by
simp [ append_eq_fastAppend]
def toList (bs : ByteArray) : List UInt8 :=
let rec loop (i : Nat) (r : List UInt8) :=
@@ -350,37 +351,4 @@ def prevn : Iterator → Nat → Iterator
end Iterator
end ByteArray
/--
Converts a list of bytes into a `ByteArray`.
-/
def List.toByteArray (bs : List UInt8) : ByteArray :=
let rec loop
| [], r => r
| b::bs, r => loop bs (r.push b)
loop bs ByteArray.empty
instance : ToString ByteArray := fun bs => bs.toList.toString
/-- Interpret a `ByteArray` of size 8 as a little-endian `UInt64`. -/
def ByteArray.toUInt64LE! (bs : ByteArray) : UInt64 :=
assert! bs.size == 8
(bs.get! 7).toUInt64 <<< 0x38 |||
(bs.get! 6).toUInt64 <<< 0x30 |||
(bs.get! 5).toUInt64 <<< 0x28 |||
(bs.get! 4).toUInt64 <<< 0x20 |||
(bs.get! 3).toUInt64 <<< 0x18 |||
(bs.get! 2).toUInt64 <<< 0x10 |||
(bs.get! 1).toUInt64 <<< 0x8 |||
(bs.get! 0).toUInt64
/-- Interpret a `ByteArray` of size 8 as a big-endian `UInt64`. -/
def ByteArray.toUInt64BE! (bs : ByteArray) : UInt64 :=
assert! bs.size == 8
(bs.get! 0).toUInt64 <<< 0x38 |||
(bs.get! 1).toUInt64 <<< 0x30 |||
(bs.get! 2).toUInt64 <<< 0x28 |||
(bs.get! 3).toUInt64 <<< 0x20 |||
(bs.get! 4).toUInt64 <<< 0x18 |||
(bs.get! 5).toUInt64 <<< 0x10 |||
(bs.get! 6).toUInt64 <<< 0x8 |||
(bs.get! 7).toUInt64

View File

@@ -0,0 +1,53 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Prelude
public import Init.Data.List.Basic
public section
namespace ByteArray
@[simp]
theorem data_push {a : ByteArray} {b : UInt8} : (a.push b).data = a.data.push b := rfl
@[expose]
protected def append (a b : ByteArray) : ByteArray :=
a.data.toList ++ b.data.toList
@[simp]
theorem toList_data_append' {a b : ByteArray} :
(a.append b).data.toList = a.data.toList ++ b.data.toList := by
have a := a
have b := b
rfl
theorem ext : {x y : ByteArray} x.data = y.data x = y
| _, _, rfl => rfl
end ByteArray
@[simp]
theorem List.toList_data_toByteArray {l : List UInt8} :
l.toByteArray.data.toList = l := by
rw [List.toByteArray]
suffices a b, (List.toByteArray.loop a b).data.toList = b.data.toList ++ a by
simpa using this l ByteArray.empty
intro a b
fun_induction List.toByteArray.loop a b with simp_all [toList_push]
where
toList_push {xs : Array UInt8} {x : UInt8} : (xs.push x).toList = xs.toList ++ [x] := by
have xs := xs
simp [Array.push, List.concat_eq_append]
theorem List.toByteArray_append' {l l' : List UInt8} :
(l ++ l').toByteArray = l.toByteArray.append l'.toByteArray :=
ByteArray.ext (ext (by simp))
where
ext : {x y : Array UInt8} x.toList = y.toList x = y
| _, _, rfl => rfl

View File

@@ -0,0 +1,34 @@
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
public import Init.Data.ByteArray.Basic
import Init.Data.String.Basic
/-- Interpret a `ByteArray` of size 8 as a little-endian `UInt64`. -/
public def ByteArray.toUInt64LE! (bs : ByteArray) : UInt64 :=
assert! bs.size == 8
(bs.get! 7).toUInt64 <<< 0x38 |||
(bs.get! 6).toUInt64 <<< 0x30 |||
(bs.get! 5).toUInt64 <<< 0x28 |||
(bs.get! 4).toUInt64 <<< 0x20 |||
(bs.get! 3).toUInt64 <<< 0x18 |||
(bs.get! 2).toUInt64 <<< 0x10 |||
(bs.get! 1).toUInt64 <<< 0x8 |||
(bs.get! 0).toUInt64
/-- Interpret a `ByteArray` of size 8 as a big-endian `UInt64`. -/
public def ByteArray.toUInt64BE! (bs : ByteArray) : UInt64 :=
assert! bs.size == 8
(bs.get! 0).toUInt64 <<< 0x38 |||
(bs.get! 1).toUInt64 <<< 0x30 |||
(bs.get! 2).toUInt64 <<< 0x28 |||
(bs.get! 3).toUInt64 <<< 0x20 |||
(bs.get! 4).toUInt64 <<< 0x18 |||
(bs.get! 5).toUInt64 <<< 0x10 |||
(bs.get! 6).toUInt64 <<< 0x8 |||
(bs.get! 7).toUInt64

View File

@@ -0,0 +1,252 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.ByteArray.Basic
public import Init.Data.Array.Extract
public section
@[simp]
theorem ByteArray.data_empty : ByteArray.empty.data = #[] := rfl
@[simp]
theorem ByteArray.data_extract {a : ByteArray} {b e : Nat} :
(a.extract b e).data = a.data.extract b e := by
simp [extract, copySlice]
by_cases b e
· rw [(by omega : b + (e - b) = e)]
· rw [Array.extract_eq_empty_of_le (by omega), Array.extract_eq_empty_of_le (by omega)]
@[simp]
theorem ByteArray.extract_zero_size {b : ByteArray} : b.extract 0 b.size = b := by
ext1
simp
@[simp]
theorem ByteArray.extract_same {b : ByteArray} {i : Nat} : b.extract i i = ByteArray.empty := by
ext1
simp [Nat.min_le_left]
theorem ByteArray.fastAppend_eq_copySlice {a b : ByteArray} :
a.fastAppend b = b.copySlice 0 a a.size b.size false := rfl
@[simp]
theorem List.toByteArray_append {l l' : List UInt8} :
(l ++ l').toByteArray = l.toByteArray ++ l'.toByteArray := by
simp [List.toByteArray_append']
@[simp]
theorem ByteArray.toList_data_append {l l' : ByteArray} :
(l ++ l').data.toList = l.data.toList ++ l'.data.toList := by
simp [ append_eq]
@[simp]
theorem ByteArray.data_append {l l' : ByteArray} :
(l ++ l').data = l.data ++ l'.data := by
simp [ Array.toList_inj]
@[simp]
theorem ByteArray.size_empty : ByteArray.empty.size = 0 := by
simp [ ByteArray.size_data]
@[simp]
theorem List.data_toByteArray {l : List UInt8} :
l.toByteArray.data = l.toArray := by
rw [List.toByteArray]
suffices a b, (List.toByteArray.loop a b).data = b.data ++ a.toArray by
simpa using this l ByteArray.empty
intro a b
fun_induction List.toByteArray.loop a b with simp_all
@[simp]
theorem List.size_toByteArray {l : List UInt8} :
l.toByteArray.size = l.length := by
simp [ ByteArray.size_data]
@[simp]
theorem List.toByteArray_nil : List.toByteArray [] = ByteArray.empty := rfl
@[simp]
theorem ByteArray.empty_append {b : ByteArray} : ByteArray.empty ++ b = b := by
ext1
simp
@[simp]
theorem ByteArray.append_empty {b : ByteArray} : b ++ ByteArray.empty = b := by
ext1
simp
@[simp, grind =]
theorem ByteArray.size_append {a b : ByteArray} : (a ++ b).size = a.size + b.size := by
simp [ size_data]
@[simp]
theorem ByteArray.size_eq_zero_iff {a : ByteArray} : a.size = 0 a = ByteArray.empty := by
refine fun h => ?_, fun h => h ByteArray.size_empty
ext1
simp [ Array.size_eq_zero_iff, h]
theorem ByteArray.getElem_eq_getElem_data {a : ByteArray} {i : Nat} {h : i < a.size} :
a[i] = a.data[i]'(by simpa [ size_data]) := rfl
@[simp]
theorem ByteArray.getElem_append_left {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
(hlt : i < a.size) : (a ++ b)[i] = a[i] := by
simp only [getElem_eq_getElem_data, data_append]
rw [Array.getElem_append_left (by simpa)]
theorem ByteArray.getElem_append_right {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
(hle : a.size i) : (a ++ b)[i] = b[i - a.size]'(by simp_all; omega) := by
simp only [getElem_eq_getElem_data, data_append]
rw [Array.getElem_append_right (by simpa)]
simp
@[simp]
theorem List.getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.toByteArray.size} :
l.toByteArray[i]'h = l[i]'(by simp_all) := by
simp [ByteArray.getElem_eq_getElem_data]
theorem List.getElem_eq_getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.length} :
l[i]'h = l.toByteArray[i]'(by simp_all) := by
simp
@[simp]
theorem ByteArray.size_extract {a : ByteArray} {b e : Nat} :
(a.extract b e).size = min e a.size - b := by
simp [ size_data]
@[simp]
theorem ByteArray.extract_eq_empty_iff {b : ByteArray} {i j : Nat} : b.extract i j = ByteArray.empty min j b.size i := by
rw [ size_eq_zero_iff, size_extract]
omega
@[simp]
theorem ByteArray.extract_add_left {b : ByteArray} {i j : Nat} : b.extract (i + j) i = ByteArray.empty := by
simp only [extract_eq_empty_iff]
exact Nat.le_trans (Nat.min_le_left _ _) (by simp)
@[simp]
theorem ByteArray.append_eq_empty_iff {a b : ByteArray} :
a ++ b = ByteArray.empty a = ByteArray.empty b = ByteArray.empty := by
simp [ size_eq_zero_iff, size_append]
@[simp]
theorem List.toByteArray_eq_empty {l : List UInt8} :
l.toByteArray = ByteArray.empty l = [] := by
simp [ ByteArray.size_eq_zero_iff]
theorem ByteArray.append_right_inj {ys₁ ys₂ : ByteArray} (xs : ByteArray) :
xs ++ ys₁ = xs ++ ys₂ ys₁ = ys₂ := by
simp [ByteArray.ext_iff, Array.append_right_inj]
@[simp]
theorem ByteArray.extract_append_extract {a : ByteArray} {i j k : Nat} :
a.extract i j ++ a.extract j k = a.extract (min i j) (max j k) := by
ext1
simp
theorem ByteArray.extract_eq_extract_append_extract {a : ByteArray} {i k : Nat} (j : Nat)
(hi : i j) (hk : j k) :
a.extract i k = a.extract i j ++ a.extract j k := by
simp
rw [Nat.min_eq_left hi, Nat.max_eq_right hk]
theorem ByteArray.append_inj_left {xs₁ xs₂ ys₁ ys₂ : ByteArray} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) : xs₁ = xs₂ := by
simp only [ByteArray.ext_iff, ByteArray.size_data, ByteArray.data_append] at *
exact Array.append_inj_left h hl
theorem ByteArray.extract_append_eq_right {a b : ByteArray} {i : Nat} (hi : i = a.size) :
(a ++ b).extract i (a ++ b).size = b := by
subst hi
ext1
simp [ size_data]
theorem ByteArray.extract_append_eq_left {a b : ByteArray} {i : Nat} (hi : i = a.size) :
(a ++ b).extract 0 i = a := by
subst hi
ext1
simp
theorem ByteArray.extract_append_size_left {a b : ByteArray} {i : Nat} :
(a ++ b).extract i a.size = a.extract i a.size := by
ext1
simp
theorem ByteArray.extract_append_size_add {a b : ByteArray} {i j : Nat} :
(a ++ b).extract (a.size + i) (a.size + j) = b.extract i j := by
ext1
simp
theorem ByteArray.extract_append {as bs : ByteArray} {i j : Nat} :
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
ext1
simp
theorem ByteArray.extract_append_size_add' {a b : ByteArray} {i j k : Nat} (h : k = a.size) :
(a ++ b).extract (k + i) (k + j) = b.extract i j := by
cases h
rw [extract_append_size_add]
theorem ByteArray.extract_extract {a : ByteArray} {i j k l : Nat} :
(a.extract i j).extract k l = a.extract (i + k) (min (i + l) j) := by
ext1
simp
theorem ByteArray.getElem_extract_aux {xs : ByteArray} {start stop : Nat} (h : i < (xs.extract start stop).size) :
start + i < xs.size := by
rw [size_extract] at h; apply Nat.add_lt_of_lt_sub'; apply Nat.lt_of_lt_of_le h
apply Nat.sub_le_sub_right; apply Nat.min_le_right
theorem ByteArray.getElem_extract {i : Nat} {b : ByteArray} {start stop : Nat}
(h) : (b.extract start stop)[i]'h = b[start + i]'(getElem_extract_aux h) := by
simp [getElem_eq_getElem_data]
theorem ByteArray.extract_eq_extract_left {a : ByteArray} {i i' j : Nat} :
a.extract i j = a.extract i' j min j a.size - i = min j a.size - i' := by
simp [ByteArray.ext_iff, Array.extract_eq_extract_left]
theorem ByteArray.extract_add_one {a : ByteArray} {i : Nat} (ha : i + 1 a.size) :
a.extract i (i + 1) = [a[i]].toByteArray := by
ext
· simp
omega
· rename_i j hj hj'
obtain rfl : j = 0 := by simpa using hj'
simp [ByteArray.getElem_eq_getElem_data]
theorem ByteArray.extract_add_two {a : ByteArray} {i : Nat} (ha : i + 2 a.size) :
a.extract i (i + 2) = [a[i], a[i + 1]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_one (by omega)]
simp [ List.toByteArray_append]
theorem ByteArray.extract_add_three {a : ByteArray} {i : Nat} (ha : i + 3 a.size) :
a.extract i (i + 3) = [a[i], a[i + 1], a[i + 2]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_two (by omega)]
simp [ List.toByteArray_append]
theorem ByteArray.extract_add_four {a : ByteArray} {i : Nat} (ha : i + 4 a.size) :
a.extract i (i + 4) = [a[i], a[i + 1], a[i + 2], a[i + 3]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_three (by omega)]
simp [ List.toByteArray_append]
theorem ByteArray.append_assoc {a b c : ByteArray} : a ++ b ++ c = a ++ (b ++ c) := by
ext1
simp
@[simp]
theorem ByteArray.toList_empty : ByteArray.empty.toList = [] := by
simp [ByteArray.toList, ByteArray.toList.loop]
theorem ByteArray.copySlice_eq_append {src : ByteArray} {srcOff : Nat} {dest : ByteArray} {destOff len : Nat} {exact : Bool} :
ByteArray.copySlice src srcOff dest destOff len exact =
dest.extract 0 destOff ++ src.extract srcOff (srcOff +len) ++ dest.extract (destOff + min len (src.data.size - srcOff)) dest.data.size := by
ext1
simp [copySlice]

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import all Init.Data.Char.Basic
public import Init.Data.Char.Basic
import all Init.Data.Char.Basic
public import Init.Data.UInt.Lemmas
public section
@@ -65,11 +66,6 @@ instance leTotal : Std.Total (· ≤ · : Char → Char → Prop) where
def notLTTotal : Std.Total (¬ · < · : Char Char Prop) where
total := fun x y => by simpa using Char.le_total y x
theorem utf8Size_eq (c : Char) : c.utf8Size = 1 c.utf8Size = 2 c.utf8Size = 3 c.utf8Size = 4 := by
have := c.utf8Size_pos
have := c.utf8Size_le_four
omega
@[simp] theorem ofNat_toNat (c : Char) : Char.ofNat c.toNat = c := by
rw [Char.ofNat, dif_pos]
rfl

12
src/Init/Data/Dyadic.lean Normal file
View File

@@ -0,0 +1,12 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Init.Data.Dyadic.Basic
public import Init.Data.Dyadic.Instances
public import Init.Data.Dyadic.Round
public import Init.Data.Dyadic.Inv

View File

@@ -0,0 +1,747 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Robin Arnez
-/
module
prelude
public import Init.Data.Rat.Lemmas
import Init.Data.Int.Bitwise.Lemmas
import Init.Data.Int.DivMod.Lemmas
/-!
# The dyadic rationals
Constructs the dyadic rationals as an ordered ring, equipped with a compatible embedding into the rationals.
-/
set_option linter.missingDocs true
@[expose] public section
open Nat
namespace Int
/-- The number of trailing zeros in the binary representation of `i`. -/
def trailingZeros (i : Int) : Nat :=
if h : i = 0 then 0 else aux i.natAbs i h (Nat.le_refl _) 0
where
aux (k : Nat) (i : Int) (hi : i 0) (hk : i.natAbs k) (acc : Nat) : Nat :=
match k, (by omega : k 0) with
| k + 1, _ =>
if h : i % 2 = 0 then aux k (i / 2) (by omega) (by omega) (acc + 1)
else acc
-- TODO: check performance of `trailingZeros` in the kernel and VM.
private theorem trailingZeros_aux_irrel (hi : i 0) (hk : i.natAbs k) (hk' : i.natAbs k') :
trailingZeros.aux k i hi hk acc = trailingZeros.aux k' i hi hk' acc := by
fun_induction trailingZeros.aux k i hi hk acc generalizing k' <;>
fun_cases trailingZeros.aux k' _ _ hk' _
· rename_i ih _ _ _ _ _
exact ih _
· contradiction
· contradiction
· rfl
private theorem trailingZeros_aux_succ :
trailingZeros.aux k i hi hk (acc + 1) = trailingZeros.aux k i hi hk acc + 1 := by
fun_induction trailingZeros.aux k i hi hk acc <;> simp_all [trailingZeros.aux]
theorem trailingZeros_zero : trailingZeros 0 = 0 := rfl
theorem trailingZeros_two_mul_add_one (i : Int) :
Int.trailingZeros (2 * i + 1) = 0 := by
unfold trailingZeros trailingZeros.aux
rw [dif_neg (by omega)]
split <;> simp_all
theorem trailingZeros_eq_zero_of_mod_eq {i : Int} (h : i % 2 = 1) :
Int.trailingZeros i = 0 := by
unfold trailingZeros trailingZeros.aux
rw [dif_neg (by omega)]
split <;> simp_all
theorem trailingZeros_two_mul {i : Int} (h : i 0) :
Int.trailingZeros (2 * i) = Int.trailingZeros i + 1 := by
rw [Int.trailingZeros, dif_neg (Int.mul_ne_zero (by decide) h), Int.trailingZeros.aux.eq_def]
simp only [ne_eq, mul_emod_right, reduceDIte, Int.reduceEq, not_false_eq_true,
mul_ediv_cancel_left, Nat.zero_add]
split
rw [trailingZeros, trailingZeros_aux_succ, dif_neg h]
apply congrArg Nat.succ (trailingZeros_aux_irrel ..) <;> omega
theorem shiftRight_trailingZeros_mod_two {i : Int} (h : i 0) :
(i >>> i.trailingZeros) % 2 = 1 := by
rw (occs := .pos [2]) [ Int.emod_add_mul_ediv i 2]
rcases i.emod_two_eq with h' | h' <;> rw [h']
· rcases Int.dvd_of_emod_eq_zero h' with a, rfl
simp only [ne_eq, Int.mul_eq_zero, Int.reduceEq, false_or] at h
rw [Int.zero_add, mul_ediv_cancel_left _ (by decide), trailingZeros_two_mul h, Nat.add_comm,
shiftRight_add, shiftRight_eq_div_pow _ 1]
simpa using shiftRight_trailingZeros_mod_two h
· rwa [Int.add_comm, trailingZeros_two_mul_add_one, shiftRight_zero]
termination_by i.natAbs
theorem two_pow_trailingZeros_dvd {i : Int} (h : i 0) :
2 ^ i.trailingZeros i := by
rcases i.emod_two_eq with h' | h'
· rcases Int.dvd_of_emod_eq_zero h' with a, rfl
simp only [ne_eq, Int.mul_eq_zero, Int.reduceEq, false_or] at h
rw [trailingZeros_two_mul h, Int.pow_succ']
exact Int.mul_dvd_mul_left _ (two_pow_trailingZeros_dvd h)
· rw (occs := .pos [1]) [ Int.emod_add_mul_ediv i 2, h', Int.add_comm, trailingZeros_two_mul_add_one]
exact Int.one_dvd _
termination_by i.natAbs
theorem trailingZeros_shiftLeft {x : Int} (hx : x 0) (n : Nat) :
trailingZeros (x <<< n) = x.trailingZeros + n := by
have : NeZero x := hx
induction n <;> simp [Int.shiftLeft_succ', trailingZeros_two_mul (NeZero.ne _), *, Nat.add_assoc]
@[simp]
theorem trailingZeros_neg (x : Int) : trailingZeros (-x) = x.trailingZeros := by
by_cases hx : x = 0
· simp [hx]
rcases x.emod_two_eq with h | h
· rcases Int.dvd_of_emod_eq_zero h with a, rfl
simp only [Int.mul_ne_zero_iff, ne_eq, Int.reduceEq, not_false_eq_true, true_and] at hx
rw [ Int.mul_neg, trailingZeros_two_mul hx, trailingZeros_two_mul (Int.neg_ne_zero.mpr hx)]
rw [trailingZeros_neg]
· simp [trailingZeros_eq_zero_of_mod_eq, h]
termination_by x.natAbs
end Int
/--
A dyadic rational is either zero or of the form `n * 2^(-k)` for some (unique) `n k : Int`
where `n` is odd.
-/
inductive Dyadic where
/-- The dyadic number `0`. -/
| zero
/-- The dyadic number `n * 2^(-k)` for some odd `n` and integer `k`. -/
| ofOdd (n : Int) (k : Int) (hn : n % 2 = 1)
deriving DecidableEq
namespace Dyadic
/-- Returns the dyadic number representation of `i * 2 ^ (-exp)`. -/
def ofIntWithPrec (i : Int) (prec : Int) : Dyadic :=
if h : i = 0 then .zero
else .ofOdd (i >>> i.trailingZeros) (prec - i.trailingZeros) (Int.shiftRight_trailingZeros_mod_two h)
/-- Convert an integer to a dyadic number (which will necessarily have non-positive precision). -/
def ofInt (i : Int) : Dyadic :=
Dyadic.ofIntWithPrec i 0
instance (n : Nat) : OfNat Dyadic n where
ofNat := Dyadic.ofInt n
instance : IntCast Dyadic := ofInt
instance : NatCast Dyadic := fun x => ofInt x
/-- Add two dyadic numbers. -/
protected def add (x y : Dyadic) : Dyadic :=
match x, y with
| .zero, y => y
| x, .zero => x
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
match k₁ - k₂ with
| 0 => .ofIntWithPrec (n₁ + n₂) k₁
-- TODO: these `simp_all` calls where previously factored out into a `where finally` clause,
-- but there is apparently a bad interaction with the module system.
| (d@hd:(d' + 1) : Nat) => .ofOdd (n₁ + (n₂ <<< d)) k₁ (by simp_all [Int.shiftLeft_eq, Int.pow_succ, Int.mul_assoc])
| -(d + 1 : Nat) => .ofOdd (n₁ <<< (d + 1) + n₂) k₂ (by simp_all [Int.shiftLeft_eq, Int.pow_succ, Int.mul_assoc])
instance : Add Dyadic := Dyadic.add
/-- Multiply two dyadic numbers. -/
protected def mul (x y : Dyadic) : Dyadic :=
match x, y with
| .zero, _ => .zero
| _, .zero => .zero
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
.ofOdd (n₁ * n₂) (k₁ + k₂) (by rw [Int.mul_emod, hn₁, hn₂]; rfl)
instance : Mul Dyadic := Dyadic.mul
/-- Multiply two dyadic numbers. -/
protected def pow (x : Dyadic) (i : Nat) : Dyadic :=
match x with
| .zero => if i = 0 then 1 else 0
| .ofOdd n k hn =>
.ofOdd (n ^ i) (k * i) (by induction i <;> simp [Int.pow_succ, Int.mul_emod, *])
instance : Pow Dyadic Nat := Dyadic.pow
/-- Negate a dyadic number. -/
protected def neg (x : Dyadic) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k hn => .ofOdd (-n) k (by rwa [Int.neg_emod_two])
instance : Neg Dyadic := Dyadic.neg
/-- Subtract two dyadic numbers. -/
protected def sub (x y : Dyadic) : Dyadic := x + (- y)
instance : Sub Dyadic := Dyadic.sub
/-- Shift a dyadic number left by `i` bits. -/
protected def shiftLeft (x : Dyadic) (i : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k hn => .ofOdd n (k - i) hn
/-- Shift a dyadic number right by `i` bits. -/
protected def shiftRight (x : Dyadic) (i : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k hn => .ofOdd n (k + i) hn
instance : HShiftLeft Dyadic Int Dyadic := Dyadic.shiftLeft
instance : HShiftRight Dyadic Int Dyadic := Dyadic.shiftRight
instance : HShiftLeft Dyadic Nat Dyadic := fun x y => x <<< (y : Int)
instance : HShiftRight Dyadic Nat Dyadic := fun x y => x >>> (y : Int)
-- TODO: move this
theorem _root_.Int.natAbs_emod_two (i : Int) : i.natAbs % 2 = (i % 2).natAbs := by omega
/-- Convert a dyadic number to a rational number. -/
def toRat (x : Dyadic) : Rat :=
match x with
| .zero => 0
| .ofOdd n (k : Nat) hn =>
have reduced : n.natAbs.Coprime (2 ^ k) := by
apply Coprime.pow_right
rw [coprime_iff_gcd_eq_one, Nat.gcd_comm, Nat.gcd_def]
simp [hn, Int.natAbs_emod_two]
n, 2 ^ k, Nat.ne_of_gt (Nat.pow_pos (by decide)), reduced
| .ofOdd n (-((k : Nat) + 1)) hn =>
(n * (2 ^ (k + 1) : Nat) : Int)
@[simp] protected theorem zero_eq : Dyadic.zero = 0 := rfl
@[simp] protected theorem add_zero (x : Dyadic) : x + 0 = x := by cases x <;> rfl
@[simp] protected theorem zero_add (x : Dyadic) : 0 + x = x := by cases x <;> rfl
@[simp] protected theorem neg_zero : (-0 : Dyadic) = 0 := rfl
@[simp] protected theorem mul_zero (x : Dyadic) : x * 0 = 0 := by cases x <;> rfl
@[simp] protected theorem zero_mul (x : Dyadic) : 0 * x = 0 := by cases x <;> rfl
@[simp] theorem toRat_zero : toRat 0 = 0 := rfl
theorem _root_.Rat.mkRat_one (x : Int) : mkRat x 1 = x := by
rw [ Rat.mk_den_one, Rat.mk_eq_mkRat]
theorem toRat_ofOdd_eq_mkRat :
toRat (.ofOdd n k hn) = mkRat (n <<< (-k).toNat) (1 <<< k.toNat) := by
cases k
· simp [toRat, Rat.mk_eq_mkRat, Int.shiftLeft_eq, Nat.shiftLeft_eq]
· simp [toRat, Int.neg_negSucc, Rat.mkRat_one, Int.shiftLeft_eq]
theorem toRat_ofIntWithPrec_eq_mkRat :
toRat (.ofIntWithPrec n k) = mkRat (n <<< (-k).toNat) (1 <<< k.toNat) := by
simp only [ofIntWithPrec]
split
· simp_all
rw [toRat_ofOdd_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [Int.natCast_shiftLeft, Int.cast_ofNat_Int, Int.shiftLeft_mul_shiftLeft, Int.mul_one]
have : (-(k - n.trailingZeros) : Int).toNat + k.toNat =
n.trailingZeros + ((-k).toNat + (k - n.trailingZeros).toNat) := by omega
rw [this, Int.shiftLeft_add, Int.shiftRight_shiftLeft_cancel]
exact Int.two_pow_trailingZeros_dvd _
theorem toRat_ofIntWithPrec_eq_mul_two_pow : toRat (.ofIntWithPrec n k) = n * 2 ^ (-k) := by
rw [toRat_ofIntWithPrec_eq_mkRat, Rat.zpow_neg, Int.shiftLeft_eq, Nat.one_shiftLeft]
rw [Rat.mkRat_eq_div, Rat.div_def]
have : ((2 : Int) : Rat) 0 := by decide
simp only [Rat.intCast_mul, Rat.intCast_pow, Rat.zpow_natCast, Rat.intCast_natCast,
Int.natCast_pow, Int.cast_ofNat_Int, Rat.zpow_neg, Rat.mul_assoc, ne_eq,
Rat.intCast_eq_zero_iff, Int.reduceEq, not_false_eq_true, Rat.zpow_add]
rw [Int.add_neg_eq_sub, Int.neg_sub, Int.toNat_sub_toNat_neg]
rfl
example : ((3 : Dyadic) >>> 2) + ((3 : Dyadic) >>> 2) = ((3 : Dyadic) >>> 1) := rfl -- 3/4 + 3/4 = 3/2
example : ((7 : Dyadic) >>> 3) + ((1 : Dyadic) >>> 3) = 1 := rfl -- 7/8 + 1/8 = 1
example : (12 : Dyadic) + ((3 : Dyadic) >>> 1) = (27 : Dyadic) >>> 1 := rfl -- 12 + 3/2 = 27/2 = (2 * 13 + 1)/2^1
example : ((3 : Dyadic) >>> 1).add 12 = (27 : Dyadic) >>> 1 := rfl -- 3/2 + 12 = 27/2 = (2 * 13 + 1)/2^1
example : (12 : Dyadic).add 12 = 24 := rfl -- 12 + 12 = 24
@[simp]
theorem toRat_add (x y : Dyadic) : toRat (x + y) = toRat x + toRat y := by
match x, y with
| .zero, _ => simp [toRat, Rat.zero_add]
| _, .zero => simp [toRat, Rat.add_zero]
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
change (Dyadic.add _ _).toRat = _
rw [Dyadic.add, toRat_ofOdd_eq_mkRat, toRat_ofOdd_eq_mkRat]
rw [Rat.mkRat_add_mkRat _ _ (NeZero.ne _) (NeZero.ne _)]
split
· rename_i h
cases Int.sub_eq_zero.mp h
rw [toRat_ofIntWithPrec_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp [Int.shiftLeft_mul_shiftLeft, Int.add_shiftLeft, Int.add_mul, Nat.add_assoc]
· rename_i h
cases Int.sub_eq_iff_eq_add.mp h
rw [toRat_ofOdd_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [succ_eq_add_one, Int.ofNat_eq_coe, Int.add_shiftLeft, Int.shiftLeft_add,
Int.natCast_mul, Int.natCast_shiftLeft, Int.shiftLeft_mul_shiftLeft, Int.add_mul]
congr 2 <;> omega
· rename_i h
cases Int.sub_eq_iff_eq_add.mp h
rw [toRat_ofOdd_eq_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [Int.add_shiftLeft, Int.shiftLeft_add, Int.natCast_mul, Int.natCast_shiftLeft,
Int.cast_ofNat_Int, Int.shiftLeft_mul_shiftLeft, Int.mul_one, Int.add_mul]
congr 2 <;> omega
@[simp]
theorem toRat_neg (x : Dyadic) : toRat (-x) = - toRat x := by
change x.neg.toRat = _
cases x
· rfl
· simp [Dyadic.neg, Rat.neg_mkRat, Int.neg_shiftLeft, toRat_ofOdd_eq_mkRat]
@[simp]
theorem toRat_sub (x y : Dyadic) : toRat (x - y) = toRat x - toRat y := by
change toRat (x + -y) = _
simp [Rat.sub_eq_add_neg]
@[simp]
theorem toRat_mul (x y : Dyadic) : toRat (x * y) = toRat x * toRat y := by
match x, y with
| .zero, _ => simp
| _, .zero => simp
| .ofOdd n₁ k₁ hn₁, .ofOdd n₂ k₂ hn₂ =>
change (Dyadic.mul _ _).toRat = _
rw [Dyadic.mul, toRat_ofOdd_eq_mkRat, toRat_ofOdd_eq_mkRat, toRat_ofOdd_eq_mkRat,
Rat.mkRat_mul_mkRat, Rat.mkRat_eq_iff (NeZero.ne _) (NeZero.ne _)]
simp only [Int.natCast_mul, Int.natCast_shiftLeft, Int.cast_ofNat_Int,
Int.shiftLeft_mul_shiftLeft, Int.mul_one]
congr 1; omega
@[simp]
protected theorem pow_zero (x : Dyadic) : x ^ 0 = 1 := by
change x.pow 0 = 1
cases x <;> simp [Dyadic.pow] <;> rfl
protected theorem pow_succ (x : Dyadic) (n : Nat) : x ^ (n + 1) = x ^ n * x := by
change x.pow (n + 1) = x.pow n * x
cases x
· simp [Dyadic.pow]
· change _ = Dyadic.mul _ _
simp [Dyadic.pow, Dyadic.mul, Int.pow_succ, Int.mul_add]
@[simp]
theorem toRat_pow (x : Dyadic) (n : Nat) : toRat (x ^ n) = toRat x ^ n := by
induction n with
| zero => simp; rfl
| succ k ih => simp [Dyadic.pow_succ, Rat.pow_succ, ih]
@[simp]
theorem toRat_intCast (x : Int) : (x : Dyadic).toRat = x := by
change (ofInt x).toRat = x
simp [ofInt, toRat_ofIntWithPrec_eq_mul_two_pow]
@[simp]
theorem toRat_natCast (x : Nat) : (x : Dyadic).toRat = x := by
change (ofInt x).toRat = x
simp [ofInt, toRat_ofIntWithPrec_eq_mul_two_pow, Rat.intCast_natCast]
@[simp] theorem of_ne_zero : ofOdd n k hn 0 := Dyadic.noConfusion
@[simp] theorem zero_ne_of : 0 ofOdd n k hn := Dyadic.noConfusion
@[simp]
theorem toRat_eq_zero_iff {x : Dyadic} : x.toRat = 0 x = 0 := by
refine fun h => ?_, fun h => h rfl
cases x
· rfl
· simp only [toRat_ofOdd_eq_mkRat, ne_eq, shiftLeft_eq_zero_iff, succ_ne_self, not_false_eq_true,
Rat.mkRat_eq_zero, Int.shiftLeft_eq_zero_iff] at h
cases h
contradiction
theorem ofOdd_eq_ofIntWithPrec : ofOdd n k hn = ofIntWithPrec n k := by
simp only [ofIntWithPrec, Dyadic.zero_eq, Int.trailingZeros_eq_zero_of_mod_eq hn,
Int.shiftRight_zero, Int.cast_ofNat_Int, Int.sub_zero, right_eq_dite_iff, of_ne_zero, imp_false]
intro rfl; contradiction
theorem toRat_ofOdd_eq_mul_two_pow : toRat (.ofOdd n k hn) = n * 2 ^ (-k) := by
rw [ofOdd_eq_ofIntWithPrec, toRat_ofIntWithPrec_eq_mul_two_pow]
@[simp]
theorem ofIntWithPrec_zero {i : Int} : ofIntWithPrec 0 i = 0 := rfl
@[simp]
theorem neg_ofOdd : -ofOdd n k hn = ofOdd (-n) k (by simpa using hn) := rfl
@[simp]
theorem neg_ofIntWithPrec {i prec : Int} : -ofIntWithPrec i prec = ofIntWithPrec (-i) prec := by
rw [ofIntWithPrec, ofIntWithPrec]
simp only [Dyadic.zero_eq, Int.neg_eq_zero, Int.trailingZeros_neg]
split
· rfl
· obtain a, h := Int.two_pow_trailingZeros_dvd _
rw [Int.mul_comm, Int.shiftLeft_eq] at h
conv => enter [1, 1, 1, 1]; rw [h]
conv => enter [2, 1, 1]; rw [h]
simp only [Int.shiftLeft_shiftRight_cancel, neg_ofOdd, Int.neg_shiftLeft]
theorem ofIntWithPrec_shiftLeft_add {n : Nat} :
ofIntWithPrec ((x : Int) <<< n) (i + n) = ofIntWithPrec x i := by
rw [ofIntWithPrec, ofIntWithPrec]
simp only [Int.shiftLeft_eq_zero_iff]
split
· rfl
· simp [Int.trailingZeros_shiftLeft, *, Int.shiftLeft_shiftRight_eq_shiftRight_of_le,
Int.add_comm x.trailingZeros n, Int.sub_sub]
/-- The "precision" of a dyadic number, i.e. in `n * 2^(-p)` with `n` odd the precision is `p`. -/
-- TODO: If `WithBot` is upstreamed, replace this with `WithBot Int`.
def precision : Dyadic Option Int
| .zero => none
| .ofOdd _ p _ => some p
theorem precision_ofIntWithPrec_le {i : Int} (h : i 0) (prec : Int) :
(ofIntWithPrec i prec).precision some prec := by
simp [ofIntWithPrec, h, precision]
omega
@[simp] theorem precision_zero : (0 : Dyadic).precision = none := rfl
@[simp] theorem precision_neg {x : Dyadic} : (-x).precision = x.precision :=
match x with
| .zero => rfl
| .ofOdd _ _ _ => rfl
end Dyadic
namespace Rat
open Dyadic
/--
Convert a rational number `x` to the greatest dyadic number with precision at most `prec`
which is less than or equal to `x`.
-/
def toDyadic (x : Rat) (prec : Int) : Dyadic :=
match prec with
| (n : Nat) => .ofIntWithPrec ((x.num <<< n) / x.den) prec
| -(n + 1 : Nat) => .ofIntWithPrec (x.num / (x.den <<< (n + 1))) prec
theorem toDyadic_mkRat (a : Int) (b : Nat) (prec : Int) :
Rat.toDyadic (mkRat a b) prec =
.ofIntWithPrec ((a <<< prec.toNat) / (b <<< (-prec).toNat)) prec := by
by_cases hb : b = 0
· cases prec <;> simp [hb, Rat.toDyadic]
rcases h : mkRat a b with n, d, hnz, hr
obtain m, hm, rfl, rfl := Rat.mkRat_num_den hb h
cases prec
· simp only [Rat.toDyadic, Int.ofNat_eq_coe, Int.toNat_natCast, Int.toNat_neg_natCast,
shiftLeft_zero, Int.natCast_mul]
rw [Int.mul_comm d, Int.ediv_ediv (by simp), Int.shiftLeft_mul,
Int.mul_ediv_cancel _ (by simpa using hm)]
· simp only [Rat.toDyadic, Int.natCast_shiftLeft, Int.negSucc_eq, Int.natCast_add_one,
Int.toNat_neg_natCast, Int.shiftLeft_zero, Int.neg_neg, Int.toNat_natCast, Int.natCast_mul]
rw [Int.mul_comm d, Int.mul_shiftLeft, Int.ediv_ediv (by simp),
Int.mul_ediv_cancel _ (by simpa using hm)]
theorem toDyadic_eq_ofIntWithPrec (x : Rat) (prec : Int) :
x.toDyadic prec = .ofIntWithPrec ((x.num <<< prec.toNat) / (x.den <<< (-prec).toNat)) prec := by
conv => lhs; rw [ Rat.mkRat_self x]
rw [Rat.toDyadic_mkRat]
/--
Converting a rational to a dyadic at a given precision and then back to a rational
gives the same result as taking the floor of the rational at precision `2 ^ prec`.
-/
theorem toRat_toDyadic (x : Rat) (prec : Int) :
(x.toDyadic prec).toRat = (x * 2 ^ prec).floor / 2 ^ prec := by
rw [Rat.toDyadic_eq_ofIntWithPrec, toRat_ofIntWithPrec_eq_mul_two_pow, Rat.zpow_neg, Rat.div_def]
congr 2
rw [Rat.floor_def, Int.shiftLeft_eq, Nat.shiftLeft_eq]
match prec with
| .ofNat prec =>
simp only [Int.ofNat_eq_coe, Int.toNat_natCast, Int.toNat_neg_natCast, Nat.pow_zero,
Nat.mul_one]
have : (2 ^ prec : Rat) = ((2 ^ prec : Nat) : Rat) := by simp
rw [Rat.zpow_natCast, this, Rat.mul_def']
simp only [Rat.num_mkRat, Rat.den_mkRat]
simp only [Rat.natCast_pow, Rat.natCast_ofNat, Rat.num_pow, Rat.num_ofNat, Rat.den_pow,
Rat.den_ofNat, Nat.one_pow, Nat.mul_one]
split
· simp_all
· rw [Int.ediv_ediv (Int.ofNat_zero_le _)]
congr 1
rw [Int.natCast_ediv, Int.mul_ediv_cancel']
rw [Int.natCast_dvd_natCast]
apply gcd_dvd_left
| .negSucc prec =>
simp only [Int.toNat_negSucc, Int.pow_zero, Int.mul_one, Int.neg_negSucc, Int.natCast_mul,
Int.natCast_pow, Int.cast_ofNat_Int]
have : (2 ^ ((prec : Int) + 1)) = ((2 ^ (prec + 1) : Nat) : Rat) := by simp; rfl
rw [Int.negSucc_eq, Rat.zpow_neg, this, Rat.mul_def']
simp only [Rat.num_mkRat, Rat.den_mkRat]
simp only [natCast_pow, natCast_ofNat, den_inv, num_pow, num_ofNat, Int.natAbs_pow,
Int.reduceAbs, num_inv, den_pow, den_ofNat, Nat.one_pow, Int.cast_ofNat_Int, Int.mul_one]
have : ¬ (2 ^ (prec + 1) : Int) = 0 := NeZero.out
simp only [if_neg this]
have : (2 ^ (prec + 1) : Int).sign = 1 := by simpa using Int.pow_pos (by decide)
simp only [this]
have : x.den * 2 ^ (prec + 1) = 0 x.den = 0 := by
rw [Nat.mul_eq_zero]
simp_all
simp only [this, Int.mul_one]
split
· simp_all
· rw [Int.ediv_ediv (Int.ofNat_zero_le _)]
congr 1
rw [Int.natCast_ediv, Int.mul_ediv_cancel']
· simp
· rw [Int.natCast_dvd_natCast]
apply gcd_dvd_left
theorem toRat_toDyadic_le {x : Rat} {prec : Int} : (x.toDyadic prec).toRat x := by
rw [toRat_toDyadic]
have : (x * 2 ^ prec).floor x * 2 ^ prec := Rat.floor_le _
apply Rat.le_of_mul_le_mul_right (c := 2 ^ prec)
rw [Rat.div_mul_cancel]
exact this
· apply Rat.ne_of_gt (Rat.zpow_pos (by decide))
· exact Rat.zpow_pos (by decide)
theorem lt_toRat_toDyadic_add {x : Rat} {prec : Int} :
x < (x.toDyadic prec + ofIntWithPrec 1 prec).toRat := by
rw [toRat_add, toRat_toDyadic, toRat_ofIntWithPrec_eq_mul_two_pow]
have := Rat.lt_floor_add_one (x * 2 ^ prec)
rw [Rat.zpow_neg, Rat.div_def, Rat.add_mul]
apply Rat.lt_of_mul_lt_mul_right (c := 2 ^ prec)
rw [Rat.mul_assoc, Rat.inv_mul_cancel, Rat.mul_one]
exact mod_cast this
· apply Rat.ne_of_gt (Rat.zpow_pos (by decide))
· exact Rat.zpow_nonneg (by decide)
-- TODO: `x.toDyadic prec` is the unique dyadic with the given precision satisfying the two inequalities above.
end Rat
namespace Dyadic
/--
Rounds a dyadic rational `x` down to the greatest dyadic number with precision at most `prec`
which is less than or equal to `x`.
-/
def roundDown (x : Dyadic) (prec : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k _ =>
match k - prec with
| .ofNat l => .ofIntWithPrec (n >>> l) prec
| .negSucc _ => x
theorem roundDown_eq_self_of_le {x : Dyadic} {prec : Int} (h : x.precision some prec) :
roundDown x prec = x := by
rcases x with _ | n, k, hn
· rfl
· simp only [precision] at h
obtain a, rfl := h.dest
rcases a with _ | a
· simp [roundDown, ofOdd_eq_ofIntWithPrec]
· have : k - (k + (a + 1 : Nat)) = Int.negSucc a := by omega
simp only [roundDown, this]
@[simp]
theorem toDyadic_toRat (x : Dyadic) (prec : Int) :
x.toRat.toDyadic prec = x.roundDown prec := by
rcases x with _ | n, k, hn
· cases prec <;> simp [Rat.toDyadic, roundDown]
· simp only [toRat_ofOdd_eq_mkRat, roundDown]
rw [Rat.toDyadic_mkRat]
simp only [ Int.shiftLeft_add, Int.natCast_shiftLeft, Int.cast_ofNat_Int]
rw [Int.shiftLeft_eq' 1, Int.one_mul, Int.shiftRight_eq_div_pow]
rw [Int.shiftLeft_shiftRight_eq, Int.toNat_sub, Int.toNat_sub, Int.neg_sub]
have : ((k.toNat + (-prec).toNat : Nat) - ((-k).toNat + prec.toNat : Nat) : Int) = k - prec := by
omega
rw [this]
cases h : k - prec
· simp
· simp only [Int.neg_negSucc, Int.natCast_add, Int.cast_ofNat_Int, Int.toNat_natCast_add_one,
Int.toNat_negSucc, Int.shiftRight_zero]
rw [Int.negSucc_eq, Int.eq_neg_comm, Int.neg_sub, eq_comm, Int.sub_eq_iff_eq_add] at h
simp only [h, Int.natCast_add_one, Int.add_comm _ k, ofIntWithPrec_shiftLeft_add,
ofOdd_eq_ofIntWithPrec]
theorem toRat_inj {x y : Dyadic} : x.toRat = y.toRat x = y := by
refine fun h => ?_, fun h => h rfl
cases x <;> cases y
· rfl
· simp [eq_comm (a := (0 : Rat))] at h
· simp at h
· rename_i n₁ k₁ hn₁ n₂ k₂ hn₂
replace h := congrArg (·.toDyadic (max k₁ k₂)) h
simpa [toDyadic_toRat, roundDown_eq_self_of_le, precision, Int.le_max_left, Int.le_max_right]
using h
theorem add_comm (x y : Dyadic) : x + y = y + x := by
rw [ toRat_inj, toRat_add, toRat_add, Rat.add_comm]
theorem add_assoc (x y z : Dyadic) : (x + y) + z = x + (y + z) := by
rw [ toRat_inj, toRat_add, toRat_add, toRat_add, toRat_add, Rat.add_assoc]
theorem mul_comm (x y : Dyadic) : x * y = y * x := by
rw [ toRat_inj, toRat_mul, toRat_mul, Rat.mul_comm]
theorem mul_assoc (x y z : Dyadic) : (x * y) * z = x * (y * z) := by
rw [ toRat_inj, toRat_mul, toRat_mul, toRat_mul, toRat_mul, Rat.mul_assoc]
theorem mul_one (x : Dyadic) : x * 1 = x := by
rw [ toRat_inj, toRat_mul]
exact Rat.mul_one x.toRat
theorem one_mul (x : Dyadic) : 1 * x = x := by
rw [ toRat_inj, toRat_mul]
exact Rat.one_mul x.toRat
theorem add_mul (x y z : Dyadic) : (x + y) * z = x * z + y * z := by
simp [ toRat_inj, Rat.add_mul]
theorem mul_add (x y z : Dyadic) : x * (y + z) = x * y + x * z := by
simp [ toRat_inj, Rat.mul_add]
theorem neg_add_cancel (x : Dyadic) : -x + x = 0 := by
simp [ toRat_inj, Rat.neg_add_cancel]
theorem neg_mul (x y : Dyadic) : -x * y = -(x * y) := by
simp [ toRat_inj, Rat.neg_mul]
/-- Determine if a dyadic rational is strictly less than another. -/
def blt (x y : Dyadic) : Bool :=
match x, y with
| .zero, .zero => false
| .zero, .ofOdd n₂ _ _ => 0 < n₂
| .ofOdd n₁ _ _, .zero => n₁ < 0
| .ofOdd n₁ k₁ _, .ofOdd n₂ k₂ _ =>
match k₂ - k₁ with
| (l : Nat) => (n₁ <<< l) < n₂
| -((l+1 : Nat)) => n₁ < (n₂ <<< (l + 1))
/-- Determine if a dyadic rational is less than or equal to another. -/
def ble (x y : Dyadic) : Bool :=
match x, y with
| .zero, .zero => true
| .zero, .ofOdd n₂ _ _ => 0 n₂
| .ofOdd n₁ _ _, .zero => n₁ 0
| .ofOdd n₁ k₁ _, .ofOdd n₂ k₂ _ =>
match k₂ - k₁ with
| (l : Nat) => (n₁ <<< l) n₂
| -((l+1 : Nat)) => n₁ (n₂ <<< (l + 1))
theorem blt_iff_toRat {x y : Dyadic} : blt x y x.toRat < y.toRat := by
rcases x with _ | n₁, k₁, hn₁ <;> rcases y with _ | n₂, k₂, hn₂
· decide
· simp only [blt, decide_eq_true_eq, Dyadic.zero_eq, toRat_zero, toRat_ofOdd_eq_mul_two_pow,
Rat.mul_pos_iff_of_pos_right (Rat.zpow_pos (by decide : (0 : Rat) < 2)), Rat.intCast_pos]
· simp only [blt, decide_eq_true_eq, Dyadic.zero_eq, toRat_zero, toRat_ofOdd_eq_mul_two_pow,
Rat.mul_neg_iff_of_pos_right (Rat.zpow_pos (by decide : (0 : Rat) < 2)), Rat.intCast_neg_iff]
· simp only [blt, toRat_ofOdd_eq_mul_two_pow,
Rat.div_lt_iff (Rat.zpow_pos (by decide : (0 : Rat) < 2)), Rat.div_def, Rat.zpow_neg,
Int.neg_neg, Rat.mul_assoc, ne_eq, Rat.ofNat_eq_ofNat, reduceCtorEq, not_false_eq_true,
Rat.zpow_add, Int.shiftLeft_eq]
rw [Int.add_comm, Int.add_neg_eq_sub]
split
· simp [decide_eq_true_eq, Rat.intCast_lt_intCast, Rat.zpow_natCast, *]
· simp only [decide_eq_true_eq, Int.negSucc_eq, *]
rw [Rat.zpow_neg, Rat.div_def, Rat.div_lt_iff (Rat.zpow_pos (by decide))]
simp [ Rat.intCast_lt_intCast, Rat.zpow_natCast, *]
theorem blt_eq_false_iff : blt x y = false ble y x = true := by
cases x <;> cases y
· simp [ble, blt]
· simp [ble, blt]
· simp [ble, blt]
· rename_i n₁ k₁ hn₁ n₂ k₂ hn₂
simp only [blt, ble]
rw [ Int.neg_sub]
rcases k₁ - k₂ with (_ | _) | _
· simp
· simp [ Int.negSucc_eq]
· simp only [Int.neg_negSucc, decide_eq_false_iff_not, Int.not_lt,
decide_eq_true_eq]
theorem ble_iff_toRat : ble x y x.toRat y.toRat := by
rw [ blt_eq_false_iff, Bool.eq_false_iff]
simp only [ne_eq, blt_iff_toRat, Rat.not_lt]
instance : LT Dyadic where
lt x y := blt x y
instance : LE Dyadic where
le x y := ble x y
instance : DecidableLT Dyadic := fun _ _ => inferInstanceAs (Decidable (_ = true))
instance : DecidableLE Dyadic := fun _ _ => inferInstanceAs (Decidable (_ = true))
theorem lt_iff_toRat {x y : Dyadic} : x < y x.toRat < y.toRat := blt_iff_toRat
theorem le_iff_toRat {x y : Dyadic} : x y x.toRat y.toRat := ble_iff_toRat
@[simp]
protected theorem not_le {x y : Dyadic} : ¬x < y y x := by
simp only [· ·, · < ·, Bool.not_eq_true, blt_eq_false_iff]
@[simp]
protected theorem not_lt {x y : Dyadic} : ¬x y y < x := by
rw [ Dyadic.not_le, Decidable.not_not]
@[simp]
protected theorem le_refl (x : Dyadic) : x x := by
rw [le_iff_toRat]
exact Rat.le_refl
protected theorem le_trans {x y z : Dyadic} (h : x y) (h' : y z) : x z := by
rw [le_iff_toRat] at h h'
exact Rat.le_trans h h'
protected theorem le_antisymm {x y : Dyadic} (h : x y) (h' : y x) : x = y := by
rw [le_iff_toRat] at h h'
rw [ toRat_inj]
exact Rat.le_antisymm h h'
protected theorem le_total (x y : Dyadic) : x y y x := by
rw [le_iff_toRat, le_iff_toRat]
exact Rat.le_total
instance : Std.LawfulOrderLT Dyadic where
lt_iff a b := by rw [ Dyadic.not_lt, iff_and_self]; exact (Dyadic.le_total _ _).resolve_left
instance : Std.IsPreorder Dyadic where
le_refl := Dyadic.le_refl
le_trans _ _ _ := Dyadic.le_trans
instance : Std.IsPartialOrder Dyadic where
le_antisymm _ _ := Dyadic.le_antisymm
instance : Std.IsLinearPreorder Dyadic where
le_total := Dyadic.le_total
instance : Std.IsLinearOrder Dyadic where
/-- `roundUp x prec` is the least dyadic number with precision at most `prec` which is greater than or equal to `x`. -/
def roundUp (x : Dyadic) (prec : Int) : Dyadic :=
match x with
| .zero => .zero
| .ofOdd n k _ =>
match k - prec with
| .ofNat l => .ofIntWithPrec (-((-n) >>> l)) prec
| .negSucc _ => x
theorem roundUp_eq_neg_roundDown_neg (x : Dyadic) (prec : Int) :
x.roundUp prec = -((-x).roundDown prec) := by
rcases x with _ | n, k, hn
· rfl
· change _ = -(ofOdd ..).roundDown prec
rw [roundDown, roundUp]
split <;> simp
end Dyadic

View File

@@ -0,0 +1,60 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Robin Arnez
-/
module
prelude
public import Init.Data.Dyadic.Basic
public import Init.Grind.Ring.Basic
public import Init.Grind.Ordered.Ring
/-! # Internal `grind` algebra instances for `Dyadic`. -/
open Lean.Grind
namespace Dyadic
instance : CommRing Dyadic where
nsmul := (· * ·)
zsmul := (· * ·)
add_zero := Dyadic.add_zero
add_comm := Dyadic.add_comm
add_assoc := Dyadic.add_assoc
mul_assoc := Dyadic.mul_assoc
mul_one := Dyadic.mul_one
one_mul := Dyadic.one_mul
zero_mul := Dyadic.zero_mul
mul_zero := Dyadic.mul_zero
mul_comm := Dyadic.mul_comm
pow_zero := Dyadic.pow_zero
pow_succ := Dyadic.pow_succ
sub_eq_add_neg _ _ := rfl
neg_add_cancel := Dyadic.neg_add_cancel
neg_zsmul i a := by
change ((-i : Int) : Dyadic) * a = -(i * a)
simp [ toRat_inj, Rat.neg_mul]
left_distrib := Dyadic.mul_add
right_distrib := Dyadic.add_mul
intCast_neg _ := by simp [ toRat_inj]
ofNat_succ n := by
change ((n + 1 : Int) : Dyadic) = ((n : Int) : Dyadic) + 1
simp [ toRat_inj, Rat.intCast_add]; rfl
instance : IsCharP Dyadic 0 := IsCharP.mk' _ _
(ofNat_eq_zero_iff := fun x => by change (x : Dyadic) = 0 _; simp [ toRat_inj])
instance : NoNatZeroDivisors Dyadic where
no_nat_zero_divisors k a b h₁ h₂ := by
change k * a = k * b at h₂
simp only [ toRat_inj, toRat_mul, toRat_natCast] at h₂
simpa [ Rat.mul_assoc, Rat.inv_mul_cancel, h₁] using congrArg ((k : Rat)⁻¹ * ·) h₂
instance : OrderedRing Dyadic where
zero_lt_one := by decide
add_le_left_iff _ := by simp [le_iff_toRat, Rat.add_le_add_right]
mul_lt_mul_of_pos_left {_ _ _} := by simpa [lt_iff_toRat] using Rat.mul_lt_mul_of_pos_left
mul_lt_mul_of_pos_right {_ _ _} := by simpa [lt_iff_toRat] using Rat.mul_lt_mul_of_pos_right
end Dyadic

View File

@@ -0,0 +1,80 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Data.Dyadic.Basic
import Init.Data.Dyadic.Round
import Init.Grind.Ordered.Ring
/-!
# Inversion for dyadic numbers
-/
namespace Dyadic
/--
Inverts a dyadic number at a given (maximum) precision.
Returns the greatest dyadic number with precision at most `prec` which is less than or equal to `1/x`.
For `x = 0`, returns `0`.
-/
def invAtPrec (x : Dyadic) (prec : Int) : Dyadic :=
match x with
| .zero => .zero
| _ => x.toRat.inv.toDyadic prec
/-- For a positive dyadic `x`, `invAtPrec x prec * x ≤ 1`. -/
theorem invAtPrec_mul_le_one {x : Dyadic} (hx : 0 < x) (prec : Int) :
invAtPrec x prec * x 1 := by
rw [le_iff_toRat]
rw [toRat_mul]
rw [show (1 : Dyadic).toRat = (1 : Rat) from rfl]
unfold invAtPrec
cases x with
| zero =>
exfalso
contradiction
| ofOdd n k hn =>
simp only
have h_le : ((ofOdd n k hn).toRat.inv.toDyadic prec).toRat (ofOdd n k hn).toRat.inv := Rat.toRat_toDyadic_le
have h_pos : 0 (ofOdd n k hn).toRat := by
rw [lt_iff_toRat, toRat_zero] at hx
exact Rat.le_of_lt hx
calc ((ofOdd n k hn).toRat.inv.toDyadic prec).toRat * (ofOdd n k hn).toRat
(ofOdd n k hn).toRat.inv * (ofOdd n k hn).toRat := Rat.mul_le_mul_of_nonneg_right h_le h_pos
_ = 1 := by
apply Rat.inv_mul_cancel
rw [lt_iff_toRat, toRat_zero] at hx
exact Rat.ne_of_gt hx
/-- For a positive dyadic `x`, `1 < (invAtPrec x prec + 2^(-prec)) * x`. -/
theorem one_lt_invAtPrec_add_inc_mul {x : Dyadic} (hx : 0 < x) (prec : Int) :
1 < (invAtPrec x prec + ofIntWithPrec 1 prec) * x := by
rw [lt_iff_toRat]
rw [toRat_mul]
rw [show (1 : Dyadic).toRat = (1 : Rat) from rfl]
unfold invAtPrec
cases x with
| zero =>
exfalso
contradiction
| ofOdd n k hn =>
simp only
have h_le : (ofOdd n k hn).toRat.inv < ((ofOdd n k hn).toRat.inv.toDyadic prec + ofIntWithPrec 1 prec).toRat :=
Rat.lt_toRat_toDyadic_add
have h_pos : 0 < (ofOdd n k hn).toRat := by
rwa [lt_iff_toRat, toRat_zero] at hx
calc
1 = (ofOdd n k hn).toRat.inv * (ofOdd n k hn).toRat := by
symm
apply Rat.inv_mul_cancel
rw [lt_iff_toRat, toRat_zero] at hx
exact Rat.ne_of_gt hx
_ < ((ofOdd n k hn).toRat.inv.toDyadic prec + ofIntWithPrec 1 prec).toRat * (ofOdd n k hn).toRat :=
Rat.mul_lt_mul_of_pos_right h_le h_pos
-- TODO: `invAtPrec` is the unique dyadic with the given precision satisfying the two inequalities above.
end Dyadic

View File

@@ -0,0 +1,77 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Init.Data.Dyadic.Basic
import all Init.Data.Dyadic.Instances
import Init.Data.Int.Bitwise.Lemmas
import Init.Grind.Ordered.Rat
import Init.Grind.Ordered.Field
namespace Dyadic
/-!
Theorems about `roundUp` and `roundDown`.
-/
public section
theorem roundDown_le {x : Dyadic} {prec : Int} : roundDown x prec x :=
match x with
| .zero => Dyadic.le_refl _
| .ofOdd n k _ => by
unfold roundDown
dsimp
match h : k - prec with
| .ofNat l =>
dsimp
rw [ofOdd_eq_ofIntWithPrec, le_iff_toRat]
replace h : k = Int.ofNat l + prec := by omega
subst h
simp only [toRat_ofIntWithPrec_eq_mul_two_pow]
rw [Int.neg_add, Rat.zpow_add (by decide), Rat.mul_assoc]
refine Lean.Grind.OrderedRing.mul_le_mul_of_nonneg_right ?_ (Rat.zpow_nonneg (by decide))
rw [Int.shiftRight_eq_div_pow]
rw [ Lean.Grind.Field.IsOrdered.mul_le_mul_iff_of_pos_right (c := 2^(Int.ofNat l)) (Rat.zpow_pos (by decide))]
simp only [Int.natCast_pow, Int.cast_ofNat_Int, Int.ofNat_eq_coe]
rw [Rat.mul_assoc, Rat.zpow_add (by decide), Int.add_left_neg, Rat.zpow_zero, Rat.mul_one]
have : (2 : Rat) ^ (l : Int) = (2 ^ l : Int) := by
rw [Rat.zpow_natCast, Rat.intCast_pow, Rat.intCast_ofNat]
rw [this, Rat.intCast_mul, Rat.intCast_le_intCast]
exact Int.ediv_mul_le n (Int.pow_ne_zero (by decide))
| .negSucc _ =>
apply Dyadic.le_refl
theorem precision_roundDown {x : Dyadic} {prec : Int} : (roundDown x prec).precision some prec := by
unfold roundDown
match x with
| zero => simp [precision]
| ofOdd n k hn =>
dsimp
split
· rename_i n' h
by_cases h' : n >>> n' = 0
· simp [h']
· exact precision_ofIntWithPrec_le h' _
· simp [precision]
omega
-- This theorem would characterize `roundDown` in terms of the order and `precision`.
-- theorem le_roundDown {x y : Dyadic} {prec : Int} (h : y.precision ≤ some prec) (h' : y ≤ x) :
-- y ≤ x.roundDown prec := sorry
theorem le_roundUp {x : Dyadic} {prec : Int} : x roundUp x prec := by
rw [roundUp_eq_neg_roundDown_neg, Lean.Grind.OrderedAdd.le_neg_iff]
apply roundDown_le
theorem precision_roundUp {x : Dyadic} {prec : Int} : (roundUp x prec).precision some prec := by
rw [roundUp_eq_neg_roundDown_neg, precision_neg]
exact precision_roundDown
-- This theorem would characterize `roundUp` in terms of the order and `precision`.
-- theorem roundUp_le {x y : Dyadic} {prec : Int} (h : y.precision ≤ some prec) (h' : x ≤ y) :
-- x.roundUp prec ≤ y := sorry

View File

@@ -51,6 +51,11 @@ The assumption `NeZero n` ensures that `Fin n` is nonempty.
@[expose] protected def ofNat (n : Nat) [NeZero n] (a : Nat) : Fin n :=
a % n, Nat.mod_lt _ (pos_of_neZero n)
@[simp]
theorem Internal.ofNat_eq_ofNat {n : Nat} {hn} {a : Nat} :
letI : NeZero n := Nat.pos_iff_ne_zero.1 hn
Fin.Internal.ofNat n hn a = Fin.ofNat n a := rfl
@[deprecated Fin.ofNat (since := "2025-05-28")]
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
Fin.ofNat n a

View File

@@ -122,7 +122,7 @@ private theorem foldlM_loop [Monad m] (f : α → Fin (n+1) → m α) (x) (h : i
rw [foldlM_loop_lt _ _ h', foldlM_loop]; rfl
else
cases Nat.le_antisymm (Nat.le_of_lt_succ h) (Nat.not_lt.1 h')
rw [foldlM_loop_lt]
rw [foldlM_loop_lt _ _ h]
congr; funext
rw [foldlM_loop_eq, foldlM_loop_eq]
termination_by n - i

View File

@@ -25,12 +25,12 @@ namespace Fin
@[deprecated ofNat_zero (since := "2025-05-28")] abbrev ofNat'_zero := @ofNat_zero
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a % m) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a.val % m.val) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
rfl
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a * b) % n) (Nat.mod_lt _ a.pos) := rfl
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a.val * b.val) % n) (Nat.mod_lt _ a.pos) := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b) + a) % n) (Nat.mod_lt _ a.pos) := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b.val) + a.val) % n) (Nat.mod_lt _ a.pos) := rfl
theorem pos' : [Nonempty (Fin n)], 0 < n | i => i.pos
@@ -81,7 +81,7 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[deprecated ofNat_self (since := "2025-05-28")] abbrev ofNat'_self := @ofNat_self
@[simp] theorem ofNat_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat n x) = x := by
@[simp] theorem ofNat_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat n x.val) = x := by
ext
rw [val_ofNat, Nat.mod_eq_of_lt]
exact x.2
@@ -121,8 +121,6 @@ Non-trivial loops lead to undesirable and counterintuitive elaboration behavior.
For example, for `x : Fin k` and `n : Nat`,
it causes `x < n` to be elaborated as `x < ↑n` rather than `↑x < n`,
silently introducing wraparound arithmetic.
Note: as of 2025-06-03, Mathlib has such a coercion for `Fin n` anyway!
-/
@[expose]
def instNatCast (n : Nat) [NeZero n] : NatCast (Fin n) where
@@ -265,7 +263,7 @@ instance : LawfulOrderLT (Fin n) where
lt_iff := by
simp [ Fin.not_le, Decidable.imp_iff_not_or, Std.Total.total]
@[simp, grind =] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp, grind =] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@@ -500,9 +498,11 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
ext
simp
@[simp] theorem cast_trans {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
@[simp, grind =] theorem cast_cast {k : Nat} (h : n = m) (h' : m = k) {i : Fin n} :
(i.cast h).cast h' = i.cast (Eq.trans h h') := rfl
@[deprecated cast_cast (since := "2025-09-03")] abbrev cast_trans := @cast_cast
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m n} : castLE h' = Fin.cast h := rfl
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@@ -531,7 +531,7 @@ theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
(i.castAdd m').cast h = i.castAdd m := rfl
theorem castAdd_castAdd {m n p : Nat} (i : Fin m) :
(i.castAdd n).castAdd p = (i.castAdd (n + p)).cast (Nat.add_assoc ..).symm := rfl
(i.castAdd n).castAdd p = (i.castAdd (n + p)).cast (Nat.add_assoc ..).symm := rfl
/-- The cast of the successor is the successor of the cast. See `Fin.succ_cast_eq` for rewriting in
the reverse direction. -/

View File

@@ -8,7 +8,7 @@ module
prelude
public import Init.Control.State
public import Init.Data.Int.Basic
public import Init.Data.String.Basic
public import Init.Data.String.Bootstrap
public section
@@ -168,8 +168,8 @@ private def spaceUptoLine : Format → Bool → Int → Nat → SpaceResult
else
{ foundLine := true }
| text s, flatten, _, _ =>
let p := s.posOf '\n'
let off := s.offsetOfPos p
let p := String.Internal.posOf s '\n'
let off := String.Internal.offsetOfPos s p
{ foundLine := p != s.endPos, foundFlattenedHardLine := flatten && p != s.endPos, space := off }
| append f₁ f₂, flatten, m, w => merge w (spaceUptoLine f₁ flatten m w) (spaceUptoLine f₂ flatten m)
| nest n f, flatten, m, w => spaceUptoLine f flatten (m - n) w
@@ -263,15 +263,15 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
| append f₁ f₂ => be w (gs' ({ i with f := f₁, activeTags := 0 }::{ i with f := f₂ }::is))
| nest n f => be w (gs' ({ i with f, indent := i.indent + n }::is))
| text s =>
let p := s.posOf '\n'
let p := String.Internal.posOf s '\n'
if p == s.endPos then
pushOutput s
endTags i.activeTags
be w (gs' is)
else
pushOutput (s.extract {} p)
pushOutput (String.Internal.extract s {} p)
pushNewline i.indent.toNat
let is := { i with f := text (s.extract (s.next p) s.endPos) }::is
let is := { i with f := text (String.Internal.extract s (String.Internal.next s p) s.endPos) }::is
-- after a hard line break, re-evaluate whether to flatten the remaining group
-- note that we shouldn't start flattening after a hard break outside a group
if g.fla == .disallow then
@@ -298,7 +298,7 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
pushGroup FlattenBehavior.fill is gs w >>= be w
-- if preceding fill item fit in a single line, try to fit next one too
if g.fla.shouldFlatten then
let gs'@(g'::_) pushGroup FlattenBehavior.fill is gs (w - " ".length)
let gs'@(g'::_) pushGroup FlattenBehavior.fill is gs (w - String.Internal.length " ")
| panic "unreachable"
if g'.fla.shouldFlatten then
pushOutput " "
@@ -316,7 +316,7 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
else
let k currColumn
if k < i.indent then
pushOutput ("".pushn ' ' (i.indent - k).toNat)
pushOutput (String.Internal.pushn "" ' ' (i.indent - k).toNat)
endTags i.activeTags
be w (gs' is)
else
@@ -350,7 +350,7 @@ Creates a format `l ++ f ++ r` with a flattening group, nesting the contents by
The group's `FlattenBehavior` is `allOrNone`; for `fill` use `Std.Format.bracketFill`.
-/
@[inline] def bracket (l : String) (f : Format) (r : String) : Format :=
group (nest l.length $ l ++ f ++ r)
group (nest (String.Internal.length l) $ l ++ f ++ r)
/--
Creates the format `"(" ++ f ++ ")"` with a flattening group, nesting by one space.
@@ -372,7 +372,7 @@ Creates a format `l ++ f ++ r` with a flattening group, nesting the contents by
The group's `FlattenBehavior` is `fill`; for `allOrNone` use `Std.Format.bracketFill`.
-/
@[inline] def bracketFill (l : String) (f : Format) (r : String) : Format :=
fill (nest l.length $ l ++ f ++ r)
fill (nest (String.Internal.length l) $ l ++ f ++ r)
/-- The default indentation level, which is two spaces. -/
def defIndent := 2
@@ -397,8 +397,8 @@ private structure State where
private instance : MonadPrettyFormat (StateM State) where
-- We avoid a structure instance update, and write these functions using pattern matching because of issue #316
pushOutput s := modify fun out, col => out ++ s, col + s.length
pushNewline indent := modify fun out, _ => out ++ "\n".pushn ' ' indent, indent
pushOutput s := modify fun out, col => String.Internal.append out s, col + (String.Internal.length s)
pushNewline indent := modify fun out, _ => String.Internal.append out (String.Internal.pushn "\n" ' ' indent), indent
currColumn := return ( get).column
startTag _ := return ()
endTags _ := return ()

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.Format.Basic
public import Init.Data.Array.Basic
public import Init.Data.ToString.Basic
import Init.Data.String.Basic
public section

View File

@@ -9,6 +9,8 @@ prelude
public import Init.Data.Format.Macro
public import Init.Data.Format.Instances
public import Init.Meta
import Init.Data.String.Basic
import Init.Data.ToString.Name
public section

View File

@@ -3,15 +3,11 @@ Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
public import Init.Core
public import Init.Grind.Tactics
public section
namespace Function
/--
@@ -34,20 +30,108 @@ Examples:
@[inline, expose]
def uncurry : (α β φ) α × β φ := fun f a => f a.1 a.2
@[simp, grind]
@[simp, grind =]
theorem curry_uncurry (f : α β φ) : curry (uncurry f) = f :=
rfl
@[simp, grind]
@[simp, grind =]
theorem uncurry_curry (f : α × β φ) : uncurry (curry f) = f :=
funext fun _a, _b => rfl
@[simp, grind]
@[simp, grind =]
theorem uncurry_apply_pair {α β γ} (f : α β γ) (x : α) (y : β) : uncurry f (x, y) = f x y :=
rfl
@[simp, grind]
@[simp, grind =]
theorem curry_apply {α β γ} (f : α × β γ) (x : α) (y : β) : curry f x y = f (x, y) :=
rfl
/-- A function `f : α → β` is called injective if `f x = f y` implies `x = y`. -/
@[expose]
def Injective (f : α β) : Prop :=
a₁ a₂, f a₁ = f a₂ a₁ = a₂
theorem Injective.comp {α β γ} {g : β γ} {f : α β} (hg : Injective g) (hf : Injective f) :
Injective (g f) := fun _a₁ _a₂ => fun h => hf (hg h)
/-- A function `f : α → β` is called surjective if every `b : β` is equal to `f a`
for some `a : α`. -/
@[expose]
def Surjective (f : α β) : Prop :=
b, Exists fun a => f a = b
theorem Surjective.comp {α β γ} {g : β γ} {f : α β} (hg : Surjective g) (hf : Surjective f) :
Surjective (g f) := fun c : γ =>
Exists.elim (hg c) fun b hb =>
Exists.elim (hf b) fun a ha =>
Exists.intro a (show g (f a) = c from Eq.trans (congrArg g ha) hb)
/-- `LeftInverse g f` means that `g` is a left inverse to `f`. That is, `g ∘ f = id`. -/
@[expose, grind]
def LeftInverse {α β} (g : β α) (f : α β) : Prop :=
x, g (f x) = x
/-- `HasLeftInverse f` means that `f` has an unspecified left inverse. -/
@[expose]
def HasLeftInverse {α β} (f : α β) : Prop :=
Exists fun finv : β α => LeftInverse finv f
/-- `RightInverse g f` means that `g` is a right inverse to `f`. That is, `f ∘ g = id`. -/
@[expose, grind]
def RightInverse {α β} (g : β α) (f : α β) : Prop :=
LeftInverse f g
/-- `HasRightInverse f` means that `f` has an unspecified right inverse. -/
@[expose]
def HasRightInverse {α β} (f : α β) : Prop :=
Exists fun finv : β α => RightInverse finv f
theorem LeftInverse.injective {α β} {g : β α} {f : α β} : LeftInverse g f Injective f :=
fun h a b faeqfb => ((h a).symm.trans (congrArg g faeqfb)).trans (h b)
theorem HasLeftInverse.injective {α β} {f : α β} : HasLeftInverse f Injective f := fun h =>
Exists.elim h fun _finv inv => inv.injective
theorem rightInverse_of_injective_of_leftInverse {α β} {f : α β} {g : β α} (injf : Injective f)
(lfg : LeftInverse f g) : RightInverse f g := fun x =>
have h : f (g (f x)) = f x := lfg (f x)
injf h
theorem RightInverse.surjective {α β} {f : α β} {g : β α} (h : RightInverse g f) : Surjective f :=
fun y => g y, h y
theorem HasRightInverse.surjective {α β} {f : α β} : HasRightInverse f Surjective f
| _finv, inv => inv.surjective
theorem leftInverse_of_surjective_of_rightInverse {α β} {f : α β} {g : β α} (surjf : Surjective f)
(rfg : RightInverse f g) : LeftInverse f g := fun y =>
Exists.elim (surjf y) fun x hx => ((hx rfl : f (g y) = f (g (f x))).trans (Eq.symm (rfg x) rfl)).trans hx
theorem injective_id : Injective (@id α) := fun _a₁ _a₂ h => h
theorem surjective_id : Surjective (@id α) := fun a => a, rfl
variable {f : α β}
theorem Injective.eq_iff (I : Injective f) {a b : α} : f a = f b a = b :=
@I _ _, congrArg f
theorem Injective.eq_iff' (I : Injective f) {a b : α} {c : β} (h : f b = c) : f a = c a = b :=
h I.eq_iff
theorem Injective.ne (hf : Injective f) {a₁ a₂ : α} : a₁ a₂ f a₁ f a₂ :=
mt fun h hf h
theorem Injective.ne_iff (hf : Injective f) {x y : α} : f x f y x y :=
mt <| congrArg f, hf.ne
theorem Injective.ne_iff' (hf : Injective f) {x y : α} {z : β} (h : f y = z) : f x z x y :=
h hf.ne_iff
protected theorem LeftInverse.id {α β} {g : β α} {f : α β} (h : LeftInverse g f) : g f = id :=
funext h
protected theorem RightInverse.id {α β} {g : β α} {f : α β} (h : RightInverse g f) : f g = id :=
funext h
end Function

View File

@@ -31,6 +31,7 @@ This file defines the `Int` type as well as
Division and modulus operations are defined in `Init.Data.Int.DivMod.Basic`.
-/
set_option genCtorIdx false in
/--
The integers.
@@ -320,6 +321,8 @@ def natAbs (m : @& Int) : Nat :=
| ofNat m => m
| -[m +1] => m.succ
attribute [gen_constructor_elims] Int
/-! ## sign -/
/--

View File

@@ -50,4 +50,21 @@ protected def shiftRight : Int → Nat → Int
instance : HShiftRight Int Nat Int := .shiftRight
/--
Bitwise left shift, usually accessed via the `<<<` operator.
Examples:
* `1 <<< 2 = 4`
* `1 <<< 3 = 8`
* `0 <<< 3 = 0`
* `0xf1 <<< 4 = 0xf10`
* `(-1) <<< 3 = -8`
-/
@[expose]
protected def shiftLeft : Int Nat Int
| Int.ofNat n, s => Int.ofNat (n <<< s)
| Int.negSucc n, s => Int.negSucc (((n + 1) <<< s) - 1)
instance : HShiftLeft Int Nat Int := .shiftLeft
end Int

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.Nat.Bitwise.Lemmas
public import all Init.Data.Int.Bitwise.Basic
public import Init.Data.Int.Bitwise.Basic
import all Init.Data.Int.Bitwise.Basic
public import Init.Data.Int.DivMod.Lemmas
public section
@@ -16,8 +17,8 @@ namespace Int
theorem shiftRight_eq (n : Int) (s : Nat) : n >>> s = Int.shiftRight n s := rfl
@[simp]
theorem natCast_shiftRight (n s : Nat) : (n : Int) >>> s = n >>> s := rfl
@[simp, norm_cast]
theorem natCast_shiftRight (n s : Nat) : n >>> s = (n : Int) >>> s := rfl
@[simp]
theorem negSucc_shiftRight (m n : Nat) :
@@ -37,11 +38,11 @@ theorem shiftRight_eq_div_pow (m : Int) (n : Nat) :
· rw [negSucc_ediv _ (by norm_cast; exact Nat.pow_pos (Nat.zero_lt_two))]
rfl
@[simp]
@[simp, grind =]
theorem zero_shiftRight (n : Nat) : (0 : Int) >>> n = 0 := by
simp [Int.shiftRight_eq_div_pow]
@[simp]
@[simp, grind =]
theorem shiftRight_zero (n : Int) : n >>> 0 = n := by
simp [Int.shiftRight_eq_div_pow]
@@ -67,7 +68,7 @@ theorem shiftRight_le_of_nonneg {n : Int} {s : Nat} (h : 0 ≤ n) : n >>> s ≤
by_cases hm : m = 0
· simp [hm]
· have := Nat.shiftRight_le m s
simp
rw [ofNat_eq_coe]
omega
case _ _ _ m =>
omega
@@ -88,4 +89,94 @@ theorem shiftRight_le_of_nonpos {n : Int} {s : Nat} (h : n ≤ 0) : (n >>> s)
have rl : n / 2 ^ s 0 := Int.ediv_nonpos_of_nonpos_of_neg (by omega) (by norm_cast at *; omega)
norm_cast at *
@[simp, norm_cast]
theorem natCast_shiftLeft (n s : Nat) : n <<< s = (n : Int) <<< s := rfl
@[simp, grind =]
theorem zero_shiftLeft (n : Nat) : (0 : Int) <<< n = 0 := by
change ((0 <<< n : Nat) : Int) = 0
simp
@[simp, grind =]
theorem shiftLeft_zero (n : Int) : n <<< 0 = n := by
change Int.shiftLeft _ _ = _
match n with
| Int.ofNat n
| Int.negSucc n => simp [Int.shiftLeft]
theorem shiftLeft_succ (m : Int) (n : Nat) : m <<< (n + 1) = (m <<< n) * 2 := by
change Int.shiftLeft _ _ = Int.shiftLeft _ _ * 2
match m with
| (m : Nat) =>
dsimp only [Int.shiftLeft, Int.ofNat_eq_coe]
rw [Nat.shiftLeft_succ, Nat.mul_comm, natCast_mul, ofNat_two]
| Int.negSucc m =>
dsimp only [Int.shiftLeft]
rw [Nat.shiftLeft_succ, Nat.mul_comm, Int.negSucc_eq]
have := Nat.le_shiftLeft (a := m + 1) (b := n)
omega
theorem shiftLeft_succ' (m : Int) (n : Nat) : m <<< (n + 1) = 2 * (m <<< n) := by
rw [shiftLeft_succ, Int.mul_comm]
theorem shiftLeft_eq (a : Int) (b : Nat) : a <<< b = a * 2 ^ b := by
induction b with
| zero => simp
| succ b ih =>
rw [shiftLeft_succ, ih, Int.pow_succ, Int.mul_assoc]
theorem shiftLeft_eq' (a : Int) (b : Nat) : a <<< b = a * (2 ^ b : Nat) := by
simp [shiftLeft_eq]
theorem shiftLeft_add (a : Int) (b c : Nat) : a <<< (b + c) = a <<< b <<< c := by
simp [shiftLeft_eq, Int.pow_add, Int.mul_assoc]
@[simp]
theorem shiftLeft_shiftRight_cancel (a : Int) (b : Nat) : a <<< b >>> b = a := by
simp [shiftLeft_eq, shiftRight_eq_div_pow, mul_ediv_cancel _ (NeZero.ne _)]
theorem shiftLeft_shiftRight_eq_shiftLeft_of_le {b c : Nat} (h : c b) (a : Int) :
a <<< b >>> c = a <<< (b - c) := by
obtain b, rfl := h.dest
simp [shiftLeft_eq, Int.pow_add, shiftRight_eq_div_pow, Int.mul_left_comm a,
Int.mul_ediv_cancel_left _ (NeZero.ne _)]
theorem shiftLeft_shiftRight_eq_shiftRight_of_le {b c : Nat} (h : b c) (a : Int) :
a <<< b >>> c = a >>> (c - b) := by
obtain c, rfl := h.dest
simp [shiftRight_add]
theorem shiftLeft_shiftRight_eq (a : Int) (b c : Nat) :
a <<< b >>> c = a <<< (b - c) >>> (c - b) := by
rcases Nat.le_total b c with h | h
· simp [shiftLeft_shiftRight_eq_shiftRight_of_le h, Nat.sub_eq_zero_of_le h]
· simp [shiftLeft_shiftRight_eq_shiftLeft_of_le h, Nat.sub_eq_zero_of_le h]
@[simp]
theorem shiftRight_shiftLeft_cancel {a : Int} {b : Nat} (h : 2 ^ b a) : a >>> b <<< b = a := by
simp [shiftLeft_eq, shiftRight_eq_div_pow, Int.ediv_mul_cancel h]
theorem add_shiftLeft (a b : Int) (n : Nat) : (a + b) <<< n = a <<< n + b <<< n := by
simp [shiftLeft_eq, Int.add_mul]
theorem neg_shiftLeft (a : Int) (n : Nat) : (-a) <<< n = -a <<< n := by
simp [Int.shiftLeft_eq, Int.neg_mul]
theorem shiftLeft_mul (a b : Int) (n : Nat) : a <<< n * b = (a * b) <<< n := by
simp [shiftLeft_eq, Int.mul_right_comm]
theorem mul_shiftLeft (a b : Int) (n : Nat) : a * b <<< n = (a * b) <<< n := by
simp [shiftLeft_eq, Int.mul_assoc]
theorem shiftLeft_mul_shiftLeft (a b : Int) (m n : Nat) :
a <<< m * b <<< n = (a * b) <<< (m + n) := by
simp [shiftLeft_mul, mul_shiftLeft, shiftLeft_add]
@[simp]
theorem shiftLeft_eq_zero_iff {a : Int} {n : Nat} : a <<< n = 0 a = 0 := by
simp [shiftLeft_eq, Int.mul_eq_zero, NeZero.ne]
instance {a : Int} {n : Nat} [NeZero a] : NeZero (a <<< n) :=
mt shiftLeft_eq_zero_iff.mp (NeZero.ne _)
end Int

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro, Paul Reichert
module
prelude
public import all Init.Data.Ord.Basic
public import Init.Data.Ord.Basic
import all Init.Data.Ord.Basic
public import Init.Data.Int.Order
public section

View File

@@ -97,7 +97,7 @@ theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := natCast_emod m n
/-! ### mod definitions -/
theorem emod_add_ediv : a b : Int, a % b + b * (a / b) = a
theorem emod_add_mul_ediv : a b : Int, a % b + b * (a / b) = a
| ofNat _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
| ofNat m, -[n+1] => by
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
@@ -111,19 +111,35 @@ 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 ..
@[deprecated emod_add_mul_ediv (since := "2025-09-01")]
def emod_add_ediv := @emod_add_mul_ediv
theorem ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
rw [Int.add_comm]; exact emod_add_ediv ..
theorem emod_add_ediv_mul (a b : Int) : a % b + a / b * b = a := by
rw [Int.mul_comm]; exact emod_add_mul_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 ..
@[deprecated emod_add_ediv_mul (since := "2025-09-01")]
def emod_add_ediv' := @emod_add_ediv_mul
theorem mul_ediv_add_emod (a b : Int) : b * (a / b) + a % b = a := by
rw [Int.add_comm]; exact emod_add_mul_ediv ..
@[deprecated mul_ediv_add_emod (since := "2025-09-01")]
def ediv_add_emod := @mul_ediv_add_emod
theorem ediv_mul_add_emod (a b : Int) : a / b * b + a % b = a := by
rw [Int.mul_comm]; exact mul_ediv_add_emod ..
@[deprecated ediv_mul_add_emod (since := "2025-09-01")]
def ediv_add_emod' := @ediv_mul_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]
rw [ Int.add_sub_cancel (a % b), emod_add_mul_ediv]
theorem mul_ediv_self (a b : Int) : b * (a / b) = a - a % b := by
rw [emod_def, Int.sub_sub_self]
theorem ediv_mul_self (a b : Int) : a / b * b = a - a % b := by
rw [Int.mul_comm, emod_def, Int.sub_sub_self]
/-! ### `/` ediv -/
@@ -226,7 +242,7 @@ theorem add_mul_emod_self {a b c : Int} : (a + b * c) % c = a % c :=
@[simp] theorem emod_add_emod (m n k : Int) : (m % n + k) % n = (m + k) % n := by
have := (add_mul_emod_self_left (m % n + k) n (m / n)).symm
rwa [Int.add_right_comm, emod_add_ediv] at this
rwa [Int.add_right_comm, emod_add_mul_ediv] at this
@[simp] theorem add_emod_emod (m n k : Int) : (m + n % k) % k = (m + n) % k := by
rw [Int.add_comm, emod_add_emod, Int.add_comm]
@@ -252,7 +268,7 @@ theorem emod_add_cancel_right {m n k : Int} (i) : (m + i) % n = (k + i) % n ↔
theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
conv => lhs; rw [
emod_add_ediv a n, emod_add_ediv' b n, Int.add_mul, Int.mul_add, Int.mul_add,
emod_add_mul_ediv a n, emod_add_ediv_mul b n, Int.add_mul, Int.mul_add, Int.mul_add,
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_emod_self_left,
Int.mul_assoc, add_mul_emod_self_right]
@@ -261,7 +277,7 @@ theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
@[simp] theorem emod_emod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n % k) % m = n % m := by
conv => rhs; rw [ emod_add_ediv n k]
conv => rhs; rw [ emod_add_mul_ediv n k]
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_emod_self_left]
@@ -275,7 +291,7 @@ theorem sub_emod (a b n : Int) : (a - b) % n = (a % n - b % n) % n := by
/-! ### properties of `/` and `%` -/
theorem mul_ediv_cancel_of_emod_eq_zero {a b : Int} (H : a % b = 0) : b * (a / b) = a := by
have := emod_add_ediv a b; rwa [H, Int.zero_add] at this
have := emod_add_mul_ediv a b; rwa [H, Int.zero_add] at this
theorem ediv_mul_cancel_of_emod_eq_zero {a b : Int} (H : a % b = 0) : a / b * b = a := by
rw [Int.mul_comm, mul_ediv_cancel_of_emod_eq_zero H]
@@ -326,11 +342,11 @@ theorem emod_pos_of_not_dvd {a b : Int} (h : ¬ a b) : a = 0 0 < b % a :
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 _ _
_ = x := mul_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) + x % k := (mul_ediv_add_emod _ _).symm
_ < k * (x / k) + k := Int.add_lt_add_left (emod_lt_of_pos x h) _
/-! ### bmod -/

View File

@@ -26,6 +26,10 @@ namespace Int
@[simp high] theorem natCast_eq_zero {n : Nat} : (n : Int) = 0 n = 0 := by omega
instance {n : Nat} [NeZero n] : NeZero (n : Int) := mt Int.natCast_eq_zero.mp (NeZero.ne _)
instance {n : Nat} [NeZero n] : NeZero (no_index (OfNat.ofNat n) : Int) :=
mt Int.natCast_eq_zero.mp (NeZero.ne _)
protected theorem exists_add_of_le {a b : Int} (h : a b) : (c : Nat), b = a + c :=
(b - a).toNat, by omega
@@ -330,7 +334,7 @@ theorem fdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.fdiv b = a / b := by
/-! ### mod definitions -/
theorem tmod_add_tdiv : a b : Int, tmod a b + b * (a.tdiv b) = a
theorem tmod_add_mul_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
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
@@ -347,21 +351,37 @@ theorem tmod_add_tdiv : ∀ a b : Int, tmod a b + b * (a.tdiv b) = a
rw [Int.neg_mul, Int.neg_add]
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
theorem tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
rw [Int.add_comm]; apply tmod_add_tdiv ..
@[deprecated tmod_add_mul_tdiv (since := "2025-09-01")]
def tmod_add_tdiv := @tmod_add_mul_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
theorem mul_tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
rw [Int.add_comm]; apply tmod_add_mul_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
@[deprecated mul_tdiv_add_tmod (since := "2025-09-01")]
def tdiv_add_tmod := @mul_tdiv_add_tmod
theorem tmod_add_tdiv_mul (m k : Int) : tmod m k + m.tdiv k * k = m := by
rw [Int.mul_comm]; apply tmod_add_mul_tdiv
@[deprecated tmod_add_tdiv_mul (since := "2025-09-01")]
def tmod_add_tdiv' := @tmod_add_mul_tdiv
theorem tdiv_mul_add_tmod (m k : Int) : m.tdiv k * k + tmod m k = m := by
rw [Int.mul_comm]; apply mul_tdiv_add_tmod
@[deprecated tdiv_mul_add_tmod (since := "2025-09-01")]
def tdiv_add_tmod' := @tdiv_mul_add_tmod
theorem tmod_def (a b : Int) : tmod a b = a - b * a.tdiv b := by
rw [ Int.add_sub_cancel (tmod a b), tmod_add_tdiv]
rw [ Int.add_sub_cancel (tmod a b), tmod_add_mul_tdiv]
theorem fmod_add_fdiv : a b : Int, a.fmod b + b * a.fdiv b = a
theorem mul_tdiv_self (a b : Int) : b * (a.tdiv b) = a - a.tmod b := by
rw [tmod_def, Int.sub_sub_self]
theorem tdiv_mul_self (a b : Int) : a.tdiv b * b = a - a.tmod b := by
rw [Int.mul_comm, tmod_def, Int.sub_sub_self]
theorem fmod_add_mul_fdiv : a b : Int, a.fmod b + b * a.fdiv b = a
| 0, ofNat _ | 0, -[_+1] => congrArg ofNat <| by simp
| succ _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
| succ m, -[n+1] => by
@@ -378,19 +398,35 @@ theorem fmod_add_fdiv : ∀ a b : Int, a.fmod b + b * a.fdiv b = a
change -((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 ..
@[deprecated fmod_add_mul_fdiv (since := "2025-09-01")]
def fmod_add_fdiv := @fmod_add_mul_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 ..
theorem fmod_add_fdiv_mul (a b : Int) : a.fmod b + (a.fdiv b) * b = a := by
rw [Int.mul_comm]; exact fmod_add_mul_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 ..
@[deprecated fmod_add_fdiv_mul (since := "2025-09-01")]
def fmod_add_fdiv' := @fmod_add_fdiv_mul
theorem mul_fdiv_add_fmod (a b : Int) : b * a.fdiv b + a.fmod b = a := by
rw [Int.add_comm]; exact fmod_add_mul_fdiv ..
@[deprecated mul_fdiv_add_fmod (since := "2025-09-01")]
def fdiv_add_fmod := @mul_fdiv_add_fmod
theorem fdiv_mul_add_fmod (a b : Int) : (a.fdiv b) * b + a.fmod b = a := by
rw [Int.mul_comm]; exact mul_fdiv_add_fmod ..
@[deprecated mul_fdiv_add_fmod (since := "2025-09-01")]
def fdiv_add_fmod' := @mul_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]
rw [ Int.add_sub_cancel (a.fmod b), fmod_add_mul_fdiv]
theorem mul_fdiv_self (a b : Int) : b * (a.fdiv b) = a - a.fmod b := by
rw [fmod_def, Int.sub_sub_self]
theorem fdiv_mul_self (a b : Int) : a.fdiv b * b = a - a.fmod b := by
rw [Int.mul_comm, fmod_def, Int.sub_sub_self]
/-! ### mod equivalences -/
@@ -769,7 +805,7 @@ protected theorem ediv_emod_unique {a b r q : Int} (h : 0 < b) :
a / b = q a % b = r r + b * q = a 0 r r < b := by
constructor
· intro rfl, rfl
exact emod_add_ediv a b, emod_nonneg _ (Int.ne_of_gt h), emod_lt_of_pos _ h
exact emod_add_mul_ediv a b, emod_nonneg _ (Int.ne_of_gt h), emod_lt_of_pos _ h
· intro rfl, hz, hb
constructor
· rw [Int.add_mul_ediv_left r q (Int.ne_of_gt h), ediv_eq_zero_of_lt hz hb]
@@ -793,7 +829,7 @@ theorem neg_ediv {a b : Int} : (-a) / b = -(a / b) - if b a then 0 else b.si
if hb : b = 0 then
simp [hb]
else
conv => lhs; rw [ ediv_add_emod a b]
conv => lhs; rw [ mul_ediv_add_emod a b]
rw [Int.neg_add, Int.mul_neg, mul_add_ediv_left _ _ hb, Int.add_comm]
split <;> rename_i h
· rw [emod_eq_zero_of_dvd h]
@@ -1083,6 +1119,10 @@ theorem emod_natAbs_of_neg {x : Int} (h : x < 0) {n : Nat} (w : n ≠ 0) :
protected theorem ediv_mul_le (a : Int) {b : Int} (H : b 0) : a / b * b a :=
Int.le_of_sub_nonneg <| by rw [Int.mul_comm, emod_def]; apply emod_nonneg _ H
protected theorem lt_ediv_mul (a : Int) {b : Int} (H : 0 < b) : a - b < a / b * b := by
rw [ediv_mul_self, Int.sub_lt_sub_left_iff]
exact emod_lt_of_pos a H
theorem le_of_mul_le_mul_left {a b c : Int} (w : a * b a * c) (h : 0 < a) : b c := by
have w := Int.sub_nonneg_of_le w
rw [ Int.mul_sub] at w
@@ -1173,9 +1213,9 @@ theorem ediv_eq_iff_of_pos {k x y : Int} (h : 0 < k) : x / k = y ↔ y * k ≤ x
theorem add_ediv_of_pos {a b c : Int} (h : 0 < c) :
(a + b) / c = a / c + b / c + if c a % c + b % c then 1 else 0 := by
have h' : c 0 := by omega
conv => lhs; rw [ Int.ediv_add_emod a c]
conv => lhs; rw [ Int.mul_ediv_add_emod a c]
rw [Int.add_assoc, Int.mul_add_ediv_left _ _ h']
conv => lhs; rw [ Int.ediv_add_emod b c]
conv => lhs; rw [ Int.mul_ediv_add_emod b c]
rw [Int.add_comm (a % c), Int.add_assoc, Int.mul_add_ediv_left _ _ h',
Int.add_assoc, Int.add_comm (b % c)]
congr
@@ -1206,7 +1246,7 @@ theorem not_dvd_iff_lt_mul_succ (m : Int) (hn : 0 < n) :
¬n m ( k, n * k < m m < n * (k + 1)) := by
refine fun h ?_, ?_
· rw [dvd_iff_emod_eq_zero, Ne] at h
rw [ emod_add_ediv m n]
rw [ emod_add_mul_ediv m n]
refine m / n, Int.lt_add_of_pos_left _ ?_, ?_
· have := emod_nonneg m (Int.ne_of_gt hn)
omega
@@ -1218,6 +1258,26 @@ theorem not_dvd_iff_lt_mul_succ (m : Int) (hn : 0 < n) :
rw [Int.lt_add_one_iff, Int.not_lt] at h2k
exact h2k h1k
private theorem ediv_ediv_of_pos {x y z : Int} (hy : 0 < y) (hz : 0 < z) :
x / y / z = x / (y * z) := by
rw [eq_comm, Int.ediv_eq_iff_of_pos (Int.mul_pos hy hz)]
constructor
· rw [Int.mul_comm y, Int.mul_assoc]
exact Int.le_trans
(Int.mul_le_mul_of_nonneg_right (Int.ediv_mul_le _ (Int.ne_of_gt hz)) (Int.le_of_lt hy))
(Int.ediv_mul_le x (Int.ne_of_gt hy))
· rw [Int.mul_comm y, Int.mul_assoc, Int.add_mul, Int.mul_comm _ z]
exact Int.lt_mul_of_ediv_lt hy (Int.lt_mul_ediv_self_add hz)
theorem ediv_ediv {x y z : Int} (hy : 0 y) : x / y / z = x / (y * z) := by
rcases y with (_ | a) | a
· simp
· rcases z with (_ | b) | b
· simp
· simp [ediv_ediv_of_pos]
· simp [Int.negSucc_eq, Int.mul_neg, ediv_ediv_of_pos]
· simp at hy
/-! ### tdiv -/
-- `tdiv` analogues of `ediv` lemmas from `Bootstrap.lean`
@@ -1461,7 +1521,7 @@ theorem sign_tmod (a b : Int) : sign (tmod a b) = if b a then 0 else sign a
-- Analogues of statements about `ediv` and `emod` from `Bootstrap.lean`
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
have := tmod_add_mul_tdiv a b; rwa [H, Int.zero_add] at this
theorem tdiv_mul_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : a.tdiv b * b = a := by
rw [Int.mul_comm, mul_tdiv_cancel_of_tmod_eq_zero H]
@@ -2186,7 +2246,7 @@ theorem fmod_add_cancel_right {m n k : Int} (i) : (m + i).fmod n = (k + i).fmod
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,
fmod_add_mul_fdiv a n, fmod_add_fdiv_mul 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_right]
@@ -2195,7 +2255,7 @@ theorem mul_fmod (a b n : Int) : (a * b).fmod n = (a.fmod n * b.fmod n).fmod n :
@[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]
conv => rhs; rw [ fmod_add_mul_fdiv n k]
match k, h with
| _, t, rfl => rw [Int.mul_assoc, add_mul_fmod_self_left]
@@ -2225,7 +2285,7 @@ theorem fmod_eq_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.fmod b = a :=
-- Analogues of properties of `ediv` and `emod` from `Bootstrap.lean`
theorem mul_fdiv_cancel_of_fmod_eq_zero {a b : Int} (H : a.fmod b = 0) : b * (a.fdiv b) = a := by
have := fmod_add_fdiv a b; rwa [H, Int.zero_add] at this
have := fmod_add_mul_fdiv a b; rwa [H, Int.zero_add] at this
theorem fdiv_mul_cancel_of_fmod_eq_zero {a b : Int} (H : a.fmod b = 0) : (a.fdiv b) * b= a := by
rw [Int.mul_comm, mul_fdiv_cancel_of_fmod_eq_zero H]
@@ -2467,9 +2527,9 @@ theorem bdiv_add_bmod (x : Int) (m : Nat) : m * bdiv x m + bmod x m = x := by
ite_self]
· dsimp only
split
· exact ediv_add_emod x m
· exact mul_ediv_add_emod x m
· rw [Int.mul_add, Int.mul_one, Int.add_assoc, Int.add_comm m, Int.sub_add_cancel]
exact ediv_add_emod x m
exact mul_ediv_add_emod x m
theorem bmod_add_bdiv (x : Int) (m : Nat) : bmod x m + m * bdiv x m = x := by
rw [Int.add_comm]; exact bdiv_add_bmod x m
@@ -2726,7 +2786,7 @@ theorem le_bmod {x : Int} {m : Nat} (h : 0 < m) : - (m/2) ≤ Int.bmod x m := by
· exact Int.ne_of_gt (natCast_pos.mpr h)
· simp [Int.not_lt] at w
refine Int.le_trans ?_ (Int.sub_le_sub_right w _)
rw [ ediv_add_emod m 2]
rw [ mul_ediv_add_emod m 2]
generalize (m : Int) / 2 = q
generalize h : (m : Int) % 2 = r at *
rcases v with rfl | rfl
@@ -2887,7 +2947,7 @@ theorem neg_bmod {a : Int} {b : Nat} :
simp only [gt_iff_lt, Nat.zero_lt_succ, Nat.mul_pos_iff_of_pos_left, Int.natCast_mul,
cast_ofNat_Int, Int.not_lt] at *
rw [Int.mul_dvd_mul_iff_left (by omega)]
have := ediv_add_emod a (2 * c)
have := mul_ediv_add_emod a (2 * c)
rw [(by omega : a % (2 * c) = c)] at this
rw [ this]
apply Int.dvd_add _ (by simp)

View File

@@ -40,7 +40,7 @@ theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
theorem neg_negSucc (n : Nat) : -(-[n+1]) = succ n := rfl
@[simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = ((n + 1 : Nat) : Int) := rfl
theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
@@ -566,6 +566,9 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 b = 0 := by
protected theorem mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 :=
Or.rec a0 b0 Int.mul_eq_zero.mp
instance {a b : Int} [NeZero a] [NeZero b] : NeZero (a * b) :=
Int.mul_ne_zero (NeZero.ne _) (NeZero.ne _)
@[simp] protected theorem mul_ne_zero_iff {a b : Int} : a * b 0 a 0 b 0 := by
rw [ne_eq, Int.mul_eq_zero, not_or, ne_eq]

View File

@@ -12,9 +12,12 @@ public import Init.Data.Int.Lemmas
public import Init.Data.Int.LemmasAux
public import Init.Data.Int.DivMod.Bootstrap
public import Init.Data.Int.Cooper
public import all Init.Data.Int.Gcd
public import Init.Data.Int.Gcd
import all Init.Data.Int.Gcd
public import Init.Data.RArray
public import all Init.Data.AC
public import Init.Data.AC
import all Init.Data.AC
import Init.LawfulBEqTactics
public section
@@ -37,7 +40,7 @@ inductive Expr where
| neg (a : Expr)
| mulL (k : Int) (a : Expr)
| mulR (a : Expr) (k : Int)
deriving Inhabited, BEq
deriving Inhabited, @[expose] BEq
@[expose]
def Expr.denote (ctx : Context) : Expr Int
@@ -52,7 +55,7 @@ def Expr.denote (ctx : Context) : Expr → Int
inductive Poly where
| num (k : Int)
| add (k : Int) (v : Var) (p : Poly)
deriving BEq
deriving @[expose] BEq, ReflBEq, LawfulBEq
@[expose]
protected noncomputable def Poly.beq' (p₁ : Poly) : Poly Bool :=
@@ -245,7 +248,7 @@ def cmod (a b : Int) : Int :=
theorem cdiv_add_cmod (a b : Int) : b*(cdiv a b) + cmod a b = a := by
unfold cdiv cmod
have := Int.ediv_add_emod (-a) b
have := Int.mul_ediv_add_emod (-a) b
have := congrArg (Neg.neg) this
simp at this
conv => rhs; rw[ this]
@@ -270,7 +273,7 @@ private abbrev div_mul_cancel_of_mod_zero :=
theorem cdiv_eq_div_of_divides {a b : Int} (h : a % b = 0) : a/b = cdiv a b := by
replace h := div_mul_cancel_of_mod_zero h
have hz : a % b = 0 := by
have := Int.ediv_add_emod a b
have := Int.mul_ediv_add_emod a b
conv at this => rhs; rw [ Int.add_zero a]
rw [Int.mul_comm, h] at this
exact Int.add_left_cancel this
@@ -377,8 +380,11 @@ def Poly.mul (p : Poly) (k : Int) : Poly :=
p₁)
fuel
@[expose] noncomputable def Poly.combine_mul_k (a b : Int) : Poly Poly Poly :=
combine_mul_k' hugeFuel a b
@[expose] noncomputable def Poly.combine_mul_k (a b : Int) (p₁ p₂ : Poly) : Poly :=
Bool.rec
(Bool.rec (combine_mul_k' hugeFuel a b p₁ p₂) (p₁.mul_k a) (Int.beq' b 0))
(p₂.mul_k b)
(Int.beq' a 0)
@[simp] theorem Poly.denote_mul (ctx : Context) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
simp [mul]
@@ -422,34 +428,36 @@ theorem Poly.denote_combine (ctx : Context) (p₁ p₂ : Poly) : (p₁.combine p
theorem Poly.denote_combine_mul_k (ctx : Context) (a b : Int) (p₁ p₂ : Poly) : (p₁.combine_mul_k a b p₂).denote ctx = a * p₁.denote ctx + b * p₂.denote ctx := by
unfold combine_mul_k
cases h₁ : Int.beq' a 0 <;> simp at h₁ <;> simp [*]
cases h₂ : Int.beq' b 0 <;> simp at h₂ <;> simp [*]
generalize hugeFuel = fuel
induction fuel generalizing p₁ p₂
next => show ((p₁.mul a).append (p₂.mul b)).denote ctx = _; simp
next fuel ih =>
cases p₁ <;> cases p₂ <;> simp [combine_mul_k']
next k₁ k₂ v₂ p₂ =>
show _ + (combine_mul_k' fuel a b (.num k₁) p₂).denote ctx = _
simp [ih, Int.mul_assoc]
next k₁ v₁ p₁ k₂ =>
show _ + (combine_mul_k' fuel a b p₁ (.num k₂)).denote ctx = _
simp [ih, Int.mul_assoc]
next k₁ v₁ p₁ k₂ v₂ p₂ =>
cases h₁ : Nat.beq v₁ v₂ <;> simp
next =>
cases h₂ : Nat.blt v₂ v₁ <;> simp
next =>
show _ + (combine_mul_k' fuel a b (add k₁ v₁ p₁) p₂).denote ctx = _
simp [ih, Int.mul_assoc]
next =>
show _ + (combine_mul_k' fuel a b p₁ (add k₂ v₂ p₂)).denote ctx = _
simp [ih, Int.mul_assoc]
next =>
simp at h₁; subst v₂
cases h₂ : (a * k₁ + b * k₂).beq' 0 <;> simp
next =>
cases p₁ <;> cases p₂ <;> simp [combine_mul_k']
next k₁ k₂ v₂ p₂ =>
show _ + (combine_mul_k' fuel a b (.num k₁) p₂).denote ctx = _
simp [ih, Int.mul_assoc]
next k₁ v₁ p₁ k₂ =>
show _ + (combine_mul_k' fuel a b p₁ (.num k₂)).denote ctx = _
simp [ih, Int.mul_assoc]
next k₁ v₁ p₁ k₂ v₂ p₂ =>
cases h₁ : Nat.beq v₁ v₂ <;> simp
next =>
cases h₂ : Nat.blt v₂ v₁ <;> simp
next =>
show _ + (combine_mul_k' fuel a b (add k₁ v₁ p₁) p₂).denote ctx = _
simp [ih, Int.mul_assoc]
next =>
show _ + (combine_mul_k' fuel a b p₁ (add k₂ v₂ p₂)).denote ctx = _
simp [ih, Int.mul_assoc]
next =>
simp at h₁; subst v₂
cases h₂ : (a * k₁ + b * k₂).beq' 0 <;> simp
next =>
show a * k₁ * v₁.denote ctx + (b * k₂ * v₁.denote ctx + (combine_mul_k' fuel a b p₁ p₂).denote ctx) = _
simp [ih, Int.mul_assoc]
next =>
next =>
simp at h₂
show (combine_mul_k' fuel a b p₁ p₂).denote ctx = _
simp [ih, Int.mul_assoc, Int.add_mul, h₂]
@@ -518,18 +526,6 @@ theorem Expr.denote_norm (ctx : Context) (e : Expr) : e.norm.denote ctx = e.deno
simp [norm, toPoly', Expr.denote_toPoly'_go]
attribute [local simp] Expr.denote_norm
instance : LawfulBEq Poly where
eq_of_beq {a} := by
induction a <;> intro b <;> cases b <;> simp_all! [BEq.beq]
next ih =>
intro _ _ h
exact ih h
rfl := by
intro a
induction a <;> simp! [BEq.beq]
assumption
attribute [local simp] Poly.denote'_eq_denote
theorem Expr.eq_of_norm_eq (ctx : Context) (e : Expr) (p : Poly) (h : e.norm.beq' p) : e.denote ctx = p.denote' ctx := by
@@ -1751,7 +1747,7 @@ theorem cooper_right_split_dvd (ctx : Context) (p₁ p₂ : Poly) (k : Nat) (b :
intros; subst b p'; simp; assumption
private theorem one_emod_eq_one {a : Int} (h : a > 1) : 1 % a = 1 := by
have aux₁ := Int.ediv_add_emod 1 a
have aux₁ := Int.mul_ediv_add_emod 1 a
have : 1 / a = 0 := Int.ediv_eq_zero_of_lt (by decide) h
simp [this] at aux₁
assumption
@@ -1778,7 +1774,7 @@ private theorem ex_of_dvd {α β a b d x : Int}
rw [Int.mul_emod, aux₁, Int.one_mul, Int.emod_emod] at this
assumption
have : x = (x / d)*d + (- α * b) % d := by
conv => lhs; rw [ Int.ediv_add_emod x d]
conv => lhs; rw [ Int.mul_ediv_add_emod x d]
rw [Int.mul_comm, this]
exists x / d
@@ -1861,7 +1857,7 @@ theorem cooper_unsat (ctx : Context) (p₁ p₂ p₃ : Poly) (d : Int) (α β :
exact cooper_unsat' h₁ h₂ h₃ h₄ h₅ h₆
theorem ediv_emod (x y : Int) : -1 * x + y * (x / y) + x % y = 0 := by
rw [Int.add_assoc, Int.ediv_add_emod x y, Int.add_comm]
rw [Int.add_assoc, Int.mul_ediv_add_emod x y, Int.add_comm]
simp
rw [Int.add_neg_eq_sub, Int.sub_self]
@@ -2196,6 +2192,9 @@ theorem mod_eq (a b k : Int) (h : b = k) : a % b = a % k := by simp [*]
theorem div_eq' (a b b' k : Int) (h₁ : b = b') (h₂ : k == a/b') : a / b = k := by simp_all
theorem mod_eq' (a b b' k : Int) (h₁ : b = b') (h₂ : k == a%b') : a % b = k := by simp_all
theorem pow_eq (a : Int) (b : Nat) (a' b' k : Int) (h₁ : a = a') (h₂ : b = b') (h₃ : k == a'^b'.toNat) : a^b = k := by
simp [ h₁, h₂] at h₃; simp [h₃]
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by

View File

@@ -701,10 +701,13 @@ theorem toNat_sub_toNat_neg : ∀ n : Int, ↑n.toNat - ↑(-n).toNat = n
| (_+1:Nat) => Nat.add_zero _
| -[_+1] => Nat.zero_add _
@[simp] theorem toNat_neg_nat : n : Nat, (-(n : Int)).toNat = 0
@[simp] theorem toNat_neg_natCast : n : Nat, (-(n : Int)).toNat = 0
| 0 => rfl
| _+1 => rfl
@[deprecated toNat_neg_natCast (since := "2025-08-29")]
theorem toNat_neg_nat : n : Nat, (-(n : Int)).toNat = 0 := toNat_neg_natCast
/-! ### toNat? -/
theorem mem_toNat? : {a : Int} {n : Nat}, toNat? a = some n a = n

View File

@@ -21,6 +21,11 @@ protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
rw [Int.mul_comm, Int.pow_succ]
protected theorem pow_add (a : Int) (m n : Nat) : a ^ (m + n) = a ^ m * a ^ n := by
induction n with
| zero => rw [Nat.add_zero, Int.pow_zero, Int.mul_one]
| succ _ ih => rw [Nat.add_succ, Int.pow_succ, Int.pow_succ, ih, Int.mul_assoc]
protected theorem zero_pow {n : Nat} (h : n 0) : (0 : Int) ^ n = 0 := by
match n, h with
| n + 1, _ => simp [Int.pow_succ]
@@ -43,6 +48,8 @@ protected theorem pow_ne_zero {n : Int} {m : Nat} : n ≠ 0 → n ^ m ≠ 0 := b
| zero => simp
| succ m ih => exact fun h => Int.mul_ne_zero (ih h) h
instance {n : Int} {m : Nat} [NeZero n] : NeZero (n ^ m) := Int.pow_ne_zero (NeZero.ne _)
@[deprecated Nat.pow_le_pow_left (since := "2025-02-17")]
abbrev pow_le_pow_of_le_left := @Nat.pow_le_pow_left

View File

@@ -19,6 +19,7 @@ universe v u v' u'
section ULiftT
/-- `ULiftT.{v, u}` shrinks a monad on `Type max u v` to a monad on `Type u`. -/
@[expose] -- for codegen
def ULiftT (n : Type max u v Type v') (α : Type u) := n (ULift.{v} α)
/-- Returns the underlying `n`-monadic representation of a `ULiftT n α` value. -/

View File

@@ -139,7 +139,7 @@ def Iter.Partial.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id
(init : γ) (it : Iter.Partial (α := α) β) : γ :=
ForIn.forIn (m := Id) it init (fun x acc => ForInStep.yield (f acc x))
@[always_inline, inline, inherit_doc IterM.size]
@[always_inline, inline, expose, inherit_doc IterM.size]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorSize α Id]
(it : Iter (α := α) β) : Nat :=
(IteratorSize.size it.toIterM).run.down

View File

@@ -6,8 +6,10 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.Attach
public import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Combinators.Attach
import all Init.Data.Iterators.Combinators.Attach
public import Init.Data.Iterators.Combinators.Monadic.Attach
import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import Init.Data.Array.Attach

View File

@@ -6,7 +6,8 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Combinators.Monadic.Attach
import all Init.Data.Iterators.Combinators.Monadic.Attach
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public section
@@ -56,6 +57,6 @@ theorem IterM.map_unattach_toArray_attachWith [Iterator α m β] [Monad m] [Mona
[LawfulMonad m] [LawfulIteratorCollect α m m] :
(·.map Subtype.val) <$> (it.attachWith P hP).toArray = it.toArray := by
rw [ toArray_toList, toArray_toList, map_unattach_toList_attachWith (it := it) (hP := hP)]
simp [-map_unattach_toList_attachWith]
simp [-map_unattach_toList_attachWith, -IterM.toArray_toList]
end Std.Iterators

View File

@@ -9,7 +9,8 @@ prelude
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.Data.Iterators.Combinators.Monadic.FilterMap
public import Init.Data.Iterators.Lemmas.Consumers.Monadic
public import all Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Collect
import all Init.Data.Iterators.Consumers.Monadic.Collect
public section

View File

@@ -6,7 +6,8 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.Monadic.ULift
public import Init.Data.Iterators.Combinators.Monadic.ULift
import all Init.Data.Iterators.Combinators.Monadic.ULift
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public section

View File

@@ -6,7 +6,8 @@ Authors: Paul Reichert
module
prelude
public import all Init.Data.Iterators.Combinators.ULift
public import Init.Data.Iterators.Combinators.ULift
import all Init.Data.Iterators.Combinators.ULift
public import Init.Data.Iterators.Lemmas.Combinators.Monadic.ULift
public import Init.Data.Iterators.Lemmas.Consumers.Collect
@@ -52,6 +53,6 @@ theorem Iter.toArray_uLift [Iterator α Id β] {it : Iter (α := α) β}
[LawfulIteratorCollect α Id Id] :
it.uLift.toArray = it.toArray.map ULift.up := by
rw [ toArray_toList, toArray_toList, toList_uLift]
simp
simp [-toArray_toList]
end Std.Iterators

View File

@@ -8,8 +8,10 @@ module
prelude
public import Init.Data.Iterators.Lemmas.Basic
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public import all Init.Data.Iterators.Consumers.Access
public import all Init.Data.Iterators.Consumers.Collect
public import Init.Data.Iterators.Consumers.Access
import all Init.Data.Iterators.Consumers.Access
public import Init.Data.Iterators.Consumers.Collect
import all Init.Data.Iterators.Consumers.Collect
public section
@@ -42,11 +44,13 @@ theorem IterM.toListRev_toIter {α β} [Iterator α Id β] [Finite α Id]
it.toIter.toListRev = it.toListRev.run :=
(rfl)
@[simp]
theorem Iter.toList_toArray {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
it.toArray.toList = it.toList := by
simp [toArray_eq_toArray_toIterM, toList_eq_toList_toIterM, IterM.toList_toArray]
@[simp]
theorem Iter.toArray_toList {α β} [Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id]
[LawfulIteratorCollect α Id Id] {it : Iter (α := α) β} :
it.toList.toArray = it.toArray := by

View File

@@ -8,9 +8,13 @@ module
prelude
public import Init.Control.Lawful.MonadLift.Instances
public import Init.Data.Iterators.Lemmas.Consumers.Collect
public import all Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public import all Init.Data.Iterators.Consumers.Loop
public import all Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
import all Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop
public import Init.Data.Iterators.Consumers.Loop
import all Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.Consumers.Monadic.Collect
import all Init.Data.Iterators.Consumers.Monadic.Collect
import Init.Data.Array.Monadic
public section
@@ -40,6 +44,20 @@ theorem Iter.forIn_eq {α β : Type w} [Iterator α Id β] [Finite α Id]
f out acc) := by
simp [ForIn.forIn, forIn'_eq, -forIn'_eq_forIn]
@[congr] theorem Iter.forIn'_congr {α β : Type w}
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
{ita itb : Iter (α := α) β} (w : ita = itb)
{b b' : γ} (hb : b = b')
{f : (a' : β) _ γ Id (ForInStep γ)}
{g : (a' : β) _ γ Id (ForInStep γ)}
(h : a m b, f a (by simpa [w] using m) b = g a m b) :
letI : ForIn' Id (Iter (α := α) β) β _ := Iter.instForIn'
forIn' ita b f = forIn' itb b' g := by
subst_eqs
simp only [ funext_iff] at h
rw [ h]
rfl
theorem Iter.forIn'_eq_forIn'_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type w Type w''} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -185,6 +203,13 @@ theorem Iter.mem_toList_iff_isPlausibleIndirectOutput {α β} [Iterator α Id β
obtain step, h₁, rfl := h₁
simp [heq, IterStep.successor] at h₁
theorem Iter.mem_toArray_iff_isPlausibleIndirectOutput {α β} [Iterator α Id β]
[IteratorCollect α Id Id] [Finite α Id]
[LawfulIteratorCollect α Id Id] [LawfulDeterministicIterator α Id]
{it : Iter (α := α) β} {out : β} :
out it.toArray it.IsPlausibleIndirectOutput out := by
rw [ Iter.toArray_toList, List.mem_toArray, mem_toList_iff_isPlausibleIndirectOutput]
theorem Iter.forIn'_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -219,6 +244,17 @@ theorem Iter.forIn'_toList {α β : Type w} [Iterator α Id β]
simp only [ihs h (f := fun out h acc => f out (this h) acc)]
· simp
theorem Iter.forIn'_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulDeterministicIterator α Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : (out : β) _ γ m (ForInStep γ)} :
letI : ForIn' m (Iter (α := α) β) β _ := Iter.instForIn'
ForIn'.forIn' it.toArray init f = ForIn'.forIn' it init (fun out h acc => f out (Iter.mem_toArray_iff_isPlausibleIndirectOutput.mpr h) acc) := by
simp only [ Iter.toArray_toList (it := it), List.forIn'_toArray, Iter.forIn'_toList]
theorem Iter.forIn'_eq_forIn'_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -231,6 +267,18 @@ theorem Iter.forIn'_eq_forIn'_toList {α β : Type w} [Iterator α Id β]
simp only [forIn'_toList]
congr
theorem Iter.forIn'_eq_forIn'_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
[LawfulDeterministicIterator α Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : (out : β) _ γ m (ForInStep γ)} :
letI : ForIn' m (Iter (α := α) β) β _ := Iter.instForIn'
ForIn'.forIn' it init f = ForIn'.forIn' it.toArray init (fun out h acc => f out (Iter.mem_toArray_iff_isPlausibleIndirectOutput.mp h) acc) := by
simp only [forIn'_toArray]
congr
theorem Iter.forIn_toList {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -257,6 +305,15 @@ theorem Iter.forIn_toList {α β : Type w} [Iterator α Id β]
rw [ihs h]
· simp
theorem Iter.forIn_toArray {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{γ : Type x} {it : Iter (α := α) β} {init : γ}
{f : β γ m (ForInStep γ)} :
ForIn.forIn it.toArray init f = ForIn.forIn it init f := by
simp only [ Iter.toArray_toList, List.forIn_toArray, forIn_toList]
theorem Iter.foldM_eq_forIn {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
{m : Type x Type x'} [Monad m] [IteratorLoop α Id m] {f : γ β m γ}
{init : γ} {it : Iter (α := α) β} :
@@ -298,6 +355,14 @@ theorem Iter.foldlM_toList {α β : Type w} {γ : Type x} [Iterator α Id β] [F
rw [Iter.foldM_eq_forIn, Iter.forIn_toList]
simp only [List.forIn_yield_eq_foldlM, id_map']
theorem Iter.foldlM_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
{m : Type x Type x'} [Monad m] [LawfulMonad m] [IteratorLoop α Id m]
[LawfulIteratorLoop α Id m] [IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{f : γ β m γ} {init : γ} {it : Iter (α := α) β} :
it.toArray.foldlM (init := init) f = it.foldM (init := init) f := by
rw [Iter.foldM_eq_forIn, Iter.forIn_toArray]
simp only [Array.forIn_yield_eq_foldlM, id_map']
theorem IterM.forIn_eq_foldM {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type x Type x'} [Monad m] [LawfulMonad m]
[IteratorLoop α Id m] [LawfulIteratorLoop α Id m]
@@ -321,6 +386,12 @@ theorem Iter.fold_eq_foldM {α β : Type w} {γ : Type x} [Iterator α Id β]
it.fold (init := init) f = (it.foldM (m := Id) (init := init) (pure <| f · ·)).run := by
simp [foldM_eq_forIn, fold_eq_forIn]
theorem Iter.fold_eq_fold_toIterM {α β : Type w} {γ : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{f : γ β γ} {init : γ} {it : Iter (α := α) β} :
it.fold (init := init) f = (it.toIterM.fold (init := init) f).run := by
rw [fold_eq_foldM, foldM_eq_foldM_toIterM, IterM.fold_eq_foldM]
@[simp]
theorem Iter.forIn_pure_yield_eq_fold {α β : Type w} {γ : Type x} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id] {f : β γ γ} {init : γ}
@@ -341,6 +412,38 @@ theorem Iter.fold_eq_match_step {α β : Type w} {γ : Type x} [Iterator α Id
generalize it.step = step
cases step using PlausibleIterStep.casesOn <;> simp
-- The argument `f : γ₁ → γ₂` is intentionally explicit, as it is sometimes not found by unification.
theorem Iter.fold_hom [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
{it : Iter (α := α) β}
(f : γ₁ γ₂) {g₁ : γ₁ β γ₁} {g₂ : γ₂ β γ₂} {init : γ₁}
(H : x y, g₂ (f x) y = f (g₁ x y)) :
it.fold g₂ (f init) = f (it.fold g₁ init) := by
-- We cannot reduce to `IterM.fold_hom` because `IterM.fold` is necessarily more restrictive
-- w.r.t. the universe of the output.
induction it using Iter.inductSteps generalizing init with | step it ihy ihs =>
rw [fold_eq_match_step, fold_eq_match_step]
split
· rw [H, ihy _]
· rw [ihs _]
· simp
theorem Iter.toList_eq_fold {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
it.toList = it.fold (init := []) (fun l out => l ++ [out]) := by
rw [Iter.toList_eq_toList_toIterM, IterM.toList_eq_fold, Iter.fold_eq_fold_toIterM]
theorem Iter.toArray_eq_fold {α β : Type w} [Iterator α Id β]
[Finite α Id] [IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{it : Iter (α := α) β} :
it.toArray = it.fold (init := #[]) (fun xs out => xs.push out) := by
simp only [ toArray_toList, toList_eq_fold]
rw [ fold_hom (List.toArray)]
simp
@[simp]
theorem Iter.foldl_toList {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
@@ -349,6 +452,14 @@ theorem Iter.foldl_toList {α β : Type w} {γ : Type x} [Iterator α Id β] [Fi
it.toList.foldl (init := init) f = it.fold (init := init) f := by
rw [fold_eq_foldM, List.foldl_eq_foldlM, Iter.foldlM_toList]
@[simp]
theorem Iter.foldl_toArray {α β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] [LawfulIteratorLoop α Id Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]
{f : γ β γ} {init : γ} {it : Iter (α := α) β} :
it.toArray.foldl (init := init) f = it.fold (init := init) f := by
rw [fold_eq_foldM, Array.foldl_eq_foldlM, Iter.foldlM_toArray]
@[simp]
theorem Iter.size_toArray_eq_size {α β : Type w} [Iterator α Id β] [Finite α Id]
[IteratorCollect α Id Id] [LawfulIteratorCollect α Id Id]

View File

@@ -8,7 +8,8 @@ module
prelude
public import Init.Data.Array.Lemmas
public import Init.Data.Iterators.Lemmas.Monadic.Basic
public import all Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Collect
import all Init.Data.Iterators.Consumers.Monadic.Collect
public section
@@ -66,15 +67,17 @@ theorem IterM.toArray_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β]
rw [IterM.DefaultConsumers.toArrayMapped_eq_match_step]
simp [bind_pure_comp, pure_bind]
@[simp]
theorem IterM.toList_toArray [Monad m] [Iterator α m β] [Finite α m] [IteratorCollect α m m]
{it : IterM (α := α) m β} :
Array.toList <$> it.toArray = it.toList := by
simp [IterM.toList]
@[simp]
theorem IterM.toArray_toList [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] {it : IterM (α := α) m β} :
List.toArray <$> it.toList = it.toArray := by
simp [IterM.toList]
simp [IterM.toList, -toList_toArray]
theorem IterM.toList_eq_match_step [Monad m] [LawfulMonad m] [Iterator α m β] [Finite α m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m] {it : IterM (α := α) m β} :
@@ -152,6 +155,6 @@ theorem LawfulIteratorCollect.toList_eq {α β : Type w} {m : Type w → Type w'
[hl : LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toList = (letI : IteratorCollect α m m := .defaultImplementation; it.toList) := by
simp [IterM.toList, toArray_eq]
simp [IterM.toList, toArray_eq, -IterM.toList_toArray]
end Std.Iterators

View File

@@ -7,7 +7,8 @@ module
prelude
public import Init.Data.Iterators.Lemmas.Consumers.Monadic.Collect
public import all Init.Data.Iterators.Consumers.Monadic.Loop
public import Init.Data.Iterators.Consumers.Monadic.Loop
import all Init.Data.Iterators.Consumers.Monadic.Loop
public section
@@ -59,6 +60,20 @@ theorem IterM.forIn_eq {α β : Type w} {m : Type w → Type w'} [Iterator α m
IteratorLoop.wellFounded_of_finite it init _ (fun _ => id) (fun out _ acc => (·, .intro) <$> f out acc) := by
simp only [ForIn.forIn, forIn'_eq]
@[congr] theorem IterM.forIn'_congr {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Finite α m] [IteratorLoop α m m]
{ita itb : IterM (α := α) m β} (w : ita = itb)
{b b' : γ} (hb : b = b')
{f : (a' : β) _ γ m (ForInStep γ)}
{g : (a' : β) _ γ m (ForInStep γ)}
(h : a m b, f a (by simpa [w] using m) b = g a m b) :
letI : ForIn' m (IterM (α := α) m β) β _ := IterM.instForIn'
forIn' ita b f = forIn' itb b' g := by
subst_eqs
simp only [ funext_iff] at h
rw [ h]
rfl
theorem IterM.forIn'_eq_match_step {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] {n : Type w Type w''} [Monad m] [Monad n] [LawfulMonad n]
[IteratorLoop α m n] [LawfulIteratorLoop α m n]
@@ -199,6 +214,23 @@ theorem IterM.fold_eq_match_step {α β γ : Type w} {m : Type w → Type w'} [I
intro step
cases step using PlausibleIterStep.casesOn <;> simp
-- The argument `f : γ₁ → γ₂` is intentionally explicit, as it is sometimes not found by unification.
theorem IterM.fold_hom {m : Type w Type w'} [Iterator α m β] [Finite α m]
[Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
{it : IterM (α := α) m β}
(f : γ₁ γ₂) {g₁ : γ₁ β γ₁} {g₂ : γ₂ β γ₂} {init : γ₁}
(H : x y, g₂ (f x) y = f (g₁ x y)) :
it.fold g₂ (f init) = f <$> (it.fold g₁ init) := by
induction it using IterM.inductSteps generalizing init with | step it ihy ihs =>
rw [fold_eq_match_step, fold_eq_match_step, map_eq_pure_bind, bind_assoc]
apply bind_congr
intro step
rw [bind_pure_comp]
split
· rw [H, ihy _]
· rw [ihs _]
· simp
theorem IterM.toList_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
@@ -222,6 +254,15 @@ theorem IterM.toList_eq_fold {α β : Type w} {m : Type w → Type w'} [Iterator
simp [ihs h]
· simp
theorem IterM.toArray_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β]
[Finite α m] [Monad m] [LawfulMonad m] [IteratorLoop α m m] [LawfulIteratorLoop α m m]
[IteratorCollect α m m] [LawfulIteratorCollect α m m]
{it : IterM (α := α) m β} :
it.toArray = it.fold (init := #[]) (fun xs out => xs.push out) := by
simp only [ toArray_toList, toList_eq_fold]
rw [ fold_hom]
simp
theorem IterM.drain_eq_fold {α β : Type w} {m : Type w Type w'} [Iterator α m β] [Finite α m]
[Monad m] [IteratorLoop α m m] {it : IterM (α := α) m β} :
it.drain = it.fold (init := PUnit.unit) (fun _ _ => .unit) :=

View File

@@ -6,7 +6,8 @@ Authors: Mario Carneiro
module
prelude
public import all Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
public import Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
import all Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
public import Init.Data.List.Count
public import Init.Data.Subtype.Basic
public import Init.BinderNameHint
@@ -166,14 +167,14 @@ theorem attachWith_map_subtype_val {p : α → Prop} {l : List α} (H : ∀ a
(l.attachWith p H).map Subtype.val = l :=
(attachWith_map_val _).trans (List.map_id _)
@[simp, grind]
@[simp, grind ]
theorem mem_attach (l : List α) : x, x l.attach
| a, h => by
have := mem_map.1 (by rw [attach_map_subtype_val]; exact h)
rcases this with _, _, m, rfl
exact m
@[simp, grind]
@[simp, grind =]
theorem mem_attachWith {l : List α} {q : α Prop} (H) (x : {x // q x}) :
x l.attachWith q H x.1 l := by
induction l with
@@ -191,12 +192,13 @@ theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H b} :
b pmap f l H (a : _) (h : a l), f a (H a h) = b := by
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
@[grind]
theorem mem_pmap_of_mem {p : α Prop} {f : a, p a β} {l H} {a} (h : a l) :
f a (H a h) pmap f l H := by
rw [mem_pmap]
exact a, h, rfl
grind_pattern mem_pmap_of_mem => _ pmap f l H, a l
@[simp, grind =]
theorem length_pmap {p : α Prop} {f : a, p a β} {l H} : (pmap f l H).length = l.length := by
induction l
@@ -369,13 +371,13 @@ theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
xs.attach.tail = xs.tail.attach.map (fun x, h => x, mem_of_mem_tail h) := by
cases xs <;> simp
@[grind]
@[grind =]
theorem foldl_pmap {l : List α} {P : α Prop} {f : (a : α) P a β}
(H : (a : α), a l P a) (g : γ β γ) (x : γ) :
(l.pmap f H).foldl g x = l.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
rw [pmap_eq_map_attach, foldl_map]
@[grind]
@[grind =]
theorem foldr_pmap {l : List α} {P : α Prop} {f : (a : α) P a β}
(H : (a : α), a l P a) (g : β γ γ) (x : γ) :
(l.pmap f H).foldr g x = l.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by

View File

@@ -80,17 +80,17 @@ namespace List
/-! ### length -/
@[simp, grind] theorem length_nil : length ([] : List α) = 0 :=
@[simp, grind =] theorem length_nil : length ([] : List α) = 0 :=
rfl
@[simp] theorem length_singleton {a : α} : length [a] = 1 := rfl
@[simp, grind] theorem length_cons {a : α} {as : List α} : (cons a as).length = as.length + 1 :=
@[simp, grind =] theorem length_cons {a : α} {as : List α} : (cons a as).length = as.length + 1 :=
rfl
/-! ### set -/
@[simp, grind] theorem length_set {as : List α} {i : Nat} {a : α} : (as.set i a).length = as.length := by
@[simp, grind =] theorem length_set {as : List α} {i : Nat} {a : α} : (as.set i a).length = as.length := by
induction as generalizing i with
| nil => rfl
| cons x xs ih =>
@@ -101,8 +101,8 @@ namespace List
/-! ### foldl -/
-- As `List.foldl` is defined in `Init.Prelude`, we write the basic simplification lemmas here.
@[simp, grind] theorem foldl_nil : [].foldl f b = b := rfl
@[simp, grind] theorem foldl_cons {l : List α} {f : β α β} {b : β} : (a :: l).foldl f b = l.foldl f (f b a) := rfl
@[simp, grind =] theorem foldl_nil : [].foldl f b = b := rfl
@[simp, grind =] theorem foldl_cons {l : List α} {f : β α β} {b : β} : (a :: l).foldl f b = l.foldl f (f b a) := rfl
/-! ### concat -/
@@ -332,7 +332,7 @@ def getLast? : List α → Option α
| [] => none
| a::as => some (getLast (a::as) (fun h => List.noConfusion h))
@[simp, grind] theorem getLast?_nil : @getLast? α [] = none := rfl
@[simp, grind =] theorem getLast?_nil : @getLast? α [] = none := rfl
/-! ### getLastD -/
@@ -365,7 +365,7 @@ Returns the first element of a non-empty list.
def head : (as : List α) as [] α
| a::_, _ => a
@[simp, grind] theorem head_cons {a : α} {l : List α} {h} : head (a::l) h = a := rfl
@[simp, grind =] theorem head_cons {a : α} {l : List α} {h} : head (a::l) h = a := rfl
/-! ### head? -/
@@ -383,8 +383,8 @@ def head? : List α → Option α
| [] => none
| a::_ => some a
@[simp, grind] theorem head?_nil : head? ([] : List α) = none := rfl
@[simp, grind] theorem head?_cons {a : α} {l : List α} : head? (a::l) = some a := rfl
@[simp, grind =] theorem head?_nil : head? ([] : List α) = none := rfl
@[simp, grind =] theorem head?_cons {a : α} {l : List α} : head? (a::l) = some a := rfl
/-! ### headD -/
@@ -420,8 +420,8 @@ def tail : List α → List α
| [] => []
| _::as => as
@[simp, grind] theorem tail_nil : tail ([] : List α) = [] := rfl
@[simp, grind] theorem tail_cons {a : α} {as : List α} : tail (a::as) = as := rfl
@[simp, grind =] theorem tail_nil : tail ([] : List α) = [] := rfl
@[simp, grind =] theorem tail_cons {a : α} {as : List α} : tail (a::as) = as := rfl
/-! ### tail? -/
@@ -441,8 +441,8 @@ def tail? : List α → Option (List α)
| [] => none
| _::as => some as
@[simp, grind] theorem tail?_nil : tail? ([] : List α) = none := rfl
@[simp, grind] theorem tail?_cons {a : α} {l : List α} : tail? (a::l) = some l := rfl
@[simp, grind =] theorem tail?_nil : tail? ([] : List α) = none := rfl
@[simp, grind =] theorem tail?_cons {a : α} {l : List α} : tail? (a::l) = some l := rfl
/-! ### tailD -/
@@ -475,23 +475,8 @@ We define the basic functional programming operations on `List`:
/-! ### map -/
/--
Applies a function to each element of the list, returning the resulting list of values.
`O(|l|)`.
Examples:
* `[a, b, c].map f = [f a, f b, f c]`
* `[].map Nat.succ = []`
* `["one", "two", "three"].map (·.length) = [3, 3, 5]`
* `["one", "two", "three"].map (·.reverse) = ["eno", "owt", "eerht"]`
-/
@[specialize] def map (f : α β) : (l : List α) List β
| [] => []
| a::as => f a :: map f as
@[simp, grind] theorem map_nil {f : α β} : map f [] = [] := rfl
@[simp, grind] theorem map_cons {f : α β} {a : α} {l : List α} : map f (a :: l) = f a :: map f l := rfl
@[simp, grind =] theorem map_nil {f : α β} : map f [] = [] := rfl
@[simp, grind =] theorem map_cons {f : α β} {a : α} {l : List α} : map f (a :: l) = f a :: map f l := rfl
/-! ### filter -/
@@ -511,7 +496,7 @@ def filter (p : α → Bool) : (l : List α) → List α
| true => a :: filter p as
| false => filter p as
@[simp, grind] theorem filter_nil {p : α Bool} : filter p [] = [] := rfl
@[simp, grind =] theorem filter_nil {p : α Bool} : filter p [] = [] := rfl
/-! ### filterMap -/
@@ -537,8 +522,8 @@ Example:
| none => filterMap f as
| some b => b :: filterMap f as
@[simp, grind] theorem filterMap_nil {f : α Option β} : filterMap f [] = [] := rfl
@[grind] theorem filterMap_cons {f : α Option β} {a : α} {l : List α} :
@[simp, grind =] theorem filterMap_nil {f : α Option β} : filterMap f [] = [] := rfl
@[grind =] theorem filterMap_cons {f : α Option β} {a : α} {l : List α} :
filterMap f (a :: l) =
match f a with
| none => filterMap f l
@@ -561,8 +546,8 @@ Examples:
| [] => init
| a :: l => f a (foldr f init l)
@[simp, grind] theorem foldr_nil : [].foldr f b = b := rfl
@[simp, grind] theorem foldr_cons {a} {l : List α} {f : α β β} {b} :
@[simp, grind =] theorem foldr_nil : [].foldr f b = b := rfl
@[simp, grind =] theorem foldr_cons {a} {l : List α} {f : α β β} {b} :
(a :: l).foldr f b = f a (l.foldr f b) := rfl
/-! ### reverse -/
@@ -591,7 +576,7 @@ Examples:
@[expose] def reverse (as : List α) : List α :=
reverseAux as []
@[simp, grind] theorem reverse_nil : reverse ([] : List α) = [] := rfl
@[simp, grind =] theorem reverse_nil : reverse ([] : List α) = [] := rfl
theorem reverseAux_reverseAux {as bs cs : List α} :
reverseAux (reverseAux as bs) cs = reverseAux bs (reverseAux (reverseAux as []) cs) := by
@@ -606,20 +591,6 @@ Appends two lists. Normally used via the `++` operator.
Appending lists takes time proportional to the length of the first list: `O(|xs|)`.
Examples:
* `[1, 2, 3] ++ [4, 5] = [1, 2, 3, 4, 5]`.
* `[] ++ [4, 5] = [4, 5]`.
* `[1, 2, 3] ++ [] = [1, 2, 3]`.
-/
protected def append : (xs ys : List α) List α
| [], bs => bs
| a::as, bs => a :: List.append as bs
/--
Appends two lists. Normally used via the `++` operator.
Appending lists takes time proportional to the length of the first list: `O(|xs|)`.
This is a tail-recursive version of `List.append`.
Examples:
@@ -645,10 +616,10 @@ instance : Append (List α) := ⟨List.append⟩
@[simp] theorem append_eq {as bs : List α} : List.append as bs = as ++ bs := rfl
@[simp, grind] theorem nil_append (as : List α) : [] ++ as = as := rfl
@[simp, grind =] theorem nil_append (as : List α) : [] ++ as = as := rfl
@[simp, grind _=_] theorem cons_append {a : α} {as bs : List α} : (a::as) ++ bs = a::(as ++ bs) := rfl
@[simp, grind] theorem append_nil (as : List α) : as ++ [] = as := by
@[simp, grind =] theorem append_nil (as : List α) : as ++ [] = as := by
induction as with
| nil => rfl
| cons a as ih =>
@@ -658,7 +629,7 @@ instance : Std.LawfulIdentity (α := List α) (· ++ ·) [] where
left_id := nil_append
right_id := append_nil
@[simp, grind] theorem length_append {as bs : List α} : (as ++ bs).length = as.length + bs.length := by
@[simp, grind =] theorem length_append {as bs : List α} : (as ++ bs).length = as.length + bs.length := by
induction as with
| nil => simp
| cons _ as ih => simp [ih, Nat.succ_add]
@@ -685,27 +656,15 @@ theorem reverseAux_eq_append {as bs : List α} : reverseAux as bs = reverseAux a
rw [ih (bs := a :: bs), ih (bs := [a]), append_assoc]
rfl
@[simp, grind] theorem reverse_cons {a : α} {as : List α} : reverse (a :: as) = reverse as ++ [a] := by
@[simp, grind =] theorem reverse_cons {a : α} {as : List α} : reverse (a :: as) = reverse as ++ [a] := by
simp [reverse, reverseAux]
rw [ reverseAux_eq_append]
/-! ### flatten -/
/--
Concatenates a list of lists into a single list, preserving the order of the elements.
`O(|flatten L|)`.
Examples:
* `[["a"], ["b", "c"]].flatten = ["a", "b", "c"]`
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flatten = ["a", "b", "c", "d", "e", "f"]`
-/
def flatten : List (List α) List α
| [] => []
| l :: L => l ++ flatten L
@[simp, grind] theorem flatten_nil : List.flatten ([] : List (List α)) = [] := rfl
@[simp, grind] theorem flatten_cons : (l :: L).flatten = l ++ L.flatten := rfl
@[simp, grind =] theorem flatten_nil : List.flatten ([] : List (List α)) = [] := rfl
@[simp, grind =] theorem flatten_cons : (l :: L).flatten = l ++ L.flatten := rfl
/-! ### singleton -/
@@ -721,20 +680,14 @@ Examples:
/-! ### flatMap -/
/--
Applies a function that returns a list to each element of a list, and concatenates the resulting
lists.
Examples:
* `[2, 3, 2].flatMap List.range = [0, 1, 0, 1, 2, 0, 1]`
* `["red", "blue"].flatMap String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
-/
@[inline] def flatMap {α : Type u} {β : Type v} (b : α List β) (as : List α) : List β := flatten (map b as)
@[simp, grind] theorem flatMap_nil {f : α List β} : List.flatMap f [] = [] := by simp [List.flatMap]
@[simp, grind] theorem flatMap_cons {x : α} {xs : List α} {f : α List β} :
@[simp, grind =] theorem flatMap_nil {f : α List β} : List.flatMap f [] = [] := by simp [List.flatMap]
@[simp, grind =] theorem flatMap_cons {x : α} {xs : List α} {f : α List β} :
List.flatMap f (x :: xs) = f x ++ List.flatMap f xs := by simp [List.flatMap]
@[simp, grind _=_] theorem flatMap_append {xs ys : List α} {f : α List β} :
(xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by
induction xs; {rfl}; simp_all [flatMap_cons, append_assoc]
/-! ### replicate -/
/--
@@ -748,10 +701,10 @@ def replicate : (n : Nat) → (a : α) → List α
| 0, _ => []
| n+1, a => a :: replicate n a
@[simp, grind] theorem replicate_zero {a : α} : replicate 0 a = [] := rfl
@[grind] theorem replicate_succ {a : α} {n : Nat} : replicate (n+1) a = a :: replicate n a := rfl
@[simp, grind =] theorem replicate_zero {a : α} : replicate 0 a = [] := rfl
@[grind =] theorem replicate_succ {a : α} {n : Nat} : replicate (n+1) a = a :: replicate n a := rfl
@[simp, grind] theorem length_replicate {n : Nat} {a : α} : (replicate n a).length = n := by
@[simp, grind =] theorem length_replicate {n : Nat} {a : α} : (replicate n a).length = n := by
induction n with
| zero => simp
| succ n ih => simp only [ih, replicate_succ, length_cons]
@@ -819,8 +772,8 @@ def isEmpty : List α → Bool
| [] => true
| _ :: _ => false
@[simp, grind] theorem isEmpty_nil : ([] : List α).isEmpty = true := rfl
@[simp, grind] theorem isEmpty_cons : (x :: xs : List α).isEmpty = false := rfl
@[simp, grind =] theorem isEmpty_nil : ([] : List α).isEmpty = true := rfl
@[simp, grind =] theorem isEmpty_cons : (x :: xs : List α).isEmpty = false := rfl
/-! ### elem -/
@@ -842,7 +795,7 @@ def elem [BEq α] (a : α) : (l : List α) → Bool
| true => true
| false => elem a bs
@[simp, grind] theorem elem_nil [BEq α] : ([] : List α).elem a = false := rfl
@[simp, grind =] theorem elem_nil [BEq α] : ([] : List α).elem a = false := rfl
theorem elem_cons [BEq α] {a : α} :
(b::bs).elem a = match a == b with | true => true | false => bs.elem a := rfl
@@ -958,9 +911,9 @@ def take : (n : Nat) → (xs : List α) → List α
| _+1, [] => []
| n+1, a::as => a :: take n as
@[simp, grind] theorem take_nil {i : Nat} : ([] : List α).take i = [] := by cases i <;> rfl
@[simp, grind] theorem take_zero {l : List α} : l.take 0 = [] := rfl
@[simp, grind] theorem take_succ_cons {a : α} {as : List α} {i : Nat} : (a::as).take (i+1) = a :: as.take i := rfl
@[simp, grind =] theorem take_nil {i : Nat} : ([] : List α).take i = [] := by cases i <;> rfl
@[simp, grind =] theorem take_zero {l : List α} : l.take 0 = [] := rfl
@[simp, grind =] theorem take_succ_cons {a : α} {as : List α} {i : Nat} : (a::as).take (i+1) = a :: as.take i := rfl
/-! ### drop -/
@@ -980,10 +933,10 @@ def drop : (n : Nat) → (xs : List α) → List α
| _+1, [] => []
| n+1, _::as => drop n as
@[simp, grind] theorem drop_nil : ([] : List α).drop i = [] := by
@[simp, grind =] theorem drop_nil : ([] : List α).drop i = [] := by
cases i <;> rfl
@[simp, grind] theorem drop_zero {l : List α} : l.drop 0 = l := rfl
@[simp, grind] theorem drop_succ_cons {a : α} {l : List α} {i : Nat} : (a :: l).drop (i + 1) = l.drop i := rfl
@[simp, grind =] theorem drop_zero {l : List α} : l.drop 0 = l := rfl
@[simp, grind =] theorem drop_succ_cons {a : α} {l : List α} {i : Nat} : (a :: l).drop (i + 1) = l.drop i := rfl
theorem drop_eq_nil_of_le {as : List α} {i : Nat} (h : as.length i) : as.drop i = [] := by
match as, i with
@@ -1094,13 +1047,13 @@ def dropLast {α} : List α → List α
| [_] => []
| a::as => a :: dropLast as
@[simp, grind] theorem dropLast_nil : ([] : List α).dropLast = [] := rfl
@[simp, grind] theorem dropLast_singleton : [x].dropLast = [] := rfl
@[simp, grind =] theorem dropLast_nil : ([] : List α).dropLast = [] := rfl
@[simp, grind =] theorem dropLast_singleton : [x].dropLast = [] := rfl
@[deprecated dropLast_singleton (since := "2025-04-16")]
theorem dropLast_single : [x].dropLast = [] := dropLast_singleton
@[simp, grind] theorem dropLast_cons₂ :
@[simp, grind =] theorem dropLast_cons₂ :
(x::y::zs).dropLast = x :: (y::zs).dropLast := rfl
-- Later this can be proved by `simp` via `[List.length_dropLast, List.length_cons, Nat.add_sub_cancel]`,
@@ -1439,8 +1392,8 @@ def replace [BEq α] : (l : List α) → (a : α) → (b : α) → List α
| true => c::as
| false => a :: replace as b c
@[simp, grind] theorem replace_nil [BEq α] : ([] : List α).replace a b = [] := rfl
@[grind] theorem replace_cons [BEq α] {a : α} :
@[simp, grind =] theorem replace_nil [BEq α] : ([] : List α).replace a b = [] := rfl
@[grind =] theorem replace_cons [BEq α] {a : α} :
(a::as).replace b c = match b == a with | true => c::as | false => a :: replace as b c :=
rfl
@@ -1648,8 +1601,8 @@ def findSome? (f : α → Option β) : List α → Option β
| some b => some b
| none => findSome? f as
@[simp, grind] theorem findSome?_nil : ([] : List α).findSome? f = none := rfl
@[grind] theorem findSome?_cons {f : α Option β} :
@[simp, grind =] theorem findSome?_nil : ([] : List α).findSome? f = none := rfl
@[grind =] theorem findSome?_cons {f : α Option β} :
(a::as).findSome? f = match f a with | some b => some b | none => as.findSome? f :=
rfl
@@ -1906,8 +1859,8 @@ def any : (l : List α) → (p : α → Bool) → Bool
| [], _ => false
| h :: t, p => p h || any t p
@[simp, grind] theorem any_nil : [].any f = false := rfl
@[simp, grind] theorem any_cons : (a::l).any f = (f a || l.any f) := rfl
@[simp, grind =] theorem any_nil : [].any f = false := rfl
@[simp, grind =] theorem any_cons : (a::l).any f = (f a || l.any f) := rfl
/-! ### all -/
@@ -1925,8 +1878,8 @@ def all : List α → (α → Bool) → Bool
| [], _ => true
| h :: t, p => p h && all t p
@[simp, grind] theorem all_nil : [].all f = true := rfl
@[simp, grind] theorem all_cons : (a::l).all f = (f a && l.all f) := rfl
@[simp, grind =] theorem all_nil : [].all f = true := rfl
@[simp, grind =] theorem all_cons : (a::l).all f = (f a && l.all f) := rfl
/-! ### or -/
@@ -2066,8 +2019,8 @@ Examples:
def sum {α} [Add α] [Zero α] : List α α :=
foldr (· + ·) 0
@[simp, grind] theorem sum_nil [Add α] [Zero α] : ([] : List α).sum = 0 := rfl
@[simp, grind] theorem sum_cons [Add α] [Zero α] {a : α} {l : List α} : (a::l).sum = a + l.sum := rfl
@[simp, grind =] theorem sum_nil [Add α] [Zero α] : ([] : List α).sum = 0 := rfl
@[simp, grind =] theorem sum_cons [Add α] [Zero α] {a : α} {l : List α} : (a::l).sum = a + l.sum := rfl
/-! ### range -/

View File

@@ -223,7 +223,7 @@ variable [BEq α]
@[simp, grind =] theorem count_nil {a : α} : count a [] = 0 := rfl
@[grind]
@[grind =]
theorem count_cons {a b : α} {l : List α} :
count a (b :: l) = count a l + if b == a then 1 else 0 := by
simp [count, countP_cons]
@@ -237,7 +237,7 @@ theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
theorem count_eq_length_filter {a : α} {l : List α} : count a l = (filter (· == a) l).length := by
simp [count, countP_eq_length_filter]
@[grind]
@[grind =]
theorem count_tail : {l : List α} {a : α},
l.tail.count a = l.count a - if l.head? == some a then 1 else 0
| [], a => by simp
@@ -380,7 +380,7 @@ theorem count_filterMap {α} [BEq β] {b : β} {f : α → Option β} {l : List
theorem count_flatMap {α} [BEq β] {l : List α} {f : α List β} {x : β} :
count x (l.flatMap f) = sum (map (count x f) l) := countP_flatMap
@[grind]
@[grind =]
theorem count_erase {a b : α} :
{l : List α}, count a (l.erase b) = count a l - if b == a then 1 else 0
| [] => by simp

View File

@@ -130,7 +130,7 @@ theorem le_length_eraseP {l : List α} : l.length - 1 ≤ (l.eraseP p).length :=
@[grind ]
theorem mem_of_mem_eraseP {l : List α} : a l.eraseP p a l := (eraseP_subset ·)
@[simp, grind] theorem mem_eraseP_of_neg {l : List α} (pa : ¬p a) : a l.eraseP p a l := by
@[simp, grind =] theorem mem_eraseP_of_neg {l : List α} (pa : ¬p a) : a l.eraseP p a l := by
refine mem_of_mem_eraseP, fun al => ?_
match exists_or_eq_self_of_eraseP p l with
| .inl h => rw [h]; assumption
@@ -265,14 +265,18 @@ theorem eraseP_eq_iff {p} {l : List α} :
subst p
simp_all
@[grind ]
theorem Pairwise.eraseP (q) : Pairwise p l Pairwise p (l.eraseP q) :=
Pairwise.sublist <| eraseP_sublist
@[grind ]
grind_pattern Pairwise.eraseP => Pairwise p (l.eraseP q)
grind_pattern Pairwise.eraseP => Pairwise p l, l.eraseP q
theorem Nodup.eraseP (p) : Nodup l Nodup (l.eraseP p) :=
Pairwise.eraseP p
grind_pattern Nodup.eraseP => Nodup (l.eraseP p)
grind_pattern Nodup.eraseP => Nodup l, l.eraseP p
@[grind =]
theorem eraseP_comm {l : List α} (h : a l, ¬ p a ¬ q a) :
(l.eraseP p).eraseP q = (l.eraseP q).eraseP p := by
@@ -393,7 +397,7 @@ theorem le_length_erase [LawfulBEq α] {a : α} {l : List α} : l.length - 1 ≤
@[grind ]
theorem mem_of_mem_erase {a b : α} {l : List α} (h : a l.erase b) : a l := erase_subset h
@[simp, grind] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {l : List α} (ab : a b) :
@[simp, grind =] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {l : List α} (ab : a b) :
a l.erase b a l :=
erase_eq_eraseP b l mem_eraseP_of_neg (mt eq_of_beq ab.symm)
@@ -508,10 +512,12 @@ theorem Nodup.not_mem_erase [LawfulBEq α] {a : α} (h : Nodup l) : a ∉ l.eras
-- Only activate `not_mem_erase` when `l.Nodup` is already available.
grind_pattern List.Nodup.not_mem_erase => a l.erase a, l.Nodup
@[grind]
theorem Nodup.erase [LawfulBEq α] (a : α) : Nodup l Nodup (l.erase a) :=
Pairwise.erase a
grind_pattern Nodup.erase => Nodup (l.erase a)
grind_pattern Nodup.erase => Nodup l, l.erase a
theorem head_erase_mem (xs : List α) (a : α) (h) : (xs.erase a).head h xs :=
erase_sublist.head_mem h
@@ -578,21 +584,21 @@ theorem eraseIdx_ne_nil_iff {l : List α} {i : Nat} : eraseIdx l i ≠ [] ↔ 2
| [a]
| a::b::l => simp
@[grind]
theorem eraseIdx_sublist : (l : List α) (k : Nat), eraseIdx l k <+ l
| [], _ => by simp
| a::l, 0 => by simp
| a::l, k + 1 => by simp [eraseIdx_sublist]
grind_pattern eraseIdx_sublist => l.eraseIdx k, _ <+ l
theorem mem_of_mem_eraseIdx {l : List α} {i : Nat} {a : α} (h : a l.eraseIdx i) : a l :=
(eraseIdx_sublist _ _).mem h
@[grind]
theorem eraseIdx_subset {l : List α} {k : Nat} : eraseIdx l k l :=
(eraseIdx_sublist _ _).subset
grind_pattern eraseIdx_sublist => l.eraseIdx k, _ l
@[simp]
theorem eraseIdx_eq_self : {l : List α} {k : Nat}, eraseIdx l k = l length l k
| [], _ => by simp
@@ -649,15 +655,18 @@ theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} :
exact m.2
· rw [eraseIdx_of_length_le (by simpa using h)]
@[grind ]
theorem Pairwise.eraseIdx {l : List α} (k) : Pairwise p l Pairwise p (l.eraseIdx k) :=
Pairwise.sublist <| eraseIdx_sublist _ _
@[grind ]
grind_pattern Pairwise.eraseIdx => Pairwise p (l.eraseIdx k)
grind_pattern Pairwise.eraseIdx => Pairwise p l, l.eraseIdx k
theorem Nodup.eraseIdx {l : List α} (k) : Nodup l Nodup (l.eraseIdx k) :=
Pairwise.eraseIdx k
@[grind ]
grind_pattern Nodup.eraseIdx => Nodup (l.eraseIdx k)
grind_pattern Nodup.eraseIdx => Nodup l, l.eraseIdx k
protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
eraseIdx l k <+: eraseIdx l' k := by
rcases h with t, rfl
@@ -667,6 +676,10 @@ protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
rw [Nat.not_lt] at hkl
simp [eraseIdx_append_of_length_le hkl, eraseIdx_of_length_le hkl]
grind_pattern IsPrefix.eraseIdx => eraseIdx l k <+: eraseIdx l' k
grind_pattern IsPrefix.eraseIdx => eraseIdx l k, l <+: l'
grind_pattern IsPrefix.eraseIdx => eraseIdx l' k, l <+: l'
-- See also `mem_eraseIdx_iff_getElem` and `mem_eraseIdx_iff_getElem?` in
-- `Init/Data/List/Nat/Basic.lean`.
@@ -686,6 +699,4 @@ theorem erase_eq_eraseIdx_of_idxOf [BEq α] [LawfulBEq α]
rw [eq_comm, eraseIdx_eq_self]
exact Nat.le_of_eq (idxOf_eq_length h).symm
end List

View File

@@ -6,7 +6,8 @@ Authors: François G. Dorais
module
prelude
public import all Init.Data.List.OfFn
public import Init.Data.List.OfFn
import all Init.Data.List.OfFn
public import Init.Data.List.Monadic
public section

View File

@@ -11,7 +11,8 @@ public import Init.Data.List.Lemmas
public import Init.Data.List.Sublist
public import Init.Data.List.Range
public import Init.Data.List.Impl
public import all Init.Data.List.Attach
public import Init.Data.List.Attach
import all Init.Data.List.Attach
public import Init.Data.Fin.Lemmas
public section
@@ -292,7 +293,6 @@ theorem mem_of_find?_eq_some : ∀ {l}, find? p l = some a → a ∈ l
· exact H .head _
· exact .tail _ (mem_of_find?_eq_some H)
@[grind]
theorem get_find?_mem {xs : List α} {p : α Bool} (h) : (xs.find? p).get h xs := by
induction xs with
| nil => simp at h
@@ -304,6 +304,8 @@ theorem get_find?_mem {xs : List α} {p : α → Bool} (h) : (xs.find? p).get h
right
apply ih
grind_pattern get_find?_mem => (xs.find? p).get h
@[simp, grind =] theorem find?_filter {xs : List α} {p : α Bool} {q : α Bool} :
(xs.filter p).find? q = xs.find? (fun a => p a q a) := by
induction xs with
@@ -557,7 +559,6 @@ where
@[simp] theorem findIdx_singleton {a : α} {p : α Bool} : [a].findIdx p = if p a then 0 else 1 := by
simp [findIdx_cons, findIdx_nil]
@[grind ]
theorem findIdx_of_getElem?_eq_some {xs : List α} (w : xs[xs.findIdx p]? = some y) : p y := by
induction xs with
| nil => simp_all

View File

@@ -9,8 +9,10 @@ module
prelude
public import Init.Data.Bool
public import Init.Data.Option.Lemmas
public import all Init.Data.List.BasicAux
public import all Init.Data.List.Control
public import Init.Data.List.BasicAux
import all Init.Data.List.BasicAux
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.Control.Lawful.Basic
public import Init.BinderPredicates
@@ -26,14 +28,14 @@ For each `List` operation, we would like theorems describing the following, when
* the length of the result `(f L).length`
* the `i`-th element, described via `(f L)[i]` and/or `(f L)[i]?` (these should typically be `@[simp]`)
* consequences for `f L` of the fact `x ∈ L` or `x ∉ L`
* conditions characterising `x ∈ f L` (often but not always `@[simp]`)
* conditions characterizing `x ∈ f L` (often but not always `@[simp]`)
* injectivity statements, or congruence statements of the form `p L M → f L = f M`.
* conditions characterising the result, i.e. of the form `f L = M ↔ p M` for some predicate `p`,
* conditions characterizing the result, i.e. of the form `f L = M ↔ p M` for some predicate `p`,
along with special cases of `M` (e.g. `List.append_eq_nil : L ++ M = [] ↔ L = [] ∧ M = []`)
* negative characterisations are also useful, e.g. `List.cons_ne_nil`
* negative characterizations are also useful, e.g. `List.cons_ne_nil`
* interactions with all previously described `List` operations where possible
(some of these should be `@[simp]`, particularly if the result can be described by a single operation)
* characterising `(∀ (i) (_ : i ∈ f L), P i)`, for some predicate `P`
* characterizing `(∀ (i) (_ : i ∈ f L), P i)`, for some predicate `P`
Of course for any individual operation, not all of these will be relevant or helpful, so some judgement is required.
@@ -304,7 +306,7 @@ theorem getD_getElem? {l : List α} {i : Nat} {d : α} :
match i, h with
| 0, _ => rfl
@[grind]
@[grind =]
theorem getElem?_singleton {a : α} {i : Nat} : [a][i]? = if i = 0 then some a else none := by
simp [getElem?_cons]
@@ -346,6 +348,18 @@ theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
theorem getElem?_concat_length {l : List α} {a : α} : (l ++ [a])[l.length]? = some a := by
simp
theorem eq_getElem_of_length_eq_one : (l : List α) (hl : l.length = 1) l = [l[0]'(hl by decide)]
| [_], _ => rfl
theorem eq_getElem_of_length_eq_two : (l : List α) (hl : l.length = 2) l = [l[0]'(hl by decide), l[1]'(hl by decide)]
| [_, _], _ => rfl
theorem eq_getElem_of_length_eq_three : (l : List α) (hl : l.length = 3) l = [l[0]'(hl by decide), l[1]'(hl by decide), l[2]'(hl by decide)]
| [_, _, _], _ => rfl
theorem eq_getElem_of_length_eq_four : (l : List α) (hl : l.length = 4) l = [l[0]'(hl by decide), l[1]'(hl by decide), l[2]'(hl by decide), l[3]'(hl by decide)]
| [_, _, _, _], _ => rfl
/-! ### getD
We simplify away `getD`, replacing `getD l n a` with `(l[n]?).getD a`.
@@ -380,14 +394,20 @@ theorem get!_eq_getElem! [Inhabited α] (l : List α) (i) : l.get! i = l[i]! :=
@[simp] theorem not_mem_nil {a : α} : ¬ a [] := nofun
@[simp] theorem mem_cons : a b :: l a = b a l :=
@[simp, grind =] theorem mem_cons : a b :: l a = b a l :=
fun h => by cases h <;> simp [Membership.mem, *],
fun | Or.inl rfl => by constructor | Or.inr h => by constructor; assumption
@[grind] theorem eq_or_mem_of_mem_cons {a b : α} {l : List α} :
theorem eq_or_mem_of_mem_cons {a b : α} {l : List α} :
a b :: l a = b a l := List.mem_cons.mp
@[grind] theorem mem_cons_self {a : α} {l : List α} : a a :: l := .head ..
-- This pattern may be excessively general:
-- it fires anytime we ae thinking about membership of lists,
-- and constructing a list via `cons`, even if the elements are unrelated.
-- Nevertheless in practice it is quite helpful!
grind_pattern eq_or_mem_of_mem_cons => b :: l, a l
theorem mem_cons_self {a : α} {l : List α} : a a :: l := .head ..
theorem mem_concat_self {xs : List α} {a : α} : a xs ++ [a] :=
mem_append_right xs mem_cons_self
@@ -409,7 +429,7 @@ theorem eq_append_cons_of_mem {a : α} {xs : List α} (h : a ∈ xs) :
· obtain as, bs, rfl, h := ih h
exact x :: as, bs, rfl, by simp_all
@[grind] theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a l a y :: l := .tail _
theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a l a y :: l := .tail _
-- The argument `l : List α` is intentionally explicit,
-- as a tactic may generate `h` without determining `l`.
@@ -545,10 +565,10 @@ theorem contains_iff [BEq α] [LawfulBEq α] {a : α} {as : List α} :
theorem elem_eq_mem [BEq α] [LawfulBEq α] (a : α) (as : List α) :
elem a as = decide (a as) := by rw [Bool.eq_iff_iff, elem_iff, decide_eq_true_iff]
@[simp, grind] theorem contains_eq_mem [BEq α] [LawfulBEq α] (a : α) (as : List α) :
@[simp, grind =] theorem contains_eq_mem [BEq α] [LawfulBEq α] (a : α) (as : List α) :
as.contains a = decide (a as) := by rw [Bool.eq_iff_iff, elem_iff, decide_eq_true_iff]
@[simp, grind] theorem contains_cons [BEq α] {a : α} {b : α} {l : List α} :
@[simp, grind =] theorem contains_cons [BEq α] {a : α} {b : α} {l : List α} :
(a :: l).contains b = (b == a || l.contains b) := by
simp only [contains, elem_cons]
split <;> simp_all
@@ -603,7 +623,7 @@ theorem decide_forall_mem {l : List α} {p : α → Prop} [DecidablePred p] :
@[simp] theorem all_eq_false {l : List α} : l.all p = false x, x l ¬p x := by
simp [all_eq]
@[grind] theorem any_beq [BEq α] {l : List α} {a : α} : (l.any fun x => a == x) = l.contains a := by
theorem any_beq [BEq α] {l : List α} {a : α} : (l.any fun x => a == x) = l.contains a := by
induction l <;> simp_all [contains_cons]
/-- Variant of `any_beq` with `==` reversed. -/
@@ -611,7 +631,7 @@ theorem any_beq' [BEq α] [PartialEquivBEq α] {l : List α} :
(l.any fun x => x == a) = l.contains a := by
simp only [BEq.comm, any_beq]
@[grind] theorem all_bne [BEq α] {l : List α} : (l.all fun x => a != x) = !l.contains a := by
theorem all_bne [BEq α] {l : List α} : (l.all fun x => a != x) = !l.contains a := by
induction l <;> simp_all [bne]
/-- Variant of `all_bne` with `!=` reversed. -/
@@ -622,10 +642,10 @@ theorem all_bne' [BEq α] [PartialEquivBEq α] {l : List α} :
/-! ### set -/
-- As `List.set` is defined in `Init.Prelude`, we write the basic simplification lemmas here.
@[simp, grind] theorem set_nil {i : Nat} {a : α} : [].set i a = [] := rfl
@[simp, grind] theorem set_cons_zero {x : α} {xs : List α} {a : α} :
@[simp, grind =] theorem set_nil {i : Nat} {a : α} : [].set i a = [] := rfl
@[simp, grind =] theorem set_cons_zero {x : α} {xs : List α} {a : α} :
(x :: xs).set 0 a = a :: xs := rfl
@[simp, grind] theorem set_cons_succ {x : α} {xs : List α} {i : Nat} {a : α} :
@[simp, grind =] theorem set_cons_succ {x : α} {xs : List α} {i : Nat} {a : α} :
(x :: xs).set (i + 1) a = x :: xs.set i a := rfl
@[simp] theorem getElem_set_self {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
@@ -668,14 +688,14 @@ theorem getElem?_set_self' {l : List α} {i : Nat} {a : α} :
simp_all
· rw [getElem?_eq_none (by simp_all), getElem?_eq_none (by simp_all)]
@[grind] theorem getElem_set {l : List α} {i j} {a} (h) :
@[grind =] theorem getElem_set {l : List α} {i j} {a} (h) :
(set l i a)[j]'h = if i = j then a else l[j]'(length_set .. h) := by
if h : i = j then
subst h; simp only [getElem_set_self, reduceIte]
else
simp [h]
@[grind] theorem getElem?_set {l : List α} {i j : Nat} {a : α} :
@[grind =] theorem getElem?_set {l : List α} {i j : Nat} {a : α} :
(l.set i a)[j]? = if i = j then if i < l.length then some a else none else l[j]? := by
if h : i = j then
subst h
@@ -745,10 +765,10 @@ theorem mem_or_eq_of_mem_set : ∀ {l : List α} {i : Nat} {a b : α}, a ∈ l.s
/-! ### BEq -/
@[simp, grind] theorem beq_nil_eq [BEq α] {l : List α} : (l == []) = l.isEmpty := by
@[simp, grind =] theorem beq_nil_eq [BEq α] {l : List α} : (l == []) = l.isEmpty := by
cases l <;> rfl
@[simp, grind] theorem nil_beq_eq [BEq α] {l : List α} : ([] == l) = l.isEmpty := by
@[simp, grind =] theorem nil_beq_eq [BEq α] {l : List α} : ([] == l) = l.isEmpty := by
cases l <;> rfl
@[deprecated beq_nil_eq (since := "2025-04-04")]
@@ -757,7 +777,7 @@ abbrev beq_nil_iff := @beq_nil_eq
@[deprecated nil_beq_eq (since := "2025-04-04")]
abbrev nil_beq_iff := @nil_beq_eq
@[simp, grind] theorem cons_beq_cons [BEq α] {a b : α} {l₁ l₂ : List α} :
@[simp, grind =] theorem cons_beq_cons [BEq α] {a b : α} {l₁ l₂ : List α} :
(a :: l₁ == b :: l₂) = (a == b && l₁ == l₂) := rfl
@[simp] theorem concat_beq_concat [BEq α] {a b : α} {l₁ l₂ : List α} :
@@ -823,7 +843,7 @@ theorem length_eq_of_beq [BEq α] {l₁ l₂ : List α} (h : l₁ == l₂) : l
/-! ### getLast -/
@[grind]
@[grind =]
theorem getLast_eq_getElem : {l : List α} (h : l []),
getLast l h = l[l.length - 1]'(by
match l with
@@ -837,7 +857,7 @@ theorem getElem_length_sub_one_eq_getLast {l : List α} (h : l.length - 1 < l.le
l[l.length - 1] = getLast l (by cases l; simp at h; simp) := by
rw [ getLast_eq_getElem]
@[simp, grind] theorem getLast_cons_cons {a : α} {l : List α} :
@[simp, grind =] theorem getLast_cons_cons {a : α} {l : List α} :
getLast (a :: b :: l) (by simp) = getLast (b :: l) (by simp) :=
rfl
@@ -850,10 +870,10 @@ theorem getLast_cons {a : α} {l : List α} : ∀ (h : l ≠ nil),
theorem getLast_eq_getLastD {a l} (h) : @getLast α (a::l) h = getLastD l a := by
cases l <;> rfl
@[simp, grind] theorem getLastD_eq_getLast? {a l} : @getLastD α l a = (getLast? l).getD a := by
@[simp, grind =] theorem getLastD_eq_getLast? {a l} : @getLastD α l a = (getLast? l).getD a := by
cases l <;> rfl
@[simp, grind] theorem getLast_singleton {a} (h) : @getLast α [a] h = a := rfl
@[simp, grind =] theorem getLast_singleton {a} (h) : @getLast α [a] h = a := rfl
theorem getLast!_cons_eq_getLastD [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
simp [getLast!, getLast_eq_getLastD]
@@ -886,7 +906,7 @@ theorem getLast?_eq_getLast : ∀ {l : List α} h, l.getLast? = some (l.getLast
| [], h => nomatch h rfl
| _ :: _, _ => rfl
@[grind] theorem getLast?_eq_getElem? : {l : List α}, l.getLast? = l[l.length - 1]?
@[grind =] theorem getLast?_eq_getElem? : {l : List α}, l.getLast? = l[l.length - 1]?
| [] => rfl
| a::l => by
rw [getLast?_eq_getLast (l := a :: l) nofun, getLast_eq_getElem, getElem?_eq_getElem]
@@ -899,14 +919,14 @@ theorem getLast_eq_iff_getLast?_eq_some {xs : List α} (h) :
-- `getLast?_eq_none_iff`, `getLast?_eq_some_iff`, `getLast?_isSome`, and `getLast_mem`
-- are proved later once more `reverse` theorems are available.
@[grind]
@[grind =]
theorem getLast?_cons {a : α} : (a::l).getLast? = some (l.getLast?.getD a) := by
cases l <;> simp [getLast?, getLast]
@[simp] theorem getLast?_cons_cons : (a :: b :: l).getLast? = (b :: l).getLast? := by
simp [getLast?_cons]
@[grind]
@[grind =]
theorem getLast?_concat {l : List α} {a : α} : (l ++ [a]).getLast? = some a := by
simp [getLast?_eq_getElem?, Nat.succ_sub_succ]
@@ -925,14 +945,14 @@ theorem getLast!_nil [Inhabited α] : ([] : List α).getLast! = default := rfl
theorem getLast!_of_getLast? [Inhabited α] : {l : List α}, getLast? l = some a getLast! l = a
| _ :: _, rfl => rfl
@[grind]
@[grind =]
theorem getLast!_eq_getElem! [Inhabited α] {l : List α} : l.getLast! = l[l.length - 1]! := by
cases l with
| nil => simp
| cons _ _ =>
apply getLast!_of_getLast?
rw [getElem!_pos, getElem_cons_length (h := by simp)]
rfl
rw [getLast?_eq_getElem?]
simp
/-! ## Head and tail -/
@@ -953,7 +973,7 @@ theorem head?_eq_getElem? : ∀ {l : List α}, l.head? = l[0]?
theorem head_singleton {a : α} : head [a] (by simp) = a := by simp
@[grind]
@[grind =]
theorem head_eq_getElem {l : List α} (h : l []) : head l h = l[0]'(length_pos_iff.mpr h) := by
cases l with
| nil => simp at h
@@ -1015,18 +1035,18 @@ theorem head_of_mem_head? {l : List α} {x} (hx : x ∈ l.head?) :
/-! ### headD -/
/-- `simp` unfolds `headD` in terms of `head?` and `Option.getD`. -/
@[simp, grind] theorem headD_eq_head?_getD {l : List α} : headD l a = (head? l).getD a := by
@[simp, grind =] theorem headD_eq_head?_getD {l : List α} : headD l a = (head? l).getD a := by
cases l <;> simp [headD]
/-! ### tailD -/
/-- `simp` unfolds `tailD` in terms of `tail?` and `Option.getD`. -/
@[simp, grind] theorem tailD_eq_tail? {l l' : List α} : tailD l l' = (tail? l).getD l' := by
@[simp, grind =] theorem tailD_eq_tail? {l l' : List α} : tailD l l' = (tail? l).getD l' := by
cases l <;> rfl
/-! ### tail -/
@[simp, grind] theorem length_tail {l : List α} : l.tail.length = l.length - 1 := by cases l <;> rfl
@[simp, grind =] theorem length_tail {l : List α} : l.tail.length = l.length - 1 := by cases l <;> rfl
theorem tail_eq_tailD {l : List α} : l.tail = tailD l [] := by cases l <;> rfl
@@ -1038,13 +1058,13 @@ theorem mem_of_mem_tail {a : α} {l : List α} (h : a ∈ tail l) : a ∈ l := b
theorem ne_nil_of_tail_ne_nil {l : List α} : l.tail [] l [] := by
cases l <;> simp
@[simp, grind] theorem getElem_tail {l : List α} {i : Nat} (h : i < l.tail.length) :
@[simp, grind =] theorem getElem_tail {l : List α} {i : Nat} (h : i < l.tail.length) :
(tail l)[i] = l[i + 1]'(add_lt_of_lt_sub (by simpa using h)) := by
cases l with
| nil => simp at h
| cons _ l => simp
@[simp, grind] theorem getElem?_tail {l : List α} {i : Nat} :
@[simp, grind =] theorem getElem?_tail {l : List α} {i : Nat} :
(tail l)[i]? = l[i + 1]? := by
cases l <;> simp
@@ -1068,7 +1088,7 @@ theorem one_lt_length_of_tail_ne_nil {l : List α} (h : l.tail ≠ []) : 1 < l.l
@[simp] theorem head?_tail {l : List α} : (tail l).head? = l[1]? := by
simp [head?_eq_getElem?]
@[simp, grind] theorem getLast_tail {l : List α} (h : l.tail []) :
@[simp, grind =] theorem getLast_tail {l : List α} (h : l.tail []) :
(tail l).getLast h = l.getLast (ne_nil_of_tail_ne_nil h) := by
simp only [getLast_eq_getElem, length_tail, getElem_tail]
congr
@@ -1094,7 +1114,7 @@ theorem cons_head_tail (h : l ≠ []) : l.head h :: l.tail = l := by
/-! ### map -/
@[simp, grind] theorem length_map {as : List α} (f : α β) : (as.map f).length = as.length := by
@[simp, grind =] theorem length_map {as : List α} (f : α β) : (as.map f).length = as.length := by
induction as with
| nil => simp [List.map]
| cons _ as ih => simp [List.map, ih]
@@ -1102,13 +1122,13 @@ theorem cons_head_tail (h : l ≠ []) : l.head h :: l.tail = l := by
@[simp] theorem isEmpty_map {l : List α} {f : α β} : (l.map f).isEmpty = l.isEmpty := by
cases l <;> simp
@[simp, grind] theorem getElem?_map {f : α β} : {l : List α} {i : Nat}, (map f l)[i]? = Option.map f l[i]?
@[simp, grind =] theorem getElem?_map {f : α β} : {l : List α} {i : Nat}, (map f l)[i]? = Option.map f l[i]?
| [], _ => rfl
| _ :: _, 0 => by simp
| _ :: l, i+1 => by simp [getElem?_map]
-- The argument `f : α → β` is explicit, to facilitate rewriting from right to left.
@[simp, grind] theorem getElem_map (f : α β) {l} {i : Nat} {h : i < (map f l).length} :
@[simp, grind =] theorem getElem_map (f : α β) {l} {i : Nat} {h : i < (map f l).length} :
(map f l)[i] = f (l[i]'(length_map f h)) :=
Option.some.inj <| by rw [ getElem?_eq_getElem, getElem?_map, getElem?_eq_getElem]; rfl
@@ -1154,7 +1174,9 @@ theorem forall_mem_map {f : α → β} {l : List α} {P : β → Prop} :
@[simp] theorem map_eq_nil_iff {f : α β} {l : List α} : map f l = [] l = [] := by
constructor <;> exact fun _ => match l with | [] => rfl
@[grind ]
-- This would be helpful as a `grind` lemma if
-- we could have it fire only once `map f l` and `[]` are the same equivalence class.
-- Otherwise it is too aggressive.
theorem eq_nil_of_map_eq_nil {f : α β} {l : List α} (h : map f l = []) : l = [] :=
map_eq_nil_iff.mp h
@@ -1274,7 +1296,7 @@ theorem getLastD_map {f : α → β} {l : List α} {a : α} : (map f l).getLastD
@[simp] theorem filter_cons_of_neg {p : α Bool} {a : α} {l} (pa : ¬ p a) :
filter p (a :: l) = filter p l := by rw [filter, eq_false_of_ne_true pa]
@[grind] theorem filter_cons :
@[grind =] theorem filter_cons :
(x :: xs : List α).filter p = if p x then x :: (xs.filter p) else xs.filter p := by
split <;> simp [*]
@@ -1313,7 +1335,7 @@ theorem length_filter_eq_length_iff {l} : (filter p l).length = l.length ↔ ∀
@[deprecated length_filter_eq_length_iff (since := "2025-04-04")]
abbrev filter_length_eq_length := @length_filter_eq_length_iff
@[simp, grind] theorem mem_filter : x filter p as x as p x := by
@[simp, grind =] theorem mem_filter : x filter p as x as p x := by
induction as with
| nil => simp
| cons a as ih =>
@@ -1328,13 +1350,15 @@ theorem forall_mem_filter {l : List α} {p : α → Bool} {P : α → Prop} :
( (i) (_ : i l.filter p), P i) (j) (_ : j l), p j P j := by
simp
@[grind] theorem getElem_filter {xs : List α} {p : α Bool} {i : Nat} (h : i < (xs.filter p).length) :
theorem getElem_filter {xs : List α} {p : α Bool} {i : Nat} (h : i < (xs.filter p).length) :
p (xs.filter p)[i] :=
(mem_filter.mp (getElem_mem h)).2
grind_pattern getElem_filter => (xs.filter p)[i]
theorem getElem?_filter {xs : List α} {p : α Bool} {i : Nat} (h : i < (xs.filter p).length)
(w : (xs.filter p)[i]? = some a) : p a := by
rw [getElem?_eq_getElem] at w
rw [getElem?_eq_getElem h] at w
simp only [Option.some.injEq] at w
rw [ w]
apply getElem_filter h
@@ -1375,7 +1399,7 @@ theorem map_filter_eq_foldr {f : α → β} {p : α → Bool} {as : List α} :
simp only [foldr]
cases hp : p head <;> simp [filter, *]
@[simp, grind] theorem filter_append {p : α Bool} :
@[simp, grind =] theorem filter_append {p : α Bool} :
(l₁ l₂ : List α), filter p (l₁ ++ l₂) = filter p l₁ ++ filter p l₂
| [], _ => rfl
| a :: l₁, l₂ => by simp only [cons_append, filter]; split <;> simp [filter_append l₁]
@@ -1440,7 +1464,7 @@ theorem filterMap_some_fun : filterMap (some : α → Option α) = id := by
erw [filterMap_eq_map]
simp
@[simp, grind] theorem filterMap_some {l : List α} : filterMap some l = l := by
@[simp, grind =] theorem filterMap_some {l : List α} : filterMap some l = l := by
rw [filterMap_some_fun, id]
theorem map_filterMap_some_eq_filter_map_isSome {f : α Option β} {l : List α} :
@@ -1475,19 +1499,19 @@ theorem filterMap_eq_filter {p : α → Bool} :
| nil => rfl
| cons a l IH => by_cases pa : p a <;> simp [Option.guard, pa, IH]
@[grind]
@[grind =]
theorem filterMap_filterMap {f : α Option β} {g : β Option γ} {l : List α} :
filterMap g (filterMap f l) = filterMap (fun x => (f x).bind g) l := by
induction l with
| nil => rfl
| cons a l IH => cases h : f a <;> simp [filterMap_cons, *]
@[grind]
@[grind =]
theorem map_filterMap {f : α Option β} {g : β γ} {l : List α} :
map g (filterMap f l) = filterMap (fun x => (f x).map g) l := by
simp only [ filterMap_eq_map, filterMap_filterMap, Option.map_eq_bind]
@[simp, grind]
@[simp, grind =]
theorem filterMap_map {f : α β} {g : β Option γ} {l : List α} :
filterMap g (map f l) = filterMap (g f) l := by
rw [ filterMap_eq_map, filterMap_filterMap]; rfl
@@ -1502,7 +1526,7 @@ theorem filterMap_filter {p : α → Bool} {f : α → Option β} {l : List α}
rw [ filterMap_eq_filter, filterMap_filterMap]
congr; funext x; by_cases h : p x <;> simp [Option.guard, h]
@[simp, grind] theorem mem_filterMap {f : α Option β} {l : List α} {b : β} :
@[simp, grind =] theorem mem_filterMap {f : α Option β} {l : List α} {b : β} :
b filterMap f l a, a l f a = some b := by
induction l <;> simp [filterMap_cons]; split <;> simp [*, eq_comm]
@@ -1514,7 +1538,7 @@ theorem forall_mem_filterMap {f : α → Option β} {l : List α} {P : β → Pr
intro a
rw [forall_comm]
@[simp, grind] theorem filterMap_append {l l' : List α} {f : α Option β} :
@[simp, grind =] theorem filterMap_append {l l' : List α} {f : α Option β} :
filterMap f (l ++ l') = filterMap f l ++ filterMap f l' := by
induction l <;> simp [filterMap_cons]; split <;> simp [*]
@@ -1586,7 +1610,7 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
@[simp] theorem cons_append_fun {a : α} {as : List α} :
(fun bs => ((a :: as) ++ bs)) = fun bs => a :: (as ++ bs) := rfl
@[simp, grind] theorem mem_append {a : α} {s t : List α} : a s ++ t a s a t := by
@[simp, grind =] theorem mem_append {a : α} {s t : List α} : a s ++ t a s a t := by
induction s <;> simp_all [or_assoc]
theorem not_mem_append {a : α} {s t : List α} (h₁ : a s) (h₂ : a t) : a s ++ t :=
@@ -1609,7 +1633,7 @@ theorem forall_mem_append {p : α → Prop} {l₁ l₂ : List α} :
( (x) (_ : x l₁ ++ l₂), p x) ( (x) (_ : x l₁), p x) ( (x) (_ : x l₂), p x) := by
simp only [mem_append, or_imp, forall_and]
@[grind] theorem getElem_append {l₁ l₂ : List α} {i : Nat} (h : i < (l₁ ++ l₂).length) :
@[grind =] theorem getElem_append {l₁ l₂ : List α} {i : Nat} (h : i < (l₁ ++ l₂).length) :
(l₁ ++ l₂)[i] = if h' : i < l₁.length then l₁[i] else l₂[i - l₁.length]'(by simp at h h'; exact Nat.sub_lt_left_of_lt_add h' h) := by
split <;> rename_i h'
· rw [getElem_append_left h']
@@ -1628,7 +1652,7 @@ theorem getElem?_append_right : ∀ {l₁ l₂ : List α} {i : Nat}, l₁.length
rw [cons_append]
simp [Nat.succ_sub_succ_eq_sub, getElem?_append_right (Nat.lt_succ.1 h₁)]
@[grind] theorem getElem?_append {l₁ l₂ : List α} {i : Nat} :
@[grind =] theorem getElem?_append {l₁ l₂ : List α} {i : Nat} :
(l₁ ++ l₂)[i]? = if i < l₁.length then l₁[i]? else l₂[i - l₁.length]? := by
split <;> rename_i h
· exact getElem?_append_left h
@@ -1707,7 +1731,6 @@ theorem getLast_concat {a : α} : ∀ {l : List α}, getLast (l ++ [a]) (by simp
theorem nil_eq_append_iff : [] = a ++ b a = [] b = [] := by
simp
@[grind ]
theorem eq_nil_of_append_eq_nil {l₁ l₂ : List α} (h : l₁ ++ l₂ = []) : l₁ = [] l₂ = [] :=
append_eq_nil_iff.mp h
@@ -1737,12 +1760,12 @@ theorem append_eq_append_iff {ws xs ys zs : List α} :
| nil => simp_all
| cons a as ih => cases ys <;> simp [eq_comm, and_assoc, ih, and_or_left]
@[simp, grind] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) :
@[simp, grind =] theorem head_append_of_ne_nil {l : List α} {w₁} (w₂) :
head (l ++ l') w₁ = head l w₂ := by
match l, w₂ with
| a :: l, _ => rfl
@[grind] theorem head_append {l₁ l₂ : List α} (w : l₁ ++ l₂ []) :
@[grind =] theorem head_append {l₁ l₂ : List α} (w : l₁ ++ l₂ []) :
head (l₁ ++ l₂) w =
if h : l₁.isEmpty then
head l₂ (by simp_all [isEmpty_iff])
@@ -1763,28 +1786,28 @@ theorem head_append_right {l₁ l₂ : List α} (w : l₁ ++ l₂ ≠ []) (h : l
head (l₁ ++ l₂) w = head l₂ (by simp_all) := by
rw [head_append, dif_pos (by simp_all)]
@[simp, grind] theorem head?_append {l : List α} : (l ++ l').head? = l.head?.or l'.head? := by
@[simp, grind =] theorem head?_append {l : List α} : (l ++ l').head? = l.head?.or l'.head? := by
cases l <;> simp
-- Note:
-- `getLast_append_of_ne_nil`, `getLast_append` and `getLast?_append`
-- are stated and proved later in the `reverse` section.
@[grind] theorem tail?_append {l l' : List α} : (l ++ l').tail? = (l.tail?.map (· ++ l')).or l'.tail? := by
@[grind =] theorem tail?_append {l l' : List α} : (l ++ l').tail? = (l.tail?.map (· ++ l')).or l'.tail? := by
cases l <;> simp
theorem tail?_append_of_ne_nil {l l' : List α} (_ : l []) : (l ++ l').tail? = some (l.tail ++ l') :=
match l with
| _ :: _ => by simp
@[grind] theorem tail_append {l l' : List α} : (l ++ l').tail = if l.isEmpty then l'.tail else l.tail ++ l' := by
@[grind =] theorem tail_append {l l' : List α} : (l ++ l').tail = if l.isEmpty then l'.tail else l.tail ++ l' := by
cases l <;> simp
@[simp] theorem tail_append_of_ne_nil {xs ys : List α} (h : xs []) :
(xs ++ ys).tail = xs.tail ++ ys := by
simp_all [tail_append]
@[grind] theorem set_append {s t : List α} :
@[grind =] theorem set_append {s t : List α} :
(s ++ t).set i x = if i < s.length then s.set i x ++ t else s ++ t.set (i - s.length) x := by
induction s generalizing i with
| nil => simp
@@ -1842,7 +1865,7 @@ theorem append_eq_filter_iff {p : α → Bool} :
L₁ ++ L₂ = filter p l l₁ l₂, l = l₁ ++ l₂ filter p l₁ = L₁ filter p l₂ = L₂ := by
rw [eq_comm, filter_eq_append_iff]
@[simp, grind] theorem map_append {f : α β} : {l₁ l₂}, map f (l₁ ++ l₂) = map f l₁ ++ map f l₂ := by
@[simp, grind =] theorem map_append {f : α β} : {l₁ l₂}, map f (l₁ ++ l₂) = map f l₁ ++ map f l₂ := by
intro l₁; induction l₁ <;> intros <;> simp_all
theorem map_eq_append_iff {f : α β} :
@@ -1915,7 +1938,7 @@ theorem eq_nil_or_concat : ∀ l : List α, l = [] ∃ l' b, l = concat l' b
| cons =>
simp [flatten, length_append, *]
@[grind] theorem flatten_singleton {l : List α} : [l].flatten = l := by simp
@[grind =] theorem flatten_singleton {l : List α} : [l].flatten = l := by simp
@[simp] theorem mem_flatten : {L : List (List α)}, a L.flatten l, l L a l
| [] => by simp
@@ -2090,7 +2113,7 @@ theorem length_flatMap {l : List α} {f : α → List β} :
length (l.flatMap f) = sum (map (fun a => (f a).length) l) := by
rw [List.flatMap, length_flatten, map_map, Function.comp_def]
@[simp, grind] theorem mem_flatMap {f : α List β} {b} {l : List α} : b l.flatMap f a, a l b f a := by
@[simp, grind =] theorem mem_flatMap {f : α List β} {b} {l : List α} : b l.flatMap f a, a l b f a := by
simp [flatMap_def, mem_flatten]
exact fun _, a, h₁, rfl, h₂ => a, h₁, h₂, fun a, h₁, h₂ => _, a, h₁, rfl, h₂
@@ -2117,7 +2140,7 @@ theorem flatMap_singleton (f : α → List β) (x : α) : [x].flatMap f = f x :=
@[simp] theorem flatMap_singleton' (l : List α) : (l.flatMap fun x => [x]) = l := by
induction l <;> simp [*]
@[grind] theorem head?_flatMap {l : List α} {f : α List β} :
@[grind =] theorem head?_flatMap {l : List α} {f : α List β} :
(l.flatMap f).head? = l.findSome? fun a => (f a).head? := by
induction l with
| nil => rfl
@@ -2125,10 +2148,6 @@ theorem flatMap_singleton (f : α → List β) (x : α) : [x].flatMap f = f x :=
simp only [findSome?_cons]
split <;> simp_all
@[simp, grind _=_] theorem flatMap_append {xs ys : List α} {f : α List β} :
(xs ++ ys).flatMap f = xs.flatMap f ++ ys.flatMap f := by
induction xs; {rfl}; simp_all [flatMap_cons, append_assoc]
theorem flatMap_assoc {l : List α} {f : α List β} {g : β List γ} :
(l.flatMap f).flatMap g = l.flatMap fun x => (f x).flatMap g := by
induction l <;> simp [*]
@@ -2170,7 +2189,7 @@ theorem flatMap_eq_foldl {f : α → List β} {l : List α} :
theorem replicate_succ' : replicate (n + 1) a = replicate n a ++ [a] := by
induction n <;> simp_all [replicate_succ, cons_append]
@[simp, grind] theorem mem_replicate {a b : α} : {n}, b replicate n a n 0 b = a
@[simp, grind =] theorem mem_replicate {a b : α} : {n}, b replicate n a n 0 b = a
| 0 => by simp
| n+1 => by simp [replicate_succ, mem_replicate, Nat.succ_ne_zero]
@@ -2195,11 +2214,11 @@ theorem forall_mem_replicate {p : α → Prop} {a : α} {n} :
@[simp] theorem replicate_eq_nil_iff {n : Nat} (a : α) : replicate n a = [] n = 0 := by
cases n <;> simp
@[simp, grind] theorem getElem_replicate {a : α} {n : Nat} {i : Nat} (h : i < (replicate n a).length) :
@[simp, grind =] theorem getElem_replicate {a : α} {n : Nat} {i : Nat} (h : i < (replicate n a).length) :
(replicate n a)[i] = a :=
eq_of_mem_replicate (getElem_mem _)
@[grind] theorem getElem?_replicate : (replicate n a)[i]? = if i < n then some a else none := by
@[grind =] theorem getElem?_replicate : (replicate n a)[i]? = if i < n then some a else none := by
by_cases h : i < n
· rw [getElem?_eq_getElem (by simpa), getElem_replicate, if_pos h]
· rw [getElem?_eq_none (by simpa using h), if_neg h]
@@ -2207,7 +2226,7 @@ theorem forall_mem_replicate {p : α → Prop} {a : α} {n} :
@[simp] theorem getElem?_replicate_of_lt {n : Nat} {i : Nat} (h : i < n) : (replicate n a)[i]? = some a := by
simp [h]
@[grind] theorem head?_replicate {a : α} {n : Nat} : (replicate n a).head? = if n = 0 then none else some a := by
@[grind =] theorem head?_replicate {a : α} {n : Nat} : (replicate n a).head? = if n = 0 then none else some a := by
cases n <;> simp [replicate_succ]
@[simp] theorem head_replicate (w : replicate n a []) : (replicate n a).head w = a := by
@@ -2296,7 +2315,7 @@ theorem replicate_eq_append_iff {l₁ l₂ : List α} {a : α} :
simp only [getElem?_map, getElem?_replicate]
split <;> simp
@[grind] theorem filter_replicate : (replicate n a).filter p = if p a then replicate n a else [] := by
@[grind =] theorem filter_replicate : (replicate n a).filter p = if p a then replicate n a else [] := by
cases n with
| zero => simp
| succ n =>
@@ -2399,7 +2418,7 @@ termination_by l.length
/-! ### reverse -/
@[simp, grind] theorem length_reverse {as : List α} : (as.reverse).length = as.length := by
@[simp, grind =] theorem length_reverse {as : List α} : (as.reverse).length = as.length := by
induction as with
| nil => rfl
| cons a as ih => simp [ih]
@@ -2408,7 +2427,7 @@ theorem mem_reverseAux {x : α} : ∀ {as bs}, x ∈ reverseAux as bs ↔ x ∈
| [], _ => .inr, fun | .inr h => h
| a :: _, _ => by rw [reverseAux, mem_cons, or_assoc, or_left_comm, mem_reverseAux, mem_cons]
@[simp, grind] theorem mem_reverse {x : α} {as : List α} : x reverse as x as := by
@[simp, grind =] theorem mem_reverse {x : α} {as : List α} : x reverse as x as := by
simp [reverse, mem_reverseAux]
@[simp] theorem reverse_eq_nil_iff {xs : List α} : xs.reverse = [] xs = [] := by
@@ -2432,14 +2451,14 @@ theorem getElem?_reverse' : ∀ {l : List α} {i j}, i + j + 1 = length l →
rw [getElem?_append_left, getElem?_reverse' this]
rw [length_reverse, this]; apply Nat.lt_add_of_pos_right (Nat.succ_pos _)
@[simp, grind]
@[simp, grind =]
theorem getElem?_reverse {l : List α} {i} (h : i < length l) :
l.reverse[i]? = l[l.length - 1 - i]? :=
getElem?_reverse' <| by
rw [Nat.add_sub_of_le (Nat.le_sub_one_of_lt h),
Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) h)]
@[simp, grind]
@[simp, grind =]
theorem getElem_reverse {l : List α} {i} (h : i < l.reverse.length) :
l.reverse[i] = l[l.length - 1 - i]'(Nat.sub_one_sub_lt_of_lt (by simpa using h)) := by
apply Option.some.inj
@@ -2452,7 +2471,7 @@ theorem reverseAux_reverseAux_nil {as bs : List α} : reverseAux (reverseAux as
| cons a as ih => simp [reverseAux, ih]
-- The argument `as : List α` is explicit to allow rewriting from right to left.
@[simp, grind] theorem reverse_reverse (as : List α) : as.reverse.reverse = as := by
@[simp, grind =] theorem reverse_reverse (as : List α) : as.reverse.reverse = as := by
simp only [reverse]; rw [reverseAux_reverseAux_nil]; rfl
theorem reverse_eq_iff {as bs : List α} : as.reverse = bs as = bs.reverse := by
@@ -2465,10 +2484,10 @@ theorem reverse_eq_iff {as bs : List α} : as.reverse = bs ↔ as = bs.reverse :
xs.reverse = a :: ys xs = ys.reverse ++ [a] := by
rw [reverse_eq_iff, reverse_cons]
@[simp, grind] theorem getLast?_reverse {l : List α} : l.reverse.getLast? = l.head? := by
@[simp, grind =] theorem getLast?_reverse {l : List α} : l.reverse.getLast? = l.head? := by
cases l <;> simp [getLast?_concat]
@[simp, grind] theorem head?_reverse {l : List α} : l.reverse.head? = l.getLast? := by
@[simp, grind =] theorem head?_reverse {l : List α} : l.reverse.head? = l.getLast? := by
rw [ getLast?_reverse, reverse_reverse]
theorem getLast?_eq_head?_reverse {xs : List α} : xs.getLast? = xs.reverse.head? := by
@@ -2532,16 +2551,16 @@ theorem flatten_reverse {L : List (List α)} :
L.reverse.flatten = (L.map reverse).flatten.reverse := by
induction L <;> simp_all
@[grind] theorem reverse_flatMap {β} {l : List α} {f : α List β} : (l.flatMap f).reverse = l.reverse.flatMap (reverse f) := by
@[grind =] theorem reverse_flatMap {β} {l : List α} {f : α List β} : (l.flatMap f).reverse = l.reverse.flatMap (reverse f) := by
induction l <;> simp_all
@[grind] theorem flatMap_reverse {β} {l : List α} {f : α List β} : (l.reverse.flatMap f) = (l.flatMap (reverse f)).reverse := by
@[grind =] theorem flatMap_reverse {β} {l : List α} {f : α List β} : (l.reverse.flatMap f) = (l.flatMap (reverse f)).reverse := by
induction l <;> simp_all
@[simp] theorem reverseAux_eq {as bs : List α} : reverseAux as bs = reverse as ++ bs :=
reverseAux_eq_append ..
@[simp, grind] theorem reverse_replicate {n : Nat} {a : α} : (replicate n a).reverse = replicate n a :=
@[simp, grind =] theorem reverse_replicate {n : Nat} {a : α} : (replicate n a).reverse = replicate n a :=
eq_replicate_iff.2
by rw [length_reverse, length_replicate],
fun _ h => eq_of_mem_replicate (mem_reverse.1 h)
@@ -2553,7 +2572,7 @@ theorem flatten_reverse {L : List (List α)} :
(l ++ l').foldlM f b = l.foldlM f b >>= l'.foldlM f := by
induction l generalizing b <;> simp [*]
@[simp, grind] theorem foldrM_cons [Monad m] [LawfulMonad m] {a : α} {l : List α} {f : α β m β} {b : β} :
@[simp, grind =] theorem foldrM_cons [Monad m] [LawfulMonad m] {a : α} {l : List α} {f : α β m β} {b : β} :
(a :: l).foldrM f b = l.foldrM f b >>= f a := by
simp only [foldrM]
induction l <;> simp_all
@@ -2597,37 +2616,37 @@ theorem id_run_foldrM {f : α → β → Id β} {b : β} {l : List α} :
/-! ### foldl and foldr -/
@[simp, grind] theorem foldr_cons_eq_append {l : List α} {f : α β} {l' : List β} :
@[simp] theorem foldr_cons_eq_append {l : List α} {f : α β} {l' : List β} :
l.foldr (fun x ys => f x :: ys) l' = l.map f ++ l' := by
induction l <;> simp [*]
/-- Variant of `foldr_cons_eq_append` specalized to `f = id`. -/
@[simp, grind] theorem foldr_cons_eq_append' {l l' : List β} :
@[simp, grind =] theorem foldr_cons_eq_append' {l l' : List β} :
l.foldr cons l' = l ++ l' := by
induction l <;> simp [*]
@[simp, grind] theorem foldl_flip_cons_eq_append {l : List α} {f : α β} {l' : List β} :
@[simp] theorem foldl_flip_cons_eq_append {l : List α} {f : α β} {l' : List β} :
l.foldl (fun xs y => f y :: xs) l' = (l.map f).reverse ++ l' := by
induction l generalizing l' <;> simp [*]
/-- Variant of `foldl_flip_cons_eq_append` specalized to `f = id`. -/
@[grind] theorem foldl_flip_cons_eq_append' {l l' : List α} :
theorem foldl_flip_cons_eq_append' {l l' : List α} :
l.foldl (fun xs y => y :: xs) l' = l.reverse ++ l' := by
simp
@[simp, grind] theorem foldr_append_eq_append {l : List α} {f : α List β} {l' : List β} :
@[simp] theorem foldr_append_eq_append {l : List α} {f : α List β} {l' : List β} :
l.foldr (f · ++ ·) l' = (l.map f).flatten ++ l' := by
induction l <;> simp [*]
@[simp, grind] theorem foldl_append_eq_append {l : List α} {f : α List β} {l' : List β} :
@[simp] theorem foldl_append_eq_append {l : List α} {f : α List β} {l' : List β} :
l.foldl (· ++ f ·) l' = l' ++ (l.map f).flatten := by
induction l generalizing l'<;> simp [*]
@[simp, grind] theorem foldr_flip_append_eq_append {l : List α} {f : α List β} {l' : List β} :
@[simp] theorem foldr_flip_append_eq_append {l : List α} {f : α List β} {l' : List β} :
l.foldr (fun x ys => ys ++ f x) l' = l' ++ (l.map f).reverse.flatten := by
induction l generalizing l' <;> simp [*]
@[simp, grind] theorem foldl_flip_append_eq_append {l : List α} {f : α List β} {l' : List β} :
@[simp] theorem foldl_flip_append_eq_append {l : List α} {f : α List β} {l' : List β} :
l.foldl (fun xs y => f y ++ xs) l' = (l.map f).reverse.flatten ++ l' := by
induction l generalizing l' <;> simp [*]
@@ -2681,19 +2700,19 @@ theorem foldr_map_hom {g : α → β} {f : ααα} {f' : β → β →
@[simp, grind _=_] theorem foldr_append {f : α β β} {b : β} {l l' : List α} :
(l ++ l').foldr f b = l.foldr f (l'.foldr f b) := by simp [foldr_eq_foldrM, -foldrM_pure]
@[grind] theorem foldl_flatten {f : β α β} {b : β} {L : List (List α)} :
@[grind =] theorem foldl_flatten {f : β α β} {b : β} {L : List (List α)} :
(flatten L).foldl f b = L.foldl (fun b l => l.foldl f b) b := by
induction L generalizing b <;> simp_all
@[grind] theorem foldr_flatten {f : α β β} {b : β} {L : List (List α)} :
@[grind =] theorem foldr_flatten {f : α β β} {b : β} {L : List (List α)} :
(flatten L).foldr f b = L.foldr (fun l b => l.foldr f b) b := by
induction L <;> simp_all
@[simp, grind] theorem foldl_reverse {l : List α} {f : β α β} {b : β} :
@[simp, grind =] theorem foldl_reverse {l : List α} {f : β α β} {b : β} :
l.reverse.foldl f b = l.foldr (fun x y => f y x) b := by
simp [foldl_eq_foldlM, foldr_eq_foldrM, -foldrM_pure]
@[simp, grind] theorem foldr_reverse {l : List α} {f : α β β} {b : β} :
@[simp, grind =] theorem foldr_reverse {l : List α} {f : α β β} {b : β} :
l.reverse.foldr f b = l.foldl (fun x y => f y x) b :=
(foldl_reverse ..).symm.trans <| by simp
@@ -2847,7 +2866,7 @@ theorem foldr_rel {l : List α} {f : α → β → β} {g : αγγ} {a
/-! #### Further results about `getLast` and `getLast?` -/
@[simp, grind] theorem head_reverse {l : List α} (h : l.reverse []) :
@[simp, grind =] theorem head_reverse {l : List α} (h : l.reverse []) :
l.reverse.head h = getLast l (by simp_all) := by
induction l with
| nil => contradiction
@@ -2877,7 +2896,7 @@ theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔
rw [getLast?_eq_head?_reverse, isSome_head?]
simp
@[simp, grind] theorem getLast_reverse {l : List α} (h : l.reverse []) :
@[simp, grind =] theorem getLast_reverse {l : List α} (h : l.reverse []) :
l.reverse.getLast h = l.head (by simp_all) := by
simp [getLast_eq_head_reverse]
@@ -2890,7 +2909,7 @@ theorem head_eq_getLast_reverse {l : List α} (h : l ≠ []) :
simp only [getLast_eq_head_reverse, reverse_append]
rw [head_append_of_ne_nil]
@[grind] theorem getLast_append {l : List α} (h : l ++ l' []) :
@[grind =] theorem getLast_append {l : List α} (h : l ++ l' []) :
(l ++ l').getLast h =
if h' : l'.isEmpty then
l.getLast (by simp_all [isEmpty_iff])
@@ -2911,7 +2930,7 @@ theorem getLast_append_left {l : List α} (w : l ++ l' ≠ []) (h : l' = []) :
(l ++ l').getLast w = l.getLast (by simp_all) := by
rw [getLast_append, dif_pos (by simp_all)]
@[simp, grind] theorem getLast?_append {l l' : List α} : (l ++ l').getLast? = l'.getLast?.or l.getLast? := by
@[simp, grind =] theorem getLast?_append {l l' : List α} : (l ++ l').getLast? = l'.getLast?.or l.getLast? := by
simp [ head?_reverse]
theorem getLast_filter_of_pos {p : α Bool} {l : List α} (w : l []) (h : p (getLast l w) = true) :
@@ -2947,7 +2966,7 @@ theorem getLast?_replicate {a : α} {n : Nat} : (replicate n a).getLast? = if n
/-! ### leftpad -/
-- We unfold `leftpad` and `rightpad` for verification purposes.
attribute [simp, grind] leftpad rightpad
attribute [simp, grind =] leftpad rightpad
-- `length_leftpad` and `length_rightpad` are in `Init.Data.List.Nat.Basic`.
@@ -2976,17 +2995,21 @@ theorem contains_iff_exists_mem_beq [BEq α] {l : List α} {a : α} :
l.contains a a' l, a == a' := by
induction l <;> simp_all
-- We add this as a `grind` lemma because it is useful without `LawfulBEq α`.
-- With `LawfulBEq α`, it would be better to use `contains_iff_mem` directly.
grind_pattern contains_iff_exists_mem_beq => l.contains a
@[grind _=_]
theorem contains_iff_mem [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.contains a a l := by
simp
@[simp, grind]
@[simp, grind =]
theorem contains_map [BEq β] {l : List α} {x : β} {f : α β} :
(l.map f).contains x = l.any (fun a => x == f a) := by
induction l with simp_all
@[simp, grind]
@[simp, grind =]
theorem contains_filter [BEq α] {l : List α} {x : α} {p : α Bool} :
(l.filter p).contains x = l.any (fun a => x == a && p a) := by
induction l with
@@ -2995,7 +3018,7 @@ theorem contains_filter [BEq α] {l : List α} {x : α} {p : α → Bool} :
simp only [filter_cons, any_cons]
split <;> simp_all
@[simp, grind]
@[simp, grind =]
theorem contains_filterMap [BEq β] {l : List α} {x : β} {f : α Option β} :
(l.filterMap f).contains x = l.any (fun a => (f a).any fun b => x == b) := by
induction l with
@@ -3011,21 +3034,21 @@ theorem contains_append [BEq α] {l₁ l₂ : List α} {x : α} :
| nil => simp
| cons a l ih => simp [ih, Bool.or_assoc]
@[simp, grind]
@[simp, grind =]
theorem contains_flatten [BEq α] {l : List (List α)} {x : α} :
l.flatten.contains x = l.any fun l => l.contains x := by
induction l with
| nil => simp
| cons _ l ih => simp [ih]
@[simp, grind]
@[simp, grind =]
theorem contains_reverse [BEq α] {l : List α} {x : α} :
(l.reverse).contains x = l.contains x := by
induction l with
| nil => simp
| cons a l ih => simp [ih, Bool.or_comm]
@[simp, grind]
@[simp, grind =]
theorem contains_flatMap [BEq β] {l : List α} {f : α List β} {x : β} :
(l.flatMap f).contains x = l.any fun a => (f a).contains x := by
induction l with
@@ -3040,7 +3063,7 @@ Because we immediately simplify `partition` into two `filter`s for verification
we do not separately develop much theory about it.
-/
@[simp, grind] theorem partition_eq_filter_filter {p : α Bool} {l : List α} :
@[simp, grind =] theorem partition_eq_filter_filter {p : α Bool} {l : List α} :
partition p l = (filter p l, filter (not p) l) := by simp [partition, aux]
where
aux : l {as bs}, partition.loop p l (as, bs) =
@@ -3060,16 +3083,16 @@ grind_pattern mem_partition => a ∈ (partition p l).2
are often used for theorems about `Array.pop`.
-/
@[simp, grind] theorem length_dropLast : {xs : List α}, xs.dropLast.length = xs.length - 1
@[simp, grind =] theorem length_dropLast : {xs : List α}, xs.dropLast.length = xs.length - 1
| [] => rfl
| x::xs => by simp
@[simp, grind] theorem getElem_dropLast : {xs : List α} {i : Nat} (h : i < xs.dropLast.length),
@[simp, grind =] theorem getElem_dropLast : {xs : List α} {i : Nat} (h : i < xs.dropLast.length),
xs.dropLast[i] = xs[i]'(Nat.lt_of_lt_of_le h (length_dropLast .. Nat.pred_le _))
| _ :: _ :: _, 0, _ => rfl
| _ :: _ :: _, _ + 1, h => getElem_dropLast (Nat.add_one_lt_add_one_iff.mp h)
@[grind] theorem getElem?_dropLast {xs : List α} {i : Nat} :
@[grind =] theorem getElem?_dropLast {xs : List α} {i : Nat} :
xs.dropLast[i]? = if i < xs.length - 1 then xs[i]? else none := by
split
· rw [getElem?_eq_getElem, getElem?_eq_getElem, getElem_dropLast]
@@ -3267,24 +3290,24 @@ theorem all_eq_not_any_not {l : List α} {p : α → Bool} : l.all p = !l.any (!
| nil => rfl
| cons h t ih => simp_all [Bool.and_assoc]
@[simp, grind] theorem any_flatten {l : List (List α)} : l.flatten.any f = l.any (any · f) := by
@[simp, grind =] theorem any_flatten {l : List (List α)} : l.flatten.any f = l.any (any · f) := by
induction l <;> simp_all
@[simp, grind] theorem all_flatten {l : List (List α)} : l.flatten.all f = l.all (all · f) := by
@[simp, grind =] theorem all_flatten {l : List (List α)} : l.flatten.all f = l.all (all · f) := by
induction l <;> simp_all
@[simp, grind] theorem any_flatMap {l : List α} {f : α List β} :
@[simp, grind =] theorem any_flatMap {l : List α} {f : α List β} :
(l.flatMap f).any p = l.any fun a => (f a).any p := by
induction l <;> simp_all
@[simp, grind] theorem all_flatMap {l : List α} {f : α List β} :
@[simp, grind =] theorem all_flatMap {l : List α} {f : α List β} :
(l.flatMap f).all p = l.all fun a => (f a).all p := by
induction l <;> simp_all
@[simp, grind] theorem any_reverse {l : List α} : l.reverse.any f = l.any f := by
@[simp, grind =] theorem any_reverse {l : List α} : l.reverse.any f = l.any f := by
induction l <;> simp_all [Bool.or_comm]
@[simp, grind] theorem all_reverse {l : List α} : l.reverse.all f = l.all f := by
@[simp, grind =] theorem all_reverse {l : List α} : l.reverse.all f = l.all f := by
induction l <;> simp_all [Bool.and_comm]
@[simp] theorem any_replicate {n : Nat} {a : α} :
@@ -3334,14 +3357,14 @@ variable [BEq α]
simp only [replace_cons]
split <;> simp_all
@[simp, grind] theorem length_replace {l : List α} : (l.replace a b).length = l.length := by
@[simp, grind =] theorem length_replace {l : List α} : (l.replace a b).length = l.length := by
induction l with
| nil => simp
| cons x l ih =>
simp only [replace_cons]
split <;> simp_all
@[grind] theorem getElem?_replace [LawfulBEq α] {l : List α} {i : Nat} :
@[grind =] theorem getElem?_replace [LawfulBEq α] {l : List α} {i : Nat} :
(l.replace a b)[i]? = if l[i]? == some a then if a l.take i then some a else some b else l[i]? := by
induction l generalizing i with
| nil => cases i <;> simp
@@ -3354,7 +3377,7 @@ theorem getElem?_replace_of_ne [LawfulBEq α] {l : List α} {i : Nat} (h : l[i]?
(l.replace a b)[i]? = l[i]? := by
simp_all [getElem?_replace]
@[grind] theorem getElem_replace [LawfulBEq α] {l : List α} {i : Nat} (h : i < l.length) :
@[grind =] theorem getElem_replace [LawfulBEq α] {l : List α} {i : Nat} (h : i < l.length) :
(l.replace a b)[i]'(by simpa) = if l[i] == a then if a l.take i then a else b else l[i] := by
apply Option.some.inj
rw [ getElem?_eq_getElem, getElem?_replace]
@@ -3384,7 +3407,7 @@ theorem head_replace {l : List α} {a b : α} (w) :
apply Option.some.inj
rw [ head?_eq_head, head?_replace, head?_eq_head]
@[grind] theorem replace_append [LawfulBEq α] {l₁ l₂ : List α} :
@[grind =] theorem replace_append [LawfulBEq α] {l₁ l₂ : List α} :
(l₁ ++ l₂).replace a b = if a l₁ then l₁.replace a b ++ l₂ else l₁ ++ l₂.replace a b := by
induction l₁ with
| nil => simp
@@ -3428,9 +3451,9 @@ end replace
section insert
variable [BEq α]
@[simp, grind] theorem insert_nil (a : α) : [].insert a = [a] := rfl
@[simp, grind =] theorem insert_nil (a : α) : [].insert a = [a] := rfl
@[simp, grind] theorem contains_insert [PartialEquivBEq α] {l : List α} {a : α} {x : α} :
@[simp, grind =] theorem contains_insert [PartialEquivBEq α] {l : List α} {a : α} {x : α} :
(l.insert a).contains x = (x == a || l.contains x) := by
simp only [List.insert]
split <;> rename_i h
@@ -3447,7 +3470,7 @@ variable [LawfulBEq α]
@[simp] theorem insert_of_not_mem {l : List α} (h : a l) : l.insert a = a :: l := by
simp [List.insert, h]
@[simp, grind] theorem mem_insert_iff {l : List α} : a l.insert b a = b a l := by
@[simp, grind =] theorem mem_insert_iff {l : List α} : a l.insert b a = b a l := by
if h : b l then
rw [insert_of_mem h]
constructor; {apply Or.inr}
@@ -3471,7 +3494,7 @@ theorem eq_or_mem_of_mem_insert {l : List α} (h : a ∈ l.insert b) : a = b
@[simp] theorem length_insert_of_not_mem {l : List α} (h : a l) :
length (l.insert a) = length l + 1 := by rw [insert_of_not_mem h]; rfl
@[grind] theorem length_insert {l : List α} :
@[grind =] theorem length_insert {l : List α} :
(l.insert a).length = l.length + if a l then 0 else 1 := by
split <;> simp_all
@@ -3506,13 +3529,13 @@ theorem getElem?_insert_succ {l : List α} {a : α} {i : Nat} :
simp only [insert_eq]
split <;> simp
@[grind] theorem getElem?_insert {l : List α} {a : α} {i : Nat} :
@[grind =] theorem getElem?_insert {l : List α} {a : α} {i : Nat} :
(l.insert a)[i]? = if a l then l[i]? else if i = 0 then some a else l[i-1]? := by
cases i
· simp [getElem?_insert_zero]
· simp [getElem?_insert_succ]
@[grind] theorem getElem_insert {l : List α} {a : α} {i : Nat} (h : i < l.length) :
@[grind =] theorem getElem_insert {l : List α} {a : α} {i : Nat} (h : i < l.length) :
(l.insert a)[i]'(Nat.lt_of_lt_of_le h length_le_length_insert) =
if a l then l[i] else if i = 0 then a else l[i-1]'(Nat.lt_of_le_of_lt (Nat.pred_le _) h) := by
apply Option.some.inj
@@ -3536,7 +3559,7 @@ theorem head_insert {l : List α} {a : α} (w) :
apply Option.some.inj
rw [ head?_eq_head, head?_insert]
@[grind] theorem insert_append {l₁ l₂ : List α} {a : α} :
@[grind =] theorem insert_append {l₁ l₂ : List α} {a : α} :
(l₁ ++ l₂).insert a = if a l₂ then l₁ ++ l₂ else l₁.insert a ++ l₂ := by
simp only [insert_eq, mem_append]
(repeat split) <;> simp_all
@@ -3549,7 +3572,7 @@ theorem insert_append_of_not_mem_left {l₁ l₂ : List α} (h : ¬ a ∈ l₂)
(l₁ ++ l₂).insert a = l₁.insert a ++ l₂ := by
simp [insert_append, h]
@[simp, grind] theorem insert_replicate_self {a : α} (h : 0 < n) : (replicate n a).insert a = replicate n a := by
@[simp, grind =] theorem insert_replicate_self {a : α} (h : 0 < n) : (replicate n a).insert a = replicate n a := by
cases n <;> simp_all
@[simp] theorem insert_replicate_ne {a b : α} (h : !b == a) :

View File

@@ -10,7 +10,8 @@ public import Init.Data.List.TakeDrop
public import Init.Data.List.Attach
public import Init.Data.List.OfFn
public import Init.Data.Array.Bootstrap
public import all Init.Data.List.Control
public import Init.Data.List.Control
import all Init.Data.List.Control
public section

View File

@@ -248,11 +248,10 @@ theorem pairwise_le_range {n : Nat} : Pairwise (· ≤ ·) (range n) :=
theorem nodup_range {n : Nat} : Nodup (range n) := by
simp +decide only [range_eq_range', nodup_range']
@[simp, grind] theorem find?_range_eq_some {n : Nat} {i : Nat} {p : Nat Bool} :
@[simp] theorem find?_range_eq_some {n : Nat} {i : Nat} {p : Nat Bool} :
(range n).find? p = some i p i i range n j, j < i !p j := by
simp [range_eq_range']
@[grind]
theorem find?_range_eq_none {n : Nat} {p : Nat Bool} :
(range n).find? p = none i, i < n !p i := by
simp

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