Compare commits

...

194 Commits

Author SHA1 Message Date
Kim Morrison
838f3593fb . 2025-06-18 12:24:02 +10:00
Kim Morrison
2f7ba07e67 deprecations 2025-06-18 12:23:59 +10:00
Kim Morrison
5778df510d . 2025-06-18 12:23:35 +10:00
Kim Morrison
846dbda4bc . 2025-06-18 12:23:35 +10:00
Kim Morrison
dec10fd2dd fix: correct Lean.Grind.NatModule 2025-06-18 12:23:32 +10:00
Kim Morrison
8f7d090829 . 2025-06-18 12:23:25 +10:00
Kim Morrison
8a34ea1004 experiments 2025-06-18 12:23:25 +10:00
Kim Morrison
d6adde535f chore: missing Nat lemmas 2025-06-18 12:23:18 +10:00
Kim Morrison
10c4d3f379 wip 2025-06-18 12:22:23 +10:00
Sebastian Ullrich
e129e75e66 chore: CI: temporarily disable .olean cache 2025-06-18 11:12:56 +09:00
Lean stage0 autoupdater
04c273dbc6 chore: update stage0 2025-06-18 02:09:11 +00:00
Leonardo de Moura
2b39b453e7 feat: proof-by-reflection support for converting semiring terms into ring ones (#8845)
This PR implements the proof-by-reflection infrastructure for embedding
semiring terms as ring ones.
2025-06-17 19:24:15 +00:00
Luisa Cicolini
43aaae7348 feat: add BitVec.(toNat, toInt, toFin)_shiftLeftZeroExtend (#8811)
This PR adds theorems `BitVec.(toNat, toInt,
toFin)_shiftLeftZeroExtend`, completing the API for
`BitVec.shiftLeftZeroExtend`.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-06-17 17:43:40 +00:00
Parth Shastri
92dec7e864 feat: allow structures to have non-bracketed binders (#8671)
This PR allow structures to have non-bracketed binders, making it
consistent with `inductive`.

The change allows the following to be written instead of having to write
`S (n)`:
```lean
structure S n where
  field : Fin n
```
2025-06-17 17:40:18 +00:00
Kim Morrison
b3a53d5d01 feat: generalize embedding of CommSemiring into its CommRing envelope to the noncommutative case (#8836)
This PR generalizes #8835 to the noncommutative case, allowing us to
embed a `Lean.Grind.Semiring` into a `Lean.Grind.Ring`.
2025-06-17 09:09:05 +00:00
Sebastian Ullrich
3b2990b381 chore: CI: work around test-speedcenter breaking on ubuntu-latest 2025-06-17 18:14:12 +09:00
Parth Shastri
17b133369d refactor: remove binductionOn, use brecOn instead (#8820)
This PR removes the auto-generated `binductionOn` and `ibelow`
implementations for inductive types in favor of the improved `brecOn`
implementation from #7639.
2025-06-17 07:07:24 +00:00
Kim Morrison
259e2ec3e8 feat: define the CommRing envelope of a CommSemiring (#8835)
This PR defines the embedding of a `CommSemiring` into its `CommRing`
envelope, injective when the `CommSemiring` is cancellative. This will
be used by `grind` to prove results in `Nat`.
2025-06-17 05:39:14 +00:00
Cameron Zwarich
c9d0af1d7e chore: delete tests/lean/run/CompilerProbe.lean (#8833)
This test is essentially disabled on `master`, because it prints
nothing. With the new compiler enabled, it prints names of functions
throughout the Lean codebase satisfying certain conditions. Even just
maintaining this on the new compiler branch got old pretty quickly, so I
can't imagine we'd ever want to deal with this on `master`.
2025-06-17 05:01:12 +00:00
Cameron Zwarich
471553102c chore: delete redundant copy of unhygienicCode test (#8832)
This copy in `lean/new-compiler` is the same as the copy In `lean`, just
with different IR printing settings.
2025-06-17 04:57:07 +00:00
Cameron Zwarich
18caad9756 fix: cache scalar type info in toIR (#8831)
This PR caches the result of `lowerEnumToScalarType`, which is used
heavily in LCNF to IR conversion.
2025-06-17 04:31:33 +00:00
Kim Morrison
f557bf6024 chore: move grind algebra instances into Init.GrindInstances (#8830)
This PR rearranges files under `Init.Grind`, moving out instances for
concrete algebraic types in `Init.GrindInstances`.
2025-06-17 03:59:15 +00:00
Kim Morrison
548cc4e555 chore: reorganize BitVec files (#8829)
This PR avoids importing all of `BitVec.Lemmas` and `BitVec.BitBlast`
into `UInt.Lemmas`. (They are still imported into `SInt.Lemmas`; this
seems much harder to avoid.)
2025-06-17 03:30:35 +00:00
Kim Morrison
38fb9c5328 chore: rename BitVec.getLsb' back to BitVec.getLsb (#8827)
This PR renames `BitVec.getLsb'` to `BitVec.getLsb`, now that older
deprecated definition occupying that name has been removed. (Similarly
for `BitVec.getMsb'`.)
2025-06-17 01:28:07 +00:00
Kim Morrison
ba39fd3ca8 fix: correct Lean.Grind.NatModule (#8826)
This PR corrects the definition of `Lean.Grind.NatModule`, which wasn't
previously useful.
2025-06-17 01:00:48 +00:00
Kim Morrison
fcb3b2ec66 chore: raise internal grind limits to allow examples (#8807) 2025-06-17 01:00:17 +00:00
Cameron Zwarich
e1408d29bc fix: improve IR for inductive types represented as scalars (#8825)
This PR improves IR generation for constructors of inductive types that
are represented by scalars. Surprisingly, this isn't required for
correctness, because the boxing pass will fix it up. The extra `unbox`
operation it inserts shouldn't matter when compiling to native code,
because it's trivial for a C compiler to optimize, but it does matter
for the interpreter.
2025-06-16 23:52:50 +00:00
Cameron Zwarich
9e913a29de chore: remove redundant headBeta call (#8824) 2025-06-16 23:13:07 +00:00
Cameron Zwarich
46c3eaece9 fix: add a cache for constructor info in toIR (#8822)
This PR adds a cache for constructor info in toIR. This is called for
all constructors, projections, and cases alternatives, so it makes sense
to cache.
2025-06-16 22:56:27 +00:00
Joachim Breitner
47c294b3a9 chore: make Linux Release CI job secondary (#8818)
Follow-up to #8817.
2025-06-16 21:29:07 +00:00
Joachim Breitner
5568e06160 chore: Run “Linux release” for PRs as secondary job (#8817)
This PR reifnes #8739 to make sure we get a linux release in the PR
release.
2025-06-16 20:45:10 +00:00
Parth Shastri
ed4195778d chore: remove unused syntax (#8760)
Removes unused `tailrecursion` syntax.
2025-06-16 20:36:56 +00:00
Cameron Zwarich
997892d49a fix: constant fold Char.ofNat in LCNF simp (#8816)
This PR adds constant folding for Char.ofNat in LCNF simp. This
implicitly relies on the representation of `Char` as `UInt32` rather
than making a separate `.char` literal type, which seems reasonable as
`Char` is erased by the trivial structure optimization in `toMono`.
2025-06-16 17:48:55 +00:00
Parth Shastri
e07ed1ae5c chore: add missing instance (#8772)
Changes `ReverseImplicationOrder.instCompleteLattice` to be an
`instance`.
2025-06-16 15:44:56 +00:00
Kim Morrison
d247297214 feat: lemmas about ordered modules (#8813)
This PR adds some basic lemmas about `grind` internal notions of
modules.
2025-06-16 13:05:38 +00:00
Sebastian Ullrich
242429a262 chore: CI: provide more than 8GB RAM (#8812)
We started running into OOMs in the test suite. This is the faster
alternative to lowering test parallelism.
2025-06-16 11:58:06 +00:00
Kim Morrison
d9b2a5e9f7 feat: additional grind annotations for List/Array/Vector lemmas (#8805)
This PR continues adding `grind` annotations for `List/Array/Vector`
lemmas.
2025-06-16 11:00:51 +00:00
Leonardo de Moura
4e96a4ff45 feat: eliminate equations in grind linarith (#8810)
This PR implements equality elimination in `grind linarith`. The current
implementation supports only `IntModule` and `IntModule` +
`NoNatZeroDivisors`
2025-06-16 09:31:13 +00:00
Kim Morrison
7b67727067 feat: do not report metaprogramming declarations via exact? and rw? (#6672)
This PR filters out all declarations from `Lean.*`, `*.Tactic.*`, and
`*.Linter.*` from the results of `exact?` and `rw?`.

---------

Co-authored-by: damiano <adomani@gmail.com>
Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-06-16 09:20:49 +00:00
David Thrane Christiansen
8ed6824b75 chore: follow up on #8173 post-stage0 update (#8722)
This PR un-does the temporary changes made in #8173 for bootstrapping
purposes.
2025-06-16 09:08:35 +00:00
Kim Morrison
fdf6d2ea3b feat: basic theory of ordered modules over Nat (#8809)
This PR introduces the basic theory of ordered modules over Nat (i.e.
without subtraction), for `grind`. We'll solve problems here by
embedding them in the `IntModule` envelope.
2025-06-16 06:46:03 +00:00
Kim Morrison
dc531a1740 feat: missing Nat lemmas (#8808)
This PR adds the missing `le_of_add_left_le {n m k : Nat} (h : k + n ≤
m) : n ≤ m` and `le_add_left_of_le {n m k : Nat} (h : n ≤ m) : n ≤ k +
m`.
2025-06-16 06:43:37 +00:00
Kim Morrison
ddff851294 chore: cleanup of grind tests (#8806) 2025-06-16 02:47:46 +00:00
Cameron Zwarich
db414957a0 chore: fix if/else indentation (#8803) 2025-06-15 23:03:52 +00:00
Kim Morrison
114fa440f0 feat: grind annotations for List.Perm (#8765)
This PR adds grind annotations for `List.Perm`; involves a revision of
grind annotations for `List.countP/count` as well.
2025-06-15 23:01:29 +00:00
Cameron Zwarich
aa988bb892 fix: prevent floatLetIn from artificially blocking code motion (#8802)
This PR fixes a bug in `floatLetIn` where if one decl (e.g. a join
point) is floated into a case arm and it uses another decl (e.g. another
join point) that does not have any other existing uses in that arm, then
the second decl does not get floated in despite this being perfectly
legal. This was causing artificial array linearity issues in
`Lean.Elab.Tactic.BVDecide.LRAT.trim.useAnalysis`.
2025-06-15 22:19:38 +00:00
Leonardo de Moura
e2a947c2e6 feat: track occurrences in linarith (#8801)
This PR implements the infrastructure for variable elimination in the
`grind linarith` procedure.
2025-06-15 18:21:50 +00:00
Leonardo de Moura
26946ddc7f feat: Inv.lean for grind linarith (#8800) 2025-06-15 17:50:43 +00:00
Cameron Zwarich
0bfd95dd20 chore: improve readability of map/fold calls (#8799) 2025-06-15 14:15:11 +00:00
Sebastian Ullrich
957b904ef9 chore: revert "fix: add terminfo for structure fields (#8568)"
This reverts commit 021c21a273 because of a stage 2 linter failure.
2025-06-15 13:39:01 +02:00
Leonardo de Moura
1835f190c7 feat: add instance IsCharP R 0 for a linear ordered field R (#8798)
This PR adds the following instance
```
instance [Field α] [LinearOrder α] [Ring.IsOrdered α] : IsCharP α 0
```
The goal is to ensure we do not perform unnecessary case-splits in our
test suite.
2025-06-15 05:04:58 +00:00
Leonardo de Moura
f86560d134 fix: grind bogus warning and missing normalization rule (#8797)
This PR adds small fixes
2025-06-15 03:44:53 +00:00
Leonardo de Moura
cc3dafe67a fix: grind linarith internalization and HSMul support (#8796)
This PR fixes `grind linarith` term internalization and support for
`HSMul`.
2025-06-15 02:34:42 +00:00
Leonardo de Moura
5bc5d31fd9 fix: grind ring + linarith internalization (#8795)
This PR ensures that auxliary terms are not internalized by the ring and
linarith modules.
2025-06-15 01:49:37 +00:00
Leonardo de Moura
ac0c59caae feat: enable linarith even if no order is available (#8791)
This PR ensures the `grind linarith` module is activated for any type
that implements only `IntModule`. That is, the type does not need to be
a preorder anymore.
2025-06-15 01:20:49 +00:00
Mac Malone
2a8cd373ca feat: respect lean --setup module name in code generation (#8780)
This PR makes Lean code generation respect the module name provided
through `lean --setup`.

This is accomplished by porting to Lean the portion of `shell.cpp` that
spans running the frontend to exiting the process. This makes it easier
to load the module setup and control how its name is passed to the code
generation functions. This port attempts to minimize the changes made to
Lean. It marks the new Lean functions `private` and tries to preserve as
faithfully as possible the behavior of the original C++ code. Exposing
the new Lean interface publicly and/or further improving the code now
that is written in Lean is left for the future.
2025-06-15 01:11:58 +00:00
Kyle Miller
ef6386b8a9 feat: Expr.collectLooseBVars (#8794)
This PR adds a module `Lean.Util.CollectLooseBVars` with a function
`Expr.collectLooseBVars` that collects the set of loose bound variables
in an expression. That is, it computes the set of all `i` such that
`e.hasLooseBVar i` is true.
2025-06-15 00:16:43 +00:00
Kyle Miller
021c21a273 fix: add terminfo for structure fields (#8568)
This PR modifies the `structure` elaborator to add local terminfo for
structure fields and explicit parent projections, enabling "go to
definition" when there are dependent fields.

Terminfo for inherited fields is still missing.
2025-06-15 00:11:47 +00:00
Cameron Zwarich
286ddf5e28 chore: fix confusing indentation (#8793) 2025-06-15 00:07:48 +00:00
Kyle Miller
97bc609e77 feat: add have forms of let_* simp lemmas (#8790)
This PR adds `have` forms of simp lemmas that will be used in a future
`have` simplifier. This depends on #8751 and future elaboration changes,
since these are meant to elaborate using `Expr.letE (nondep := true) ..`
expressions; for now they are duplicates of the `letFun_*` lemmas.
2025-06-14 23:15:10 +00:00
Kyle Miller
cdc923167e feat: add the nondep field of Expr.letE to the C++ data model (#8751)
This PR adds the `nondep` field of `Expr.letE` to the C++ data model.
Previously this field has been unused, and in followup PRs the
elaborator will use it to encode `have` expressions (non-dependent
`let`s). The kernel does not verify that `nondep` is correctly applied
during typechecking. The `letE` delaborator now prints `have`s when
`nondep` is true, though `have` still elaborates as `letFun` for now.
Breaking change: `Expr.updateLet!` is renamed to `Expr.updateLetE!`.

This PR also fixes a bug in `Expr.letFun?` and `Expr.letFunAppArgs?`
when the body is not a lambda. In any case, these functions will be
removed once the `Expr.letE (nondep := true)` encoding of `have`
expressions is complete.
2025-06-14 23:10:27 +00:00
Leonardo de Moura
1d971c8735 feat: Rabinowitsch transformation in grind (#8789)
This PR implements the Rabinowitsch transformation for `Field`
disequalities in `grind`. For example, this transformation is necessary
for solving:
```lean
example [Field α] (a : α) : a^2 = 0 → a = 0 := by
  grind
```
2025-06-14 22:22:40 +00:00
Kyle Miller
82c2c4cd51 feat: add zetaHave/letToHave simp options (#8788)
This PR adds the `zetaHave` and `letToHave` options to `simp`.
Implementations will appear in future PRs.
2025-06-14 21:26:36 +00:00
Leonardo de Moura
019ea2a74b feat: improve support for Field in grind (#8786)
This PR improves the support for fields in `grind`. New supported
examples:
```lean
example [Field α] [IsCharP α 0] (x : α) : x ≠ 0 → (4 / x)⁻¹ * ((3 * x^3) / x)^2 * ((1 / (2 * x))⁻¹)^3 = 18 * x^8 := by grind
example [Field α] (a : α) : 2 * a ≠ 0 → 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] [IsCharP α 0] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] [IsCharP α 0] (a b : α) : 2*b - a = a + b → 1 / a + 1 / (2 * a) = 3 / b := by grind
example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] {x y z w : α} : x / y = z / w → y ≠ 0 → w ≠ 0 → x * w = z * y := by grind
example [Field α] (a : α) : a = 0 → a ≠ 1 := by grind
example [Field α] (a : α) : a = 0 → a ≠ 1 - a := by grind
```
2025-06-14 19:29:02 +00:00
Sebastian Ullrich
ec9ff12fc6 fix: meta tag can be added async (#8783) 2025-06-14 11:19:35 +00:00
Cameron Zwarich
444595878b chore: improve clarity in a match expression (#8781) 2025-06-14 00:53:12 +00:00
Cameron Zwarich
3d3aa98c83 chore: use FVarIdHashSet for the visited set in LCNF closure computation (#8779) 2025-06-13 23:39:16 +00:00
Cameron Zwarich
27080dca35 chore: use FVarIdHashSet in LCNF collectUsed (#8778) 2025-06-13 22:55:15 +00:00
Leonardo de Moura
aef4a29148 feat: Field support in grind ring (#8777)
This PR implements basic `Field` support in the commutative ring module
in `grind`. It is just division by numerals for now. Examples:
```lean
open Lean Grind

example [Field α] [IsCharP α 0] (a b c : α) : a/3 = b → c = a/3 → a/2 + a/2 = b + 2*c  := by
  grind

example [Field α] (a b : α) : b = 0 → (a + a) / 0 = b := by
  grind

example [Field α] [IsCharP α 3] (a b : α) : a/3 = b → b = 0 := by
  grind

example [Field α] [IsCharP α 7] (a b c : α) : a/3 = b → c = a/3 → a/2 + a/2 = b + 2*c + 7 := by
  grind

example [Field R] [IsCharP R 0] (x : R) (cos : R → R) :
    (cos x ^ 2 + (2 * cos x ^ 2 - 1) ^ 2 + (4 * cos x ^ 3 - 3 * cos x) ^ 2 - 1) / 4 =
      cos x * (cos x ^ 2 - 1 / 2) * (4 * cos x ^ 3 - 3 * cos x) := by
  grind
```
2025-06-13 22:42:49 +00:00
Parth Shastri
5d50433e6a fix: allow arbitrary sorts in structural recursion over reflexive inductive types (#7639)
This PR changes the generated `below` and `brecOn` implementations for
reflexive inductive types to support motives in `Sort u` rather than
`Type u`.

Closes #7638
2025-06-13 21:51:09 +00:00
Rob23oba
812bab6910 chore: convert ExtDHashMap into a one-field structure (#8770) 2025-06-13 20:22:20 +00:00
Leonardo de Moura
ff6eb56f5c fix: natCast in grind cutsat (#8776)
This PR ensures that user provided `natCast` application are properly
internalized in the grind cutsat module.
2025-06-13 17:56:00 +00:00
Leonardo de Moura
4b7ea26d91 fix: add grind normalization theorem for Int.negSucc (#8775)
This PR adds a `grind` normalization theorem for `Int.negSucc`. Example:

```lean
example (p : Int) (n : Nat) (hmp : Int.negSucc (n + 1) + 1 = p)
    (hnm : Int.negSucc (n + 1 + 1) + 1 = Int.negSucc (n + 1)) : p = Int.negSucc n := by
  grind
```
2025-06-13 16:53:42 +00:00
Leonardo de Moura
32eedc2c22 feat: grind -cutsat (#8774)
This PR adds an option for disabling the cutsat procedure in `grind`.
The linarith module takes over linear integer/nat constraints. Example:

```lean
set_option trace.grind.cutsat.assert true in -- cutsat should **not** process the following constraints
example (x y z : Int) (h1 : 2 * x < 3 * y) (h2 : -4 * x + 2 * z < 0) : ¬ 12*y - 4* z < 0 := by
  grind -cutsat -- `linarith` module solves it
```
2025-06-13 16:40:44 +00:00
Leonardo de Moura
95e532a536 feat: heterogeneous (k : Nat) * (a : R) support in grind linarith (#8773)
This PR implements support for the heterogeneous `(k : Nat) * (a : R)`
in ordered modules. Example:
```lean
variable (R : Type u) [IntModule R] [LinearOrder R] [IntModule.IsOrdered R]

example (x y z : R) (hx : x ≤ 3 * y) (h2 : y ≤ 2 * z) (h3 : x ≥ 6 * z) : x = 3 * y := by
  grind

example (x y z : Int) (h1 : 2 * x < 3 * y) (h2 : -4 * x + 2 * z < 0) (h3 : x * y < 5) : ¬ 12*y - 4* z < 0 := by
  grind
```
2025-06-13 16:18:24 +00:00
plp127
cceabbbe7e fix: quoting single quote Char (''') (#8742)
This PR fixes a bug where the single-quote character `Char.ofNat 39`
would delaborate as `'''`, which causes a parse error if pasted back in
to the source code.

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
2025-06-13 15:29:11 +00:00
Cameron Zwarich
8019c6cc32 chore: add .dSYM files (Mac debug symbols) to tests .gitignore files (#8771) 2025-06-13 15:27:46 +00:00
Parth Shastri
5390cdbee1 fix: correctly handle explicit monotonicity proofs in mutual definitions (#8763)
This PR corrects the handling of explicit `monotonicity` proofs for
mutual `partial_fixpoint` definitions.
2025-06-13 15:04:13 +00:00
Rob23oba
e713232623 fix: resolve symbolic links through IO.FS.realPath on windows (#8534)
This PR fixes `IO.FS.realPath` on windows to take symbolic links into
account.

Closes #810
2025-06-13 13:16:13 +00:00
Justin King
0d0da768d8 perf: update free_sized declaration to be compatible with glibc (#8661)
glibc adds `__attribute__((nothrow))` to its declarations, at least for
those related to malloc. glibc has yet to introduce `free_sized`, but
when it does it would cause compilation errors. This is due to the fact
that if a function declarations has `__attribute__((nothrow))` and it is
re-declared or implemented in C++ it must also have
`__attribute__((nothrow))` or `noexcept`, otherwise the compilation will
fail.

This is a follow up to https://github.com/leanprover/lean4/pull/6598.

Signed-off-by: Justin King <jcking@google.com>
2025-06-13 13:13:00 +00:00
Sebastian Ullrich
3feb63231e chore: merge-checkout test fixed and removed from exclusions on master 2025-06-13 15:30:58 +02:00
Sebastian Ullrich
121ce56506 chore: CI: make "Linux Lake" primary PR CI job (#8739)
Comes with .olean caching and module system-powered short-circuiting
2025-06-13 11:27:49 +00:00
Luisa Cicolini
300c22a4e6 feat: associativity lemmas for BitVec.(umul, smul, uadd, sadd)Overflow (#8740)
This PR introduces associativity rules and preservation of `(umul, smul,
uadd, sadd)Overflow`flags.

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-13 09:07:09 +00:00
Cameron Zwarich
f247f2bdd0 fix: run LCNF checks less often by default (#8764)
This PR changes the LCNF pass pipeline so checks are no longer run by
default after every pass, only after `init`, `saveBase`, `toMono` and
`saveMono`. This is a compile time improvement, and the utility of these
checks is decreased a bit after the decision to no longer attempt to
preserve types throughout compilation. They have not been a significant
way to discover issues during development of the new compiler.
2025-06-13 05:39:21 +00:00
Kim Morrison
db5bd5a205 chore: missing easy Int lemmas (#8762) 2025-06-13 04:20:47 +00:00
Leonardo de Moura
140a633589 feat: model based theory combination for grind mbtc (#8759)
This PR implements model-based theory combination for grind linarith.
Example:
```lean
example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (f : α → α → α) (x y z : α)
    : z ≤ x → x ≤ 1 → z = 1 → f x y = 2 → f 1 y = 2 := by
  grind
```
2025-06-13 01:20:45 +00:00
Cameron Zwarich
3aa479fd8c fix: cache TrivialStructureInfo in LCNF toMono (#8758)
This PR adds caching for the `hasTrivialStructure?` function for LCNF
types. This is one of the hottest small functions in the new compiler,
so adding a cache makes a lot of sense.
2025-06-13 01:07:38 +00:00
Kim Morrison
b280b83c98 chore: add test case with bad grind pattern (#8757) 2025-06-13 01:06:02 +00:00
Kyle Miller
84f15ac93a fix: refine how simp tracks unfolded local definitions (#8753)
This PR fixes a bug in `simp` where it was not resetting the set of
zeta-delta reduced let definitions between `simp` calls. It also fixes a
bug where `simp` would report zeta-delta reduced let definitions that
weren't given as simp arguments (these extraneous let definitions appear
due to certain processes temporarily setting `zetaDelta := true`). This
PR also modifies the metaprogramming interface for the zeta-delta
tracking functions to be re-entrant and to prevent this kind of no-reset
bug from occurring again. Closes #6655.

Re-entrance of this metaprogramming interface is not needed to fix
#6655, but it is needed for some future PRs.

The `tests/lean/run/6655.lean` file has an example of a deficiency of
`simp?`, where `simp?` still over-reports unfolded let declarations.
This is likely due to `withInferTypeConfig` setting `zetaDelta := true`
from within `isDefEq`, but I did not verify this.

This PR supersedes #7539. The difference is that this PR has
`withResetZetaDeltaFVarIds` save and restore `zetaDeltaFVarIds`, but
that PR saves and then extends `zetaDeltaFVarIds` to persist unfolded
fvars. The behavior in this PR lets metaprograms control whether they
want to persist any of the unfolded fvars in this context themselves. In
practice, metaprograms that use `withResetZetaDeltaFVarIds` are creating
many temporary fvars and are doing dependence computations. These
temporary fvars shouldn't be persisted, and also dependence shouldn't be
inferred from the fact that a dependence calculation was done. (Concrete
example: the let-to-have transformation in an upcoming PR can be run
from within simp. Just because let-to-have unfolds an fvar while
calculating dependencies of lets doesn't mean that this fvar should be
included by `simp?`.)
2025-06-13 00:57:57 +00:00
Leonardo de Moura
d4b17b9fd2 feat: counterexamples for grind linarith module (#8756)
This PR implements counterexamples for grind linarith. Example:
```lean
example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (a b c d : α)
    : b ≥ 0 → c > b → d > b → a ≠ b + c → a > b + c → a < b + d →  False := by
  grind
```
produces the counterexample
```
a := 7/2
b := 1
c := 2
d := 3
```

```lean
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c d : α)
    : a ≤ b → a - c ≥ 0 + d → d ≤ 0 → b = c → a ≠ b → False := by
  grind
```
generates the counterexample
```
a := 0
b := 1
c := 1
d := -1
```
2025-06-13 00:21:35 +00:00
Cameron Zwarich
4694aaad02 chore: rewrite mkFieldParamsForCtorType in a more readable style (#8755) 2025-06-12 23:54:30 +00:00
Rob23oba
e450a02621 fix: change show tactic to work as documented (#7395)
This PR changes the `show t` tactic to match its documentation.
Previously it was a synonym for `change t`, but now it finds the first
goal that unifies with the term `t` and moves it to the front of the
goal list.
2025-06-12 23:54:09 +00:00
Cameron Zwarich
deda28e6e3 fix: enable more optimizations on inductives with computed fields in the new compiler (#8754)
This PR changes the implementation of computed fields in the new
compiler, which should enable more optimizations (and remove a
questionable hack in `toLCNF` that was only suitable for bringup). We
convert `casesOn` to `cases` like we do for other inductive types, all
constructors get replaced by their real implementations late in the base
phase, and then the `cases` expression is rewritten to use the real
constructors in `toMono`.

In the future, it might be better to move to a model where the `cases`
expression gets rewritten earlier or the constructors get replaced
later, so that both are done at the same time.
2025-06-12 23:28:09 +00:00
Cameron Zwarich
8aa003bdfc fix: move structProjCases pass before extendJoinPointContext (#8752)
This PR fixes an issue where the `extendJoinPointContext` pass can lift
join points containing projections to the top level, as siblings of
`cases` constructs matching on other projections of the same base value.
This prevents the `structProjCases` pass from projecting both at once,
extending the lifetime of the parent value and breaking linearity at
runtime.

This would theoretically be possible to fix in `structProjCases`, but it
would require some better infrastructure for handling join points. It's
also likely that the IR passes dealing with reference counting would
have similar bugs that pessimize the code. For this reason, the simplest
thing is to just perform the `structProjCases` pass earlier, which
prevents `extendJoinPointContext` from lifting these join points.
2025-06-12 21:52:02 +00:00
Kim Morrison
6a698c1c22 feat: grind annotations for List/Array/Vector.zip functions (#8750)
This PR adds grind annotations for the
`List/Array/Vector.zipWith/zipWithAll/unzip` functions.
2025-06-12 18:41:24 +00:00
Kim Morrison
b4660c96a9 feat: grind annotations for List/Array/Vector.ofFn theorems and List.Impl (#8749)
This PR adds grind annotations for `List/Array/Vector.ofFn` theorems and
additional `List.Impl` find operations.

The annotations are added to theorems that correspond to those already
annotated in the List implementation, ensuring consistency across all
three container types (List, Array, Vector) for ofFn operations and
related functionality.

Key theorems annotated include:
- Element access theorems (`getElem_ofFn`, `getElem?_ofFn`)
- Construction and conversion theorems (`ofFn_zero`, `toList_ofFn`,
`toArray_ofFn`)
- Membership theorems (`mem_ofFn`)
- Head/tail operations (`back_ofFn`)
- Monadic operations (`ofFnM_zero`, `toList_ofFnM`, `toArray_ofFnM`,
`idRun_ofFnM`)
- List.Impl find operations (`find?_singleton`, `find?_append`,
`findSome?_singleton`, `findSome?_append`)
2025-06-12 18:09:08 +00:00
Kim Morrison
2cddf2394b feat: grind annotations for List/Array/Vector.mapIdx theorems (#8748)
This PR adds grind annotations for `Array/Vector.mapIdx` and `mapFinIdx`
theorems.

The annotations are added to theorems that correspond to those already
annotated in the List implementation, ensuring consistency across all
three container types (List, Array, Vector) for indexed mapping
operations.

Key theorems annotated include:
- Size and element access theorems (`size_mapIdx`, `getElem_mapIdx`,
`getElem?_mapIdx`)
- Construction theorems (`mapIdx_empty`, `mapIdx_push`, `mapIdx_append`)
- Membership and equality theorems (`mem_mapIdx`, `mapIdx_mapIdx`)
- Conversion theorems (`toList_mapIdx`, `mapIdx_toArray`, etc.)
- Reverse and composition operations
- Similar annotations for `mapFinIdx` variants
2025-06-12 18:06:01 +00:00
Kim Morrison
75fe50a33e feat: grind annotations for List/Array/Vector.finRange theorems (#8747)
This PR adds grind annotations for \`List/Array/Vector.finRange\`
theorems.
2025-06-12 17:49:58 +00:00
Sebastian Ullrich
c2876a1a6a chore: update stage0 2025-06-12 16:36:08 +02:00
Sebastian Ullrich
9f6846a343 chore: work around old compiler bug 2025-06-12 16:36:08 +02:00
Sebastian Ullrich
64e105c121 feat: meta phase restrictions 2025-06-12 16:36:08 +02:00
Kim Morrison
d10a85539a feat: grind annotations for List/Array/Vector.find?/findSome?/idxOf?/findIdx? (#8741)
This PR adds annotations for
`List/Array/Vector.find?/findSome?/idxOf?/findIdx?`.
2025-06-12 11:06:18 +00:00
Sebastian Ullrich
f0347ee719 chore: lean --stats gives number of imported bytes (#8725)
Thanks to `mmap`, startup time is not necessarily related to this
figure, but it can be used as a rough measure for that and how much data
the module depends on, i.e. the rebuild chance.

Also adds new cumulative benchmarks for this metric as well as the
number of imported constants and env ext entries.
2025-06-12 08:29:42 +00:00
Kim Morrison
faffe86334 chore: add failing grind tests from Mathlib (#8737) 2025-06-12 05:57:32 +00:00
Mac Malone
c168d06edf chore: partially revert "feat: lake: use lean --setup" (#8736)
This PR partially reverts #8024 which introduced a significant Lake
performance regression during builds. Once the cause is discovered and
fixed, a similar PR will be made to revert this.
2025-06-12 05:53:59 +00:00
Kim Morrison
abfc49d0f7 chore: cleanup of grind tests (#8735) 2025-06-12 04:42:25 +00:00
Kim Morrison
34e98c2efc feat: add Decidable (∃ i, P i) (#8734)
This PR adds the missing instance
```
instance decidableExistsFin (P : Fin n → Prop) [DecidablePred P] : Decidable (∃ i, P i)
```
2025-06-12 02:58:37 +00:00
Leonardo de Moura
e7549b5651 feat: diseq splitting and non-chronological backtracking for linarith (#8733)
This PR implements disequality splitting and non-chronological
backtracking for the `grind` linarith procedure.
```lean
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c d : α)
    : a ≤ b → a - c ≥ 0 + d → d ≤ 0 → d ≥ 0 → b = c → a ≠ b → False := by
  grind
```
2025-06-12 02:49:35 +00:00
Cameron Zwarich
9f65d0251a chore: remove comments about missing functionality now implemented elsewhere (#8732) 2025-06-12 00:38:42 +00:00
Cameron Zwarich
a7af9f7d5f chore: fix a typo in a doc comment (#8731) 2025-06-11 20:41:32 +00:00
Cameron Zwarich
39cbe04946 fix: use Arg in LCNF FVarSubst rather than Expr (#8729)
This PR changes LCNF's `FVarSubst` to use `Arg` rather than `Expr`. This
enforces the requirements on substitutions, which match the requirements
on `Arg`.
2025-06-11 18:08:30 +00:00
Lean stage0 autoupdater
77fd1ba6b9 chore: update stage0 2025-06-11 16:51:07 +00:00
jrr6
0002ea8a37 feat: pre-stage0 groundwork for named error messages (#8649)
This PR adds the pre-stage0-update infrastructure for named error
messages. It adds macro syntax for registering and throwing named errors
(without elaborators), mechanisms for displaying error names in the
Infoview and at the command line, and the ability to link to error
explanations in the manual (once they are added).
2025-06-11 14:52:08 +00:00
jrr6
7bd82b103a feat: pre-stage0 groundwork for error explanations (#8651)
This PR adds the pre-stage0-update infrastructure for error
explanations. It adds the environment-extension machinery for
registering and accessing explanations, and it provides a cursory parser
that validates that the high-level structure of error explanations
matches the prescribed format.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-06-11 14:51:44 +00:00
Sebastian Ullrich
2c9c58b1f7 fix: allow mixing modules and non-modules when root is not a module (#8724) 2025-06-11 14:39:49 +00:00
Sebastian Ullrich
54c12df950 refactor: environment extension state splitting (#8653)
Replaces the previous `export/saveEntriesFn` split with a stricly more
general function such that `exportEntriesFn` could be deprecated at a
later point. Also gives the new function access to the `Environment`
while we're at it. Also gives `getModuleEntries` access to more olean
levels in preparation for `meta import`.
2025-06-11 12:52:04 +00:00
Sebastian Ullrich
01a0524749 chore: move benchmarking script to this repo (#8718)
Corresponding to
d3f39f8343
2025-06-11 12:27:06 +00:00
Lean stage0 autoupdater
551e755d23 chore: update stage0 2025-06-11 11:06:17 +00:00
Kim Morrison
082ca94d3b feat: add grind annotations for List/Array/Vector.eraseP/erase/eraseIdx (#8719)
This PR adds grind annotations for
List/Array/Vector.eraseP/erase/eraseIdx. It also adds some missing
lemmas.
2025-06-11 09:44:47 +00:00
Rob23oba
ee5b652136 doc: add documentation for builtin attributes (#8173)
This PR adds documentation to builtin attributes like `@[refl]` or
`@[implemented_by]`.

Closes #8432

---------

Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
Co-authored-by: David Thrane Christiansen <david@lean-fro.org>
2025-06-11 09:04:37 +00:00
Marc Huisinga
91b5e19833 feat: server-side for module hierarchy (#8654)
This PR adds server-side support for a new module hierarchy component in
VS Code that can be used to navigate both the import tree of a module
and the imported-by tree of a module. Specifically, it implements new
requests `$/lean/prepareModuleHierarchy`,
`$/lean/moduleHierarchy/imports` and
`$/lean/moduleHierarchy/importedBy`. These requests are not supported by
standard LSP. Companion PR at
[leanprover/vscode-lean4#620](https://github.com/leanprover/vscode-lean4/pull/620).


![Imports](https://github.com/user-attachments/assets/5ef650e7-3b0e-4a33-9ecb-f442bff88006)
![Imported
by](https://github.com/user-attachments/assets/d98e7a2c-3c4f-4509-afdf-08134a97aa78)

### Breaking changes
This PR augments the .ilean format with the direct imports of a file in
order to implement the `$/lean/moduleHierarchy/importedBy` request and
bumps the .ilean format version.
2025-06-11 08:02:18 +00:00
Paul Reichert
cf8315ed96 fix: restrict the IteratorLoop instance on DropWhile, which was accidentally more general (#8703)
This PR corrects the `IteratorLoop` instance in `DropWhile`, which
previously triggered for arbitrary iterator types.
2025-06-11 07:35:46 +00:00
Eric Wieser
44e36dec6f feat: strengthen finIdxOf? lemmas (#8678)
This PR makes the LHS of `isSome_finIdxOf?` and `isNone_finIdxOf?` more
general.
2025-06-11 07:32:01 +00:00
Cameron Zwarich
a92890ec84 fix: use the fvar subst for erased code in LCNF simp (#8717)
This PR uses the fvar substitution mechanism to replace erased code.
This isn't entirely satisfactory, since LCNF's `.return` doesn't support
a general `Arg` (which has a `.erased` constructor), it only supports an
`FVarId`. This is in contrast to the IR `.ret`, which does support a
general `Arg`.
2025-06-11 05:46:39 +00:00
Kim Morrison
eccc472e8d chore: remove set_option grind.warning false (#8714)
This PR removes the now unnecessary `set_option grind.warning false`
statements, now that the warning is disabled by default.
2025-06-11 05:09:19 +00:00
Cameron Zwarich
d8c54fb93d fix: consider any type application of an erased term to be erased (#8716)
This PR makes any type application of an erased term to be erased. This
comes up a bit more than one would expect in the implementation of Lean
itself.
2025-06-11 04:58:21 +00:00
Leonardo de Moura
aab65f595d feat: infrastructure for disequality constraints in grind linarith (#8715)
This PR implements the basic infrastructure for processing disequalities
in the `grind linarith` module. We still have to implement backtracking.
2025-06-11 04:04:41 +00:00
Lean stage0 autoupdater
0a9c246497 chore: update stage0 2025-06-11 02:42:58 +00:00
Leonardo de Moura
2a63b392dd fix: ring module in grind (#8713)
This PR fixes a bug in the commutative ring module used in `grind`. It
was missing simplification opportunities.
2025-06-11 01:20:50 +00:00
Cameron Zwarich
0b2884bfa3 fix: erase code of an erased type in LCNF simp (#8712)
This PR optimizes let decls of an erased type to an erased value.
Specialization can create local functions that produce a Prop, and
there's no point in keeping them around.
2025-06-11 00:58:55 +00:00
Johan Commelin
c53ab2835c fix: pin version of softprops/action-gh-release (#8710)
This PR pins the precise hash of softprops/action-gh-release to

    softprops/action-gh-release@da05d55257

because the latest version is broken.
See https://github.com/softprops/action-gh-release/issues/628 for more
details.
2025-06-11 00:08:18 +00:00
Anne Baanen
54dd7aae8c chore: improvements to release checklist and scripts (#8586)
This PR improves the release checklist and scripts:

* Check that the release's commit hash is not all-numeric starting with
0 (this can break SemVer, which [required us to release
v4.21.0-rc2](https://github.com/leanprover/lean4/releases/tag/v4.21.0-rc2)).
* Check that projects being bumped to a release tag do not reference
`nightly-testing` anymore.
* Clarify how to create subsequent release candidates if an `-rc1`
already exists.
* Fix typos in the release checklist documentation.
2025-06-10 22:56:06 +00:00
euprunin
52e0742108 chore: fix spelling mistakes (#8711)
Co-authored-by: euprunin <euprunin@users.noreply.github.com>
2025-06-10 20:24:28 +00:00
Sebastian Ullrich
614e6122f7 chore: fix LEAN_PATH for building stage2+ Leanc.lean (#8705)
It would accidentally fall back to stage 1 otherwise
2025-06-10 17:11:23 +00:00
Cameron Zwarich
1a9de502f2 fix: handle constants with erased types in toMonoType (#8709)
This PR handles constants with erased types in `toMonoType`. It is much
harder to write a test case for this than you would think, because most
references to such types get replaced with `lcErased` earlier.
2025-06-10 16:27:33 +00:00
Leonardo de Moura
085c4ed3f9 fix: internalization issue in the interface between linarith and ring (#8708)
This PR fixes an internalization bug in the interface between linarith
and ring modules in `grind`. The `CommRing` module may create new terms
during normalization.
2025-06-10 16:06:47 +00:00
Rob23oba
be4ebb8ac3 feat: equivalence of tree maps (#8210)
This PR adds an equivalence relation to tree maps akin to the existing
one for hash maps. In order to get many congruence lemmas to eventually
use for defining functions on extensional tree maps, almost all of the
remaining tree map functions have also been given lemmas to relate them
to list functions, although these aren't currently used to prove lemmas
other than congruence lemmas.
2025-06-10 14:49:52 +00:00
Kim Morrison
2344e3f254 chore: minor fixes to grind_indexmap test case (#8706) 2025-06-10 11:35:48 +00:00
Anne Baanen
48f394b1d4 chore: begin development cycle for v4.22.0 (#8642)
This PR bumps the version number of the Lean project to 4.22.0, since
v4.21.0 is now in the release candidate stage.
2025-06-10 11:29:41 +00:00
Sebastian Ullrich
2629921c01 fix: import completion after meta import (#8704)
The details of `identWithPartialTrailingDot` prevent a robust approach
using quotations.
2025-06-10 09:06:58 +00:00
Marc Huisinga
e123b327a5 feat: enable auto-implicits in lake math template (#8656)
This PR enables auto-implicits in the Lake math template. This resolves
an issue where new users sometimes set up a new project for math
formalization and then quickly realize that none of the code samples in
our official books and docs that use auto-implicits work in their
projects. With the introduction of [inlay hints for
auto-implicits](https://github.com/leanprover/lean4/pull/6768), we
consider the auto-implicit UX to be sufficiently usable that they can be
enabled by default in the math template.
Notably, this change does not affect Mathlib itself, which will proceed
to disable auto-implicits.

This change was previously discussed with and agreed to by the Mathlib
maintainer team.
2025-06-10 08:08:21 +00:00
Kim Morrison
e904314742 feat: add SHA-suffixed PR release tags (#8702)
This PR enhances the PR release workflow to create both short format and
SHA-suffixed release tags. Creates both pr-release-{PR_NUMBER} and
pr-release-{PR_NUMBER}-{SHORT_SHA} tags, generates separate releases for
both formats, adds separate GitHub status checks, and updates
Batteries/Mathlib testing branches to use SHA-suffixed tags for exact
commit traceability.

This removes the need for downstream repositories to deal with the
toolchain changing without the toolchain name changing.
2025-06-10 07:09:08 +00:00
Mac Malone
0ebd320940 fix: lake: export LeanOption in Lean from Lake (#8701)
This PR exports `LeanOption` in the `Lean` namespace from the `Lake`
namespace. `LeanOption` was moved from `Lean` to `Lake` in #8447, which
can cause unnecessary breakage without this.
2025-06-10 04:09:40 +00:00
Kim Morrison
b1980ef871 chore: cleanup notes about grind in LRAT (#8623)
This PR cleans up some notes about `grind` failures in the LRAT checker,
now that some `grind` bugs have been fixed.
2025-06-10 03:47:28 +00:00
Kim Morrison
8fce30e7cb chore: change grind.warning default to false (#8698)
This PR turns off the default warning when using `grind`, in preparation
for v4.22. I'll removing all the `set_option grind.warning false` in our
codebase in a second PR, after an update-stage0.
2025-06-10 03:40:45 +00:00
Kim Morrison
308a383079 chore: fix grind annotation on DHashMap.contains_iff_mem (#8700)
The original annotations produced patterns that matched too often.
2025-06-10 03:26:54 +00:00
Leonardo de Moura
2d67524e42 feat: equality in grind linarith (#8697)
This PR implements support for inequalities in the `grind` linear
arithmetic procedure and simplifies its design. Some examples that can
already be solved:
```lean
open Lean.Grind
example [IntModule α] [Preorder α] [IntModule.IsOrdered α] (a b c d : α)
    : a + d < c → b = a + (2:Int)*d → b - d > c → False := by
  grind

example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (a b : α)
    : a = 0 → b = 1 → a + b ≤ 2 := by
  grind

example [CommRing α] [Preorder α] [Ring.IsOrdered α] (a b c d e : α) :
    2*a + b ≥ 1 → b ≥ 0 → c ≥ 0 → d ≥ 0 → e ≥ 0
    → a ≥ 3*c → c ≥ 6*e → d - e*5 ≥ 0
    → a + b + 3*c + d + 2*e < 0 → False := by
  grind
```
2025-06-09 23:39:24 +00:00
Leonardo de Moura
41c41e455a feat: One.one support in linarith (#8694)
This PR implements special support for `One.one` in linarith when the
structure is a ordered ring. It also fixes bugs during initialization.
2025-06-09 20:17:48 +00:00
Cameron Zwarich
f61a412801 fix: make unsafeBaseIO noinline (#8669)
This PR makes `unsafeBaseIO` `noinline`. The new compiler is better at
optimizing `Result`-like types, which can cause the final operation in
an `unsafeBaseIO` block to be dropped, since `unsafeBaseIO` is
discarding the state.
2025-06-09 14:48:37 +00:00
Leonardo de Moura
00f6b1e70a fix: denotation functions for interfacing CommRing and linarith (#8693)
This PR fixes the denotation functions used to interface the ring and
linarith modules in grind.
2025-06-09 14:43:13 +00:00
Sebastian Ullrich
8422d936cf chore: revert "fix LEAN_PATH for building stage2+ Leanc.lean" (#8692)
Reverts leanprover/lean4#8685 pending Windows fix
2025-06-09 08:50:34 +00:00
Leonardo de Moura
dd1d3e6a3a feat: model search procedure for grind linarith (#8690)
This PR implements the main framework of the model search procedure for
the linarith component in grind. It currently handles only inequalities.
It can already solve simple goals such as
```lean
example [IntModule α] [Preorder α] [IntModule.IsOrdered α] (a b c : α)
    : a < b → b < c → c < a → False := by
  grind

example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c : α)
    : a < b → b < c + d → a - d < c := by
  grind
```
2025-06-09 04:31:28 +00:00
Leonardo de Moura
e38b8a0a7a feat: proof terms generation for CommRing and linarith interface (#8689)
This PR implements proof term generation for the `CommRing` and
`linarith` interface. It also fixes the `CommRing` helper theorems.
2025-06-08 23:38:03 +00:00
Leonardo de Moura
3e0168df58 feat: proof term construction infrastructure for linarith in grind (#8687)
This PR implements the infrastructure for constructing proof terms in
the linarith procedure in `grind`. It also adds the `ToExpr` instances
for the reified objects.
2025-06-08 19:58:48 +00:00
Mac Malone
fcaae1dc58 feat: lake: use lean --setup (#8447)
This PR makes use of `lean --setup` in Lake builds of Lean modules and
adds Lake support for the new `.olean` artifacts produced by the module
system.

Lake now computes the entire transitive import graph of a module for
Lean, allowing it eagerly provide the artifacts managed by Lake to Lean
via the `modules` field of `lean --setup`.

`lake setup-file` no longer respects the imports passed to it and
instead just parses the file's header for imports. This is necessary
because import statements are now more complex than a simple module
name.
2025-06-08 17:42:45 +00:00
Sebastian Ullrich
8cc6a4a028 chore: fix LEAN_PATH for building stage2+ Leanc.lean (#8685)
It would accidentally fall back to stage 1 otherwise
2025-06-08 16:17:05 +00:00
Cameron Zwarich
4ec5dad05f fix: only mark single-alt cases discriminant as used if any param is used (#8683)
This PR adds an optimization to the LCNF simp pass where the
discriminant of a single-alt cases is only marked as used if any param
is used.
2025-06-08 06:20:38 +00:00
Leonardo de Moura
7e1d0cc125 feat: use CommRing to normalize linarith expressions (#8682)
This PR uses the `CommRing` module to normalize linarith inequalities.
2025-06-08 05:41:00 +00:00
Cameron Zwarich
2ae066fdc0 fix: only mark a cases discriminant used if it has non-default alt (#8681)
This PR adds an optimization to the LCNF simp pass where the
discriminant of a `cases` construct will only be mark used if it has a
non-default alternative.
2025-06-08 05:07:02 +00:00
Leonardo de Moura
c9c794ee8a feat: reification and denotation for linarith module in grind (#8680)
This PR adds the `reify?` and `denoteExpr` for the new linarith module
in `grind`.
2025-06-08 02:53:28 +00:00
Leonardo de Moura
106708ee78 feat: grind linarith module infrastructure (#8677)
This PR adds the basic infrastructure for the linarith module in
`grind`.
2025-06-08 00:19:52 +00:00
Cameron Zwarich
666fb5c571 fix: update maxHeartbeats in tests/lean/run/match_expr_perf.lean (#8676)
This PR updates `maxHeartbeats` in the match_expr_perf.lean test, since
with the new compiler this also includes the allocations made by the
compiler.
2025-06-07 23:27:16 +00:00
Cameron Zwarich
8d8fd0715f fix: increase precision of new compiler's noncomputable check (#8675)
This PR increases the precision of the new compiler's non computable
check, particularly around irrelevant uses of `noncomputable` defs in
applications.

There are no tests included because they don't pass with the old
compiler. They are on the new compiler's branch and they will be merged
when it is enabled.
2025-06-07 22:20:55 +00:00
Leonardo de Moura
4abc4430dc refactor: ENodeKey => ExprPtr (#8674) 2025-06-07 19:30:02 +00:00
Lean stage0 autoupdater
d46188de54 chore: update stage0 2025-06-07 14:27:00 +00:00
Sebastian Ullrich
de57b77feb chore: support meta in ParseImportsFast (#8672) 2025-06-07 13:08:20 +00:00
Lean stage0 autoupdater
f0eae3b879 chore: update stage0 2025-06-07 11:04:28 +00:00
Sebastian Ullrich
1abf6fe1f5 chore: do not interpret meta as noncomputable (#8668)
To be replaced by actual handling of `meta`
2025-06-07 09:45:04 +00:00
Mac Malone
f917951745 fix: lake: ensure Lake versions are SemVer compatible (#8613)
This PR changes the Lake version syntax (to `5.0.0-src+<commit>`) to
ensure it is a well-formed SemVer,
2025-06-07 07:17:06 +00:00
Mac Malone
8904e5c070 feat: lake: builtin facet memoize toggle (#7738)
This PR makes memoization of built-in facets toggleable through a
`memoize` option on the facet configuration. Built-in facets which are
essentially aliases (e.g., `default`, `o`) have had memoization
disabled.
2025-06-07 06:00:05 +00:00
Leonardo de Moura
ef9094d7f8 feat: CommRing interface for grind linarith (#8670)
This PR adds helper theorems that will be used to interface the
`CommRing` module with the linarith procedure in `grind`.
2025-06-07 00:35:14 +00:00
Lean stage0 autoupdater
d50292d31b chore: update stage0 2025-06-06 20:02:08 +00:00
Joachim Breitner
24cb133eb2 feat: explicit defeq attribute (#8419)
This PR introduces an explicit `defeq` attribute to mark theorems that
can be used by `dsimp`. The benefit of an explicit attribute over the
prior logic of looking at the proof body is that we can reliably omit
theorem bodies across module boundaries. It also helps with intra-file
parallelism.

If a theorem is syntactically defined by `:= rfl`, then the attribute is
assumed and need not given explicitly. This is a purely syntactic check
and can be fooled, e.g. if in the current namespace, `rfl` is not
actually “the” `rfl` of `Eq`. In that case, some other syntax has be
used, such as `:= (rfl)`. This is also the way to go if a theorem can be
proved by `defeq`, but one does not actually want `dsimp` to use this
fact.

The `defeq` attribute will look at the *type* of the declaration, not
the body, to check if it really holds definitionally. Because of
different reduction settings, this can sometimes go wrong. Then one
should also write `:= (rfl)`, if one does not want this to be a defeq
theorem. (If one does then this is currently not possible, but it’s
probably a bad idea anyways).

The `set_option debug.tactic.simp.checkDefEqAttr true`, `dsimp` will
warn if could not apply a lemma due to a missing `defeq` attribute.

With `set_option backward.dsimp.useDefEqAttr.get false` one can revert
to the old behavior of inferring rfl-ness based on the theorem body.

Both options will go away eventually (too bad we can’t mark them as
deprecated right away, see #7969)

Meta programs that generate theorems (e.g. equational theorems) can use
`inferDefEqAttr` to set the attribute based on the theorem body of the
just created declaration.

This builds on #8501 to update Init to `@[expose]` a fair amount of
definitions that, if not exposed, would prevent some existing `:= rfl`
theorems from being `defeq` theorems. In the interest of starting
backwards compatible, I exposed these function. Hopefully many can be
un-exposed later again.

A mathlib adaption branch exists that includes both the meta programming
fixes and changes to the theorems (e.g. changing `:= by rfl` to `:=
rfl`).

With the module system there is now no special handling for `defeq`
theorem bodies, because we don’t look at the body anymore. The previous
hack is removed. The `defeq`-ness of the theorem needs to be checked in
the context of the theorem’s *type*; the error message contains a hint
if the defeq check fails because of the exported context.
2025-06-06 18:40:06 +00:00
Henrik Böving
eddbe08118 refactor: AIG doesn't need to be modified for constants (#8663) 2025-06-06 15:32:38 +00:00
Paul Reichert
d16c4052c2 feat: introduce empty iterator (#8615)
This PR provides a special empty iterator type. Although its behavior
can be emulated with a list iterator (for example), having a special
type has the advantage of being easier to optimize for the compiler.
2025-06-06 14:26:52 +00:00
tonneaus
febad6a380 doc: typo in IO.lean (#8657) 2025-06-06 13:12:12 +00:00
Marc Huisinga
257cd15a00 fix: wrong signature help after map/filter/etc (#8655)
This PR fixes a bug in the signature help where it would be displayed
for higher-order-functions that are the last argument of another
function.
2025-06-06 13:07:01 +00:00
Paul Reichert
5963bc8b8a fix: remove IteratorLoop instances without associated LawfulIteratorLoop instances (#8629)
This PR replaces special, more optimized `IteratorLoop` instances, for
which no lawfulness proof has been made, with the verified default
implementation. The specialization of the loop/collect implementations
is low priority, but having lawfulness instances for all iterators is
important for verification.
2025-06-06 08:06:59 +00:00
Paul Reichert
ec9b00996f feat: equivalence of iterators (#8545)
This PR provides the means to reason about "equivalent" iterators.
Simply speaking, two iterators are equivalent if they behave the same as
long as consumers do not introspect their states.
2025-06-06 08:06:39 +00:00
Kim Morrison
50474fef78 chore: cleanup after renaming get_elem_tactic_trivial 2025-06-06 13:10:18 +10:00
Kim Morrison
a5567618ac chore: update stage0 2025-06-06 13:10:18 +10:00
Kim Morrison
a3caf60f6a feat: rename get_elem_tactic_trivial to get_elem_tactic_extensible 2025-06-06 13:10:17 +10:00
Leonardo de Moura
c3d31cf24b feat: helper theorems for equality detection and coefficent normalization (#8650)
This PR adds helper theorems for coefficient normalization and equality
detection. This theorems are for the linear arithmetic procedure in
`grind`.
2025-06-06 02:42:57 +00:00
Leonardo de Moura
f7ecf06234 feat: normalization and ordered IntModule helper theorems (#8645)
This PR adds many helper theorems for the future `IntModule` linear
arithmetic procedure in `grind`.
It also adds helper theorems for normalizing input atoms and support for
disequality in the new linear arithmetic procedure in `grind`.
2025-06-05 23:39:10 +00:00
Cameron Zwarich
b97d35d879 fix: improve precision of the new compiler's noncomputable check for proj (#8647)
This PR improves the precision of the new compiler's `noncomputable`
check for projections. There is no test included because while this was
reduced from Mathlib, the old compiler does not correctly handle the
reduced test case. It's not entirely clear to me if the check is passing
with the old compiler for correct reasons. A test will be added to the
new compiler's branch.
2025-06-05 22:44:02 +00:00
Kim Morrison
ebf5fbd294 feat: complete grind's ToInt framework (#8639)
This PR completes the `ToInt` family of typeclasses which `grind` will
use to embed types into the integers for `cutsat`. It contains instances
for the usual concrete data types (`Fin`, `UIntX`, `IntX`, `BitVec`),
and is extensible (e.g. for Mathlib's `PNat`).
2025-06-05 11:25:04 +00:00
Luisa Cicolini
74d8746356 feat: add BitVec.setWidth'_eq to bv_normalize (#8640)
This PR adds `BitVec.setWidth'_eq` to `bv_normalize` such that
`bv_decide` can reduce it and solve lemmas involving `setWidth'_eq`
2025-06-05 09:42:47 +00:00
Joachim Breitner
1d9dd33bec feat: #print sig (#8641)
This PR adds the `#print sig $ident` variant of the `#print` command,
which omits the body. This is useful for testing meta-code, in the
```
#guard_msgs (drop trace, all) in #print sig foo
```
idiom. The benefit over `#check` is that it shows the declaration kind,
reducibility attributes (and in the future more built-in attributes,
like `@[defeq]` in #8419). (One downside is that `#check` shows unused
function parameter names, e.g. in induction principles; this could
probably be refined.)
2025-06-05 09:02:19 +00:00
Siddharth
9b9dd8546a feat: simplify T-division into E-division when numerator is positive (#8205)
This PR adds a simp lemma that simplifies T-division where the numerator
is a `Nat` into an E-division:


```lean
@[simp] theorem ofNat_tdiv_eq_ediv {a : Nat} {b : Int} : (a : Int).tdiv b = a / b :=
   tdiv_eq_ediv_of_nonneg (by simp)
```

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2025-06-05 06:20:49 +00:00
Siddharth
de7d43865e feat: bitvector trichotomy lemmas (#8203)
This PR adds trichotomy lemmas for unsigned and signed comparisons,
stating that only one of three cases may happen: either `x < y`, `x =
y`, or `x > y` (for both signed and unsigned comparsions). We use
explicit arguments so that users can write `rcases slt_trichotomy x y
with hlt | heq | hgt`.
2025-06-05 05:28:44 +00:00
Leonardo de Moura
3ce7dd318d feat: sort equivalence classes in grind diagnostics (#8638)
This PR improves the diagnostic information produced by `grind`. It now
sorts the equivalence classes by generation and then `Expr. lt`.
2025-06-05 04:35:59 +00:00
Leonardo de Moura
b1709d1fc1 feat: background theorems for IntModule (#8637)
This PR adds background theorems for normalizing `IntModule` expressions
using reflection.
2025-06-05 02:32:53 +00:00
Cameron Zwarich
6ebf39d0fc chore: fix formatting (#8635) 2025-06-04 22:43:45 +00:00
Cameron Zwarich
a6e2df6250 fix: don't treat types with erased constructor types as having trivial structure (#8634)
This PR makes `hasTrivialStructure?` return false for types whose
constructors have types that are erased, e.g. if they construct a
`Prop`.
2025-06-04 22:33:44 +00:00
1240 changed files with 19148 additions and 4730 deletions

View File

@@ -82,7 +82,7 @@ jobs:
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-*
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-* tests/lean/run/importStructure.lean
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk
@@ -104,7 +104,7 @@ jobs:
# NOTE: must be in sync with `save` below
path: |
.ccache
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
${{ matrix.name == 'Linux Lake' && false && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.c
@@ -127,9 +127,12 @@ jobs:
[ -d build ] || mkdir build
cd build
# arguments passed to `cmake`
# this also enables githash embedding into stage 1 library
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
OPTIONS=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
if [[ -n '${{ matrix.release }}' ]]; then
# this also enables githash embedding into stage 1 library, which prohibits reusing
# `.olean`s across commits, so we don't do it in the fast non-release CI
OPTIONS+=(-DCHECK_OLEAN_VERSION=ON)
fi
if [[ -n '${{ matrix.cross_target }}' ]]; then
# used by `prepare-llvm`
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
@@ -193,7 +196,7 @@ jobs:
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.name == 'Linux release')
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
- name: Test Summary
uses: test-summary/action@v2
with:
@@ -210,7 +213,7 @@ jobs:
- name: Check Stage 3
run: |
make -C build -j$NPROC check-stage3
if: matrix.test-speedcenter
if: matrix.check-stage3
- name: Test Speedcenter Benchmarks
run: |
# Necessary for some timing metrics but does not work on Namespace runners
@@ -224,7 +227,7 @@ jobs:
run: |
# clean rebuild in case of Makefile changes
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
if: matrix.name == 'Linux' && inputs.check-level >= 1
if: matrix.check-rebootstrap
- name: CCache stats
if: always()
run: ccache -s
@@ -242,7 +245,7 @@ jobs:
# NOTE: must be in sync with `restore` above
path: |
.ccache
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
${{ matrix.name == 'Linux Lake' && false && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.c

View File

@@ -164,9 +164,15 @@ jobs:
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"os": large && level < 2 ? "nscloud-ubuntu-22.04-amd64-4x16" : "ubuntu-latest",
"release": true,
"check-level": 0,
// Special handling for release jobs. We want:
// 1. To run it in PRs so developrs get PR toolchains (so secondary is sufficient)
// 2. To skip it in merge queues as it takes longer than the
// Linux lake build and adds little value in the merge queue
// 3. To run it in release (obviously)
"check-level": isPr ? 0 : 2,
"secondary": isPr,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
@@ -176,21 +182,14 @@ jobs:
},
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16" : "ubuntu-latest",
"check-level": 0,
// just a secondary build job for now until false positives can be excluded
"secondary": true,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
// TODO: importStructure is not compatible with .olean caching
// TODO: why does scopedMacros fail?
"CTEST_OPTIONS": "-E 'scopedMacros|importStructure'"
},
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"test": true,
"check-rebootstrap": level >= 1,
"check-stage3": level >= 2,
"test-speedcenter": level >= 2,
"check-level": 1,
// NOTE: `test-speedcenter` currently seems to be broken on `ubuntu-latest`
"test-speedcenter": large && level >= 2,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
},
{
"name": "Linux Reldebug",
@@ -223,7 +222,8 @@ jobs:
},
{
"name": "macOS aarch64",
"os": "macos-14",
// standard GH runner only comes with 7GB so use large runner if possible
"os": large ? "nscloud-macos-sonoma-arm64-6x14" : "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"shell": "bash -euxo pipefail {0}",
@@ -231,11 +231,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
// Special handling for MacOS aarch64, we want:
// 1. To run it in PRs so Mac devs get PR toolchains (so secondary is sufficient)
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
// little value in the merge queue
// 3. To run it in release (obviously)
// See above for release job levels
"check-level": isPr ? 0 : 2,
"secondary": isPr,
},
@@ -254,7 +250,7 @@ jobs:
},
{
"name": "Linux aarch64",
"os": "nscloud-ubuntu-22.04-arm64-4x8",
"os": "nscloud-ubuntu-22.04-arm64-4x16",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"check-level": 2,
@@ -364,7 +360,7 @@ jobs:
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -408,7 +404,7 @@ jobs:
echo -e "\n*Full commit log*\n" >> diff.md
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
- name: Release Nightly
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
body_path: diff.md
prerelease: true

View File

@@ -48,19 +48,30 @@ jobs:
git -C lean4.git remote add origin https://github.com/${{ github.repository_owner }}/lean4.git
git -C lean4.git fetch -n origin master
git -C lean4.git fetch -n origin "${{ steps.workflow-info.outputs.sourceHeadSha }}"
# Create both the original tag and the SHA-suffixed tag
SHORT_SHA="${{ steps.workflow-info.outputs.sourceHeadSha }}"
SHORT_SHA="${SHORT_SHA:0:7}"
# Export the short SHA for use in subsequent steps
echo "SHORT_SHA=${SHORT_SHA}" >> "$GITHUB_ENV"
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}" "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}"
- name: Delete existing release if present
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
# Try to delete any existing release for the current PR.
# Try to delete any existing release for the current PR (just the version without the SHA suffix).
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -73,7 +84,22 @@ jobs:
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Report release status
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})
# There are coredumps files here as well, but all in deeper subdirectories.
files: artifacts/*/*
fail_on_unmatched_files: true
draft: false
tag_name: pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}
repository: ${{ github.repository_owner }}/lean4-pr-releases
env:
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Report release status (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
with:
@@ -87,6 +113,20 @@ jobs:
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}",
});
- name: Report release status (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: "${{ steps.workflow-info.outputs.sourceHeadSha }}",
state: "success",
context: "PR toolchain (SHA-suffixed)",
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}",
});
- name: Add label
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
@@ -282,16 +322,18 @@ jobs:
if [ "$EXISTS" = "0" ]; then
echo "Branch does not exist, creating it."
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, pushing an empty commit."
echo "Branch already exists, updating lean-toolchain."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The Batteries `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
@@ -346,21 +388,23 @@ jobs:
if [ "$EXISTS" = "0" ]; then
echo "Branch does not exist, creating it."
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
sed -i 's,require "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}",' lakefile.lean
lake update batteries
git add lakefile.lean lake-manifest.json
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, merging $BASE and bumping Batteries."
echo "Branch already exists, updating lean-toolchain and bumping Batteries."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The Mathlib `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag may have moved since this branch was created, so merge their changes.
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
lake update batteries
git add lake-manifest.json
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes

View File

@@ -50,7 +50,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Re-running `script/release_checklist.py` will then create the tag `v4.6.0` from `master`/`main` and push it (unless `toolchain-tag: false` in the `release_repos.yml` file)
- `script/release_checklist.py` will then merge the tag `v4.6.0` into the `stable` branch and push it (unless `stable-branch: false` in the `release_repos.yml` file).
- Special notes on repositories with exceptional requirements:
- `doc-gen4` has addition dependencies which we do not update at each toolchain release, although occasionally these break and need to be updated manually.
- `doc-gen4` has additional dependencies which we do not update at each toolchain release, although occasionally these break and need to be updated manually.
- `verso`:
- The `subverso` dependency is unusual in that it needs to be compatible with _every_ Lean release simultaneously.
Usually you don't need to do anything.
@@ -94,6 +94,8 @@ We'll use `v4.6.0` as the intended release version as a running example.
This checklist walks you through creating the first release candidate for a version of Lean.
For subsequent release candidates, the process is essentially the same, but we start out with the `releases/v4.7.0` branch already created.
We'll use `v4.7.0-rc1` as the intended release version in this example.
- Decide which nightly release you want to turn into a release candidate.
@@ -112,7 +114,7 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git fetch nightly tag nightly-2024-02-29
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
git push --set-upstream origin releases/v4.18.0
git push --set-upstream origin releases/v4.7.0
```
- In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.

9
script/bench.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
# We benchmark against stage 2 to test new optimizations.
timeout -s KILL 1h time bash -c 'mkdir -p build/release; cd build/release; cmake ../.. && make -j$(nproc) stage2' 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2

View File

@@ -53,6 +53,23 @@ def tag_exists(repo_url, tag_name, github_token):
matching_tags = response.json()
return any(tag["ref"] == f"refs/tags/{tag_name}" for tag in matching_tags)
def commit_hash_for_tag(repo_url, tag_name, github_token):
# Use /git/matching-refs/tags/ to get all matching tags
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/git/matching-refs/tags/{tag_name}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
response = requests.get(api_url, headers=headers)
if response.status_code != 200:
return False
# Check if any of the returned refs exactly match our tag
matching_tags = response.json()
matching_commits = [tag["object"]["sha"] for tag in matching_tags if tag["ref"] == f"refs/tags/{tag_name}"]
if len(matching_commits) != 1:
return None
else:
return matching_commits[0]
def release_page_exists(repo_url, tag_name, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
@@ -286,6 +303,14 @@ def main():
lean4_success = False
else:
print(f" ✅ Tag {toolchain} exists")
commit_hash = commit_hash_for_tag(lean_repo_url, toolchain, github_token)
SHORT_HASH_LENGTH = 7 # Lake abbreviates the Lean commit to 7 characters.
if commit_hash is None:
print(f" ❌ Could not resolve tag {toolchain} to a commit.")
lean4_success = False
elif commit_hash[0] == '0' and commit_hash[:SHORT_HASH_LENGTH].isnumeric():
print(f" ❌ Short commit hash {commit_hash[:SHORT_HASH_LENGTH]} is numeric and starts with 0, causing issues for version parsing. Try regenerating the last commit to get a new hash.")
lean4_success = False
if not release_page_exists(lean_repo_url, toolchain, github_token):
print(f" ❌ Release page for {toolchain} does not exist")

View File

@@ -94,6 +94,7 @@ def generate_script(repo, version, config):
"echo 'This repo has nightly-testing infrastructure'",
f"git merge origin/bump/{version.split('-rc')[0]}",
"echo 'Please resolve any conflicts.'",
"grep nightly-testing lakefile.* && echo 'Please ensure the lakefile does not include nightly-testing versions.'",
""
])
if re.search(r'rc\d+$', version) and repo_name in ["verso", "reference-manual"]:

View File

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

View File

@@ -37,6 +37,7 @@ import Init.Ext
import Init.Omega
import Init.MacroTrace
import Init.Grind
import Init.GrindInstances
import Init.While
import Init.Syntax
import Init.Internal

View File

@@ -7,6 +7,7 @@ module
prelude
import Init.Prelude
meta import Init.Prelude
set_option linter.missingDocs true -- keep it documented
/-!

View File

@@ -148,7 +148,7 @@ attribute [simp] pure_bind bind_assoc bind_pure_comp
attribute [grind] pure_bind
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x
change x >>= (fun a => pure (id a)) = x
rw [bind_pure_comp, id_map]
/--

View File

@@ -58,7 +58,7 @@ protected theorem bind_pure_comp [Monad m] (f : α → β) (x : ExceptT ε m α)
intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
show (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
change (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
@@ -70,7 +70,7 @@ protected theorem seqLeft_eq {α β ε : Type u} {m : Type u → Type v} [Monad
cases b <;> simp [comp, Except.map, const]
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
show (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
change (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
@@ -206,15 +206,15 @@ theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f :
(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
show (f >>= fun g => g <$> x).run s = _
change (f >>= fun g => g <$> x).run s = _
simp
@[simp] theorem run_seqRight [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _
change (x >>= fun _ => y).run s = _
simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _
change (x >>= fun a => y >>= fun _ => pure a).run s = _
simp
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by

View File

@@ -9,7 +9,7 @@ module
prelude
import Init.Tactics
import Init.Meta
meta import Init.Meta
namespace Lean.Parser.Tactic.Conv

View File

@@ -8,7 +8,7 @@ notation, basic datatypes and type classes
module
prelude
import Init.Prelude
meta import Init.Prelude
import Init.SizeOf
set_option linter.missingDocs true -- keep it documented
@@ -2252,7 +2252,7 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
Quot.liftOn f
(fun (f : (x : α), β x) => f x)
(fun _ _ h => h x)
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
change extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
exact congrArg extfunApp (Quot.sound h)
/--

View File

@@ -246,7 +246,7 @@ def swap (xs : Array α) (i j : @& Nat) (hi : i < xs.size := by get_elem_tactic)
xs'.set j v₁ (Nat.lt_of_lt_of_eq hj (size_set _).symm)
@[simp] theorem size_swap {xs : Array α} {i j : Nat} {hi hj} : (xs.swap i j hi hj).size = xs.size := by
show ((xs.set i xs[j]).set j xs[i]
change ((xs.set i xs[j]).set j xs[i]
(Nat.lt_of_lt_of_eq hj (size_set _).symm)).size = xs.size
rw [size_set, size_set]

View File

@@ -52,6 +52,7 @@ theorem countP_push {a : α} {xs : Array α} : countP p (xs.push a) = countP p x
rcases xs with xs
simp_all
@[grind =]
theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
simp
@@ -59,10 +60,12 @@ theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + coun
rcases xs with xs
simp [List.length_eq_countP_add_countP (p := p)]
@[grind _=_]
theorem countP_eq_size_filter {xs : Array α} : countP p xs = (filter p xs).size := by
rcases xs with xs
simp [List.countP_eq_length_filter]
@[grind =]
theorem countP_eq_size_filter' : countP p = size filter p := by
funext xs
apply countP_eq_size_filter
@@ -71,7 +74,7 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
simp only [countP_eq_size_filter]
apply size_filter_le
@[simp] theorem countP_append {xs ys : Array α} : countP p (xs ++ ys) = countP p xs + countP p ys := by
@[simp, grind =] theorem countP_append {xs ys : Array α} : countP p (xs ++ ys) = countP p xs + countP p ys := by
rcases xs with xs
rcases ys with ys
simp
@@ -102,6 +105,7 @@ theorem boole_getElem_le_countP {xs : Array α} {i : Nat} (h : i < xs.size) :
rcases xs with xs
simp [List.boole_getElem_le_countP]
@[grind =]
theorem countP_set {xs : Array α} {i : Nat} {a : α} (h : i < xs.size) :
(xs.set i a).countP p = xs.countP p - (if p xs[i] then 1 else 0) + (if p a then 1 else 0) := by
rcases xs with xs
@@ -146,7 +150,7 @@ theorem countP_flatMap {p : β → Bool} {xs : Array α} {f : α → Array β} :
rcases xs with xs
simp [List.countP_flatMap, Function.comp_def]
@[simp] theorem countP_reverse {xs : Array α} : countP p xs.reverse = countP p xs := by
@[simp, grind =] theorem countP_reverse {xs : Array α} : countP p xs.reverse = countP p xs := by
rcases xs with xs
simp [List.countP_reverse]
@@ -173,7 +177,7 @@ variable [BEq α]
cases xs
simp
@[simp] theorem count_empty {a : α} : count a #[] = 0 := rfl
@[simp, grind =] theorem count_empty {a : α} : count a #[] = 0 := rfl
theorem count_push {a b : α} {xs : Array α} :
count a (xs.push b) = count a xs + if b == a then 1 else 0 := by
@@ -186,21 +190,28 @@ theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
theorem count_le_size {a : α} {xs : Array α} : count a xs xs.size := countP_le_size
grind_pattern count_le_size => count a xs
@[grind =]
theorem count_eq_size_filter {a : α} {xs : Array α} : count a xs = (filter (· == a) xs).size := by
simp [count, countP_eq_size_filter]
theorem count_le_count_push {a b : α} {xs : Array α} : count a xs count a (xs.push b) := by
simp [count_push]
@[grind =]
theorem count_singleton {a b : α} : count a #[b] = if b == a then 1 else 0 := by
simp [count_eq_countP]
@[simp] theorem count_append {a : α} {xs ys : Array α} : count a (xs ++ ys) = count a xs + count a ys :=
@[simp, grind =] theorem count_append {a : α} {xs ys : Array α} : count a (xs ++ ys) = count a xs + count a ys :=
countP_append
@[simp] theorem count_flatten {a : α} {xss : Array (Array α)} :
@[simp, grind =] theorem count_flatten {a : α} {xss : Array (Array α)} :
count a xss.flatten = (xss.map (count a)).sum := by
cases xss using array₂_induction
simp [List.count_flatten, Function.comp_def]
@[simp] theorem count_reverse {a : α} {xs : Array α} : count a xs.reverse = count a xs := by
@[simp, grind =] theorem count_reverse {a : α} {xs : Array α} : count a xs.reverse = count a xs := by
rcases xs with xs
simp
@@ -209,6 +220,7 @@ theorem boole_getElem_le_count {xs : Array α} {i : Nat} {a : α} (h : i < xs.si
rw [count_eq_countP]
apply boole_getElem_le_countP (p := (· == a))
@[grind =]
theorem count_set {xs : Array α} {i : Nat} {a b : α} (h : i < xs.size) :
(xs.set i a).count b = xs.count b - (if xs[i] == b then 1 else 0) + (if a == b then 1 else 0) := by
simp [count_eq_countP, countP_set, h]

View File

@@ -24,6 +24,7 @@ open Nat
/-! ### eraseP -/
@[grind =]
theorem eraseP_empty : #[].eraseP p = #[] := by simp
theorem eraseP_of_forall_mem_not {xs : Array α} (h : a, a xs ¬p a) : xs.eraseP p = xs := by
@@ -64,6 +65,7 @@ theorem exists_or_eq_self_of_eraseP (p) (xs : Array α) :
let _, ys, zs, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [size_append, e₁]
@[grind =]
theorem size_eraseP {xs : Array α} : (xs.eraseP p).size = if xs.any p then xs.size - 1 else xs.size := by
split <;> rename_i h
· simp only [any_eq_true] at h
@@ -81,11 +83,12 @@ theorem le_size_eraseP {xs : Array α} : xs.size - 1 ≤ (xs.eraseP p).size := b
rcases xs with xs
simpa using List.le_length_eraseP
@[grind ]
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] 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
@@ -93,15 +96,18 @@ theorem mem_of_mem_eraseP {xs : Array α} : a ∈ xs.eraseP p → a ∈ xs := by
rcases xs with xs
simp
@[grind _=_]
theorem eraseP_map {f : β α} {xs : Array β} : (xs.map f).eraseP p = (xs.eraseP (p f)).map f := by
rcases xs with xs
simpa using List.eraseP_map
@[grind =]
theorem eraseP_filterMap {f : α Option β} {xs : Array α} :
(filterMap f xs).eraseP p = filterMap f (xs.eraseP (fun x => match f x with | some y => p y | none => false)) := by
rcases xs with xs
simpa using List.eraseP_filterMap
@[grind =]
theorem eraseP_filter {f : α Bool} {xs : Array α} :
(filter f xs).eraseP p = filter f (xs.eraseP (fun x => p x && f x)) := by
rcases xs with xs
@@ -119,6 +125,7 @@ theorem eraseP_append_right {xs : Array α} ys (h : ∀ b ∈ xs, ¬p b) :
rcases ys with ys
simpa using List.eraseP_append_right ys (by simpa using h)
@[grind =]
theorem eraseP_append {xs : Array α} {ys : Array α} :
(xs ++ ys).eraseP p = if xs.any p then xs.eraseP p ++ ys else xs ++ ys.eraseP p := by
rcases xs with xs
@@ -126,6 +133,7 @@ theorem eraseP_append {xs : Array α} {ys : Array α} :
simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray]
split <;> simp
@[grind =]
theorem eraseP_replicate {n : Nat} {a : α} {p : α Bool} :
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
simp only [ List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
@@ -165,6 +173,7 @@ theorem eraseP_eq_iff {p} {xs : Array α} :
· exact Or.inl h
· exact Or.inr a, l₁, by simpa using h₁, h₂, l, by simp
@[grind =]
theorem eraseP_comm {xs : Array α} (h : a xs, ¬ p a ¬ q a) :
(xs.eraseP p).eraseP q = (xs.eraseP q).eraseP p := by
rcases xs with xs
@@ -208,6 +217,7 @@ theorem exists_erase_eq [LawfulBEq α] {a : α} {xs : Array α} (h : a ∈ xs) :
(xs.erase a).size = xs.size - 1 := by
rw [erase_eq_eraseP]; exact size_eraseP_of_mem h (beq_self_eq_true a)
@[grind =]
theorem size_erase [LawfulBEq α] {a : α} {xs : Array α} :
(xs.erase a).size = if a xs then xs.size - 1 else xs.size := by
rw [erase_eq_eraseP, size_eraseP]
@@ -222,11 +232,12 @@ theorem le_size_erase [LawfulBEq α] {a : α} {xs : Array α} : xs.size - 1 ≤
rcases xs with xs
simpa using List.le_length_erase
@[grind ]
theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a xs.erase b) : a xs := by
rcases xs with xs
simpa using List.mem_of_mem_erase (by simpa using h)
@[simp] 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)
@@ -234,6 +245,7 @@ theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a ∈ xs.erase b) : a
rw [erase_eq_eraseP', eraseP_eq_self_iff]
simp [forall_mem_ne']
@[grind _=_]
theorem erase_filter [LawfulBEq α] {f : α Bool} {xs : Array α} :
(filter f xs).erase a = filter f (xs.erase a) := by
rcases xs with xs
@@ -251,6 +263,7 @@ theorem erase_append_right [LawfulBEq α] {a : α} {xs : Array α} (ys : Array
rcases ys with ys
simpa using List.erase_append_right ys (by simpa using h)
@[grind =]
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
@@ -258,6 +271,7 @@ theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
split <;> simp
@[grind =]
theorem erase_replicate [LawfulBEq α] {n : Nat} {a b : α} :
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
simp only [ List.toArray_replicate, List.erase_toArray]
@@ -269,6 +283,7 @@ abbrev erase_mkArray := @erase_replicate
-- The arguments `a b` are explicit,
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
@[grind =]
theorem erase_comm [LawfulBEq α] (a b : α) {xs : Array α} :
(xs.erase a).erase b = (xs.erase b).erase a := by
rcases xs with xs
@@ -312,6 +327,7 @@ theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.siz
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
simp [eraseIdxIfInBounds, h]
@[grind =]
theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
xs.eraseIdx i h = xs.take i ++ xs.drop (i + 1) := by
rcases xs with xs
@@ -322,6 +338,7 @@ theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
rw [List.take_of_length_le]
simp
@[grind =]
theorem getElem?_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} :
(xs.eraseIdx i)[j]? = if j < i then xs[j]? else xs[j + 1]? := by
rcases xs with xs
@@ -339,6 +356,7 @@ theorem getElem?_eraseIdx_of_ge {xs : Array α} {i : Nat} (h : i < xs.size) {j :
intro h'
omega
@[grind =]
theorem getElem_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i)[j] = if h'' : j < i then
xs[j]
@@ -362,6 +380,7 @@ theorem eraseIdx_ne_empty_iff {xs : Array α} {i : Nat} {h} : xs.eraseIdx i ≠
simp [h]
· simp
@[grind ]
theorem mem_of_mem_eraseIdx {xs : Array α} {i : Nat} {h} {a : α} (h : a xs.eraseIdx i) : a xs := by
rcases xs with xs
simpa using List.mem_of_mem_eraseIdx (by simpa using h)
@@ -373,13 +392,29 @@ theorem eraseIdx_append_of_lt_size {xs : Array α} {k : Nat} (hk : k < xs.size)
simp at hk
simp [List.eraseIdx_append_of_lt_length, *]
theorem eraseIdx_append_of_length_le {xs : Array α} {k : Nat} (hk : xs.size k) (ys : Array α) (h) :
theorem eraseIdx_append_of_size_le {xs : Array α} {k : Nat} (hk : xs.size k) (ys : Array α) (h) :
eraseIdx (xs ++ ys) k = xs ++ eraseIdx ys (k - xs.size) (by simp at h; omega) := by
rcases xs with l
rcases ys with l'
simp at hk
simp [List.eraseIdx_append_of_length_le, *]
@[deprecated eraseIdx_append_of_size_le (since := "2025-06-11")]
abbrev eraseIdx_append_of_length_le := @eraseIdx_append_of_size_le
@[grind =]
theorem eraseIdx_append {xs ys : Array α} (h : k < (xs ++ ys).size) :
eraseIdx (xs ++ ys) k =
if h' : k < xs.size then
eraseIdx xs k ++ ys
else
xs ++ eraseIdx ys (k - xs.size) (by simp at h; omega) := by
split <;> rename_i h
· simp [eraseIdx_append_of_lt_size h]
· rw [eraseIdx_append_of_size_le]
omega
@[grind =]
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by
simp at h
@@ -428,6 +463,48 @@ theorem eraseIdx_set_gt {xs : Array α} {i : Nat} {j : Nat} {a : α} (h : i < j)
rcases xs with xs
simp [List.eraseIdx_set_gt, *]
@[grind =]
theorem eraseIdx_set {xs : Array α} {i : Nat} {a : α} {hi : i < xs.size} {j : Nat} {hj : j < (xs.set i a).size} :
(xs.set i a).eraseIdx j =
if h' : j < i then
(xs.eraseIdx j).set (i - 1) a (by simp; omega)
else if h'' : j = i then
xs.eraseIdx i
else
(xs.eraseIdx j (by simp at hj; omega)).set i a (by simp at hj ; omega) := by
split <;> rename_i h'
· rw [eraseIdx_set_lt]
omega
· split <;> rename_i h''
· subst h''
rw [eraseIdx_set_eq]
· rw [eraseIdx_set_gt]
omega
theorem set_eraseIdx_le {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (h : i j) (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a = (xs.set (j + 1) a (by simp at hj; omega)).eraseIdx i (by simp at ; omega) := by
rw [eraseIdx_set_lt]
· simp
· omega
theorem set_eraseIdx_gt {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (h : j < i) (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a = (xs.set j a).eraseIdx i (by simp at ; omega) := by
rw [eraseIdx_set_gt]
omega
@[grind =]
theorem set_eraseIdx {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a =
if h' : i j then
(xs.set (j + 1) a (by simp at hj; omega)).eraseIdx i (by simp at ; omega)
else
(xs.set j a).eraseIdx i (by simp at ; omega) := by
split <;> rename_i h'
· rw [set_eraseIdx_le]
omega
· rw [set_eraseIdx_gt]
omega
@[simp] theorem set_getElem_succ_eraseIdx_succ
{xs : Array α} {i : Nat} (h : i + 1 < xs.size) :
(xs.eraseIdx (i + 1)).set i xs[i + 1] (by simp; omega) = xs.eraseIdx i := by

View File

@@ -46,7 +46,7 @@ theorem size_extract_of_le {as : Array α} {i j : Nat} (h : j ≤ as.size) :
simp
omega
@[simp]
@[simp, grind =]
theorem extract_push {as : Array α} {b : α} {start stop : Nat} (h : stop as.size) :
(as.push b).extract start stop = as.extract start stop := by
ext i h₁ h₂
@@ -56,7 +56,7 @@ theorem extract_push {as : Array α} {b : α} {start stop : Nat} (h : stop ≤ a
simp only [getElem_extract, getElem_push]
rw [dif_pos (by omega)]
@[simp]
@[simp, grind =]
theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
as.extract 0 stop = as.pop := by
ext i h₁ h₂
@@ -65,7 +65,7 @@ theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
· simp only [size_extract, size_pop] at h₁ h₂
simp [getElem_extract, getElem_pop]
@[simp]
@[simp, grind _=_]
theorem extract_append_extract {as : Array α} {i j k : Nat} :
as.extract i j ++ as.extract j k = as.extract (min i j) (max j k) := by
ext l h₁ h₂
@@ -169,7 +169,7 @@ theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
simp [getElem?_extract]
omega
@[simp] theorem extract_extract {as : Array α} {i j k l : Nat} :
@[simp, grind =] theorem extract_extract {as : Array α} {i j k l : Nat} :
(as.extract i j).extract k l = as.extract (i + k) (min (i + l) j) := by
ext m h₁ h₂
· simp
@@ -185,6 +185,7 @@ theorem ne_empty_of_extract_ne_empty {as : Array α} {i j : Nat} (h : as.extract
as #[] :=
mt extract_eq_empty_of_eq_empty h
@[grind =]
theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
(as.set k a).extract i j =
if _ : k < i then
@@ -211,13 +212,14 @@ theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
simp [getElem_set]
omega
@[grind =]
theorem set_extract {as : Array α} {i j k : Nat} (h : k < (as.extract i j).size) {a : α} :
(as.extract i j).set k a = (as.set (i + k) a (by simp at h; omega)).extract i j := by
ext l h₁ h₂
· simp
· simp_all [getElem_set]
@[simp]
@[simp, grind =]
theorem extract_append {as bs : Array α} {i j : Nat} :
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
ext l h₁ h₂
@@ -242,14 +244,14 @@ theorem extract_append_right {as bs : Array α} :
(as ++ bs).extract as.size (as.size + i) = bs.extract 0 i := by
simp
@[simp] theorem map_extract {as : Array α} {i j : Nat} :
@[simp, grind =] theorem map_extract {as : Array α} {i j : Nat} :
(as.extract i j).map f = (as.map f).extract i j := by
ext l h₁ h₂
· simp
· simp only [size_map, size_extract] at h₁ h₂
simp only [getElem_map, getElem_extract]
@[simp] theorem extract_replicate {a : α} {n i j : Nat} :
@[simp, grind =] theorem extract_replicate {a : α} {n i j : Nat} :
(replicate n a).extract i j = replicate (min j n - i) a := by
ext l h₁ h₂
· simp
@@ -297,6 +299,7 @@ theorem set_eq_push_extract_append_extract {as : Array α} {i : Nat} (h : i < as
simp at h
simp [List.set_eq_take_append_cons_drop, h, List.take_of_length_le]
@[grind =]
theorem extract_reverse {as : Array α} {i j : Nat} :
as.reverse.extract i j = (as.extract (as.size - j) (as.size - i)).reverse := by
ext l h₁ h₂
@@ -307,6 +310,7 @@ theorem extract_reverse {as : Array α} {i j : Nat} :
congr 1
omega
@[grind =]
theorem reverse_extract {as : Array α} {i j : Nat} :
(as.extract i j).reverse = as.reverse.extract (as.size - j) (as.size - i) := by
rw [extract_reverse]

View File

@@ -23,10 +23,10 @@ Examples:
-/
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
@[simp] theorem size_finRange {n} : (Array.finRange n).size = n := by
@[simp, grind =] theorem size_finRange {n} : (Array.finRange n).size = n := by
simp [Array.finRange]
@[simp] theorem getElem_finRange {i : Nat} (h : i < (Array.finRange n).size) :
@[simp, grind =] theorem getElem_finRange {i : Nat} (h : i < (Array.finRange n).size) :
(Array.finRange n)[i] = Fin.cast size_finRange i, h := by
simp [Array.finRange]
@@ -49,6 +49,7 @@ theorem finRange_succ_last {n} :
· simp_all
omega
@[grind _=_]
theorem finRange_reverse {n} : (Array.finRange n).reverse = (Array.finRange n).map Fin.rev := by
ext i h
· simp

View File

@@ -38,11 +38,22 @@ theorem findSome?_singleton {a : α} {f : α → Option β} : #[a].findSome? f =
@[simp] theorem findSomeRev?_push_of_isNone {xs : Array α} (h : (f a).isNone) : (xs.push a).findSomeRev? f = xs.findSomeRev? f := by
cases xs; simp_all
@[grind =]
theorem findSomeRev?_push {xs : Array α} {a : α} {f : α Option β} :
(xs.push a).findSomeRev? f = (f a).or (xs.findSomeRev? f) := by
match h : f a with
| some b =>
rw [findSomeRev?_push_of_isSome]
all_goals simp_all
| none =>
rw [findSomeRev?_push_of_isNone]
all_goals simp_all
theorem exists_of_findSome?_eq_some {f : α Option β} {xs : Array α} (w : xs.findSome? f = some b) :
a, a xs f a = some b := by
cases xs; simp_all [List.exists_of_findSome?_eq_some]
@[simp] theorem findSome?_eq_none_iff : findSome? p xs = none x xs, p x = none := by
@[simp, grind =] theorem findSome?_eq_none_iff : findSome? p xs = none x xs, p x = none := by
cases xs; simp
@[simp] theorem findSome?_isSome_iff {f : α Option β} {xs : Array α} :
@@ -59,36 +70,39 @@ theorem findSome?_eq_some_iff {f : α → Option β} {xs : Array α} {b : β} :
· rintro xs, a, ys, h₀, h₁, h₂
exact xs.toList, a, ys.toList, by simpa using congrArg toList h₀, h₁, by simpa
@[simp] theorem findSome?_guard {xs : Array α} : findSome? (Option.guard fun x => p x) xs = find? p xs := by
@[simp, grind =] theorem findSome?_guard {xs : Array α} : findSome? (Option.guard p) xs = find? p xs := by
cases xs; simp
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard fun x => p x) xs :=
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard p) xs :=
findSome?_guard.symm
@[simp] theorem getElem?_zero_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f)[0]? = xs.findSome? f := by
@[simp, grind =] theorem getElem?_zero_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f)[0]? = xs.findSome? f := by
cases xs; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filterMap {f : α Option β} {xs : Array α} (h) :
@[simp, grind =] theorem getElem_zero_filterMap {f : α Option β} {xs : Array α} (h) :
(xs.filterMap f)[0] = (xs.findSome? f).get (by cases xs; simpa [List.length_filterMap_eq_countP] using h) := by
cases xs; simp [ List.head_eq_getElem, getElem?_zero_filterMap]
@[simp] theorem back?_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
@[simp, grind =] theorem back?_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
cases xs; simp
@[simp] theorem back!_filterMap [Inhabited β] {f : α Option β} {xs : Array α} :
@[simp, grind =] theorem back!_filterMap [Inhabited β] {f : α Option β} {xs : Array α} :
(xs.filterMap f).back! = (xs.findSomeRev? f).getD default := by
cases xs; simp
@[simp] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Array α} :
@[simp, grind _=_] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Array α} :
(xs.findSome? f).map g = xs.findSome? (Option.map g f) := by
cases xs; simp
@[grind _=_]
theorem findSome?_map {f : β γ} {xs : Array β} : findSome? p (xs.map f) = xs.findSome? (p f) := by
cases xs; simp [List.findSome?_map]
@[grind =]
theorem findSome?_append {xs ys : Array α} : (xs ++ ys).findSome? f = (xs.findSome? f).or (ys.findSome? f) := by
cases xs; cases ys; simp [List.findSome?_append]
@[grind =]
theorem getElem?_zero_flatten (xss : Array (Array α)) :
(flatten xss)[0]? = xss.findSome? fun xs => xs[0]? := by
cases xss using array₂_induction
@@ -104,12 +118,14 @@ theorem getElem_zero_flatten.proof {xss : Array (Array α)} (h : 0 < xss.flatten
obtain _, xs, m, rfl, h := h
exact xs, m, by simpa using h
@[grind =]
theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
(flatten xss)[0] = (xss.findSome? fun xs => xs[0]?).get (getElem_zero_flatten.proof h) := by
have t := getElem?_zero_flatten xss
simp [getElem?_eq_getElem, h] at t
simp [ t]
@[grind =]
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [ List.toArray_replicate, List.findSome?_replicate]
@@ -140,8 +156,9 @@ abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@[simp] theorem find?_empty : find? p #[] = none := rfl
@[simp, grind =] theorem find?_empty : find? p #[] = none := rfl
@[grind =]
theorem find?_singleton {a : α} {p : α Bool} :
#[a].find? p = if p a then some a else none := by
simp
@@ -150,11 +167,26 @@ theorem find?_singleton {a : α} {p : α → Bool} :
findRev? p (xs.push a) = some a := by
cases xs; simp [h]
@[simp] theorem findRev?_cons_of_neg {xs : Array α} (h : ¬p a) :
@[simp] theorem findRev?_push_of_neg {xs : Array α} (h : ¬p a) :
findRev? p (xs.push a) = findRev? p xs := by
cases xs; simp [h]
@[simp] theorem find?_eq_none : find? p xs = none x xs, ¬ p x := by
@[deprecated findRev?_push_of_neg (since := "2025-06-12")]
abbrev findRev?_cons_of_neg := @findRev?_push_of_neg
@[grind =]
theorem finRev?_push {xs : Array α} :
findRev? p (xs.push a) = (Option.guard p a).or (xs.findRev? p) := by
cases h : p a
· rw [findRev?_push_of_neg, Option.guard_eq_none_iff.mpr h]
all_goals simp [h]
· rw [findRev?_push_of_pos, Option.guard_eq_some_iff.mpr rfl, h]
all_goals simp [h]
@[deprecated finRev?_push (since := "2025-06-12")]
abbrev findRev?_cons := @finRev?_push
@[simp, grind =] theorem find?_eq_none : find? p xs = none x xs, ¬ p x := by
cases xs; simp
theorem find?_eq_some_iff_append {xs : Array α} :
@@ -178,60 +210,63 @@ theorem find?_push_eq_some {xs : Array α} :
(xs.push a).find? p = some b xs.find? p = some b (xs.find? p = none (p a a = b)) := by
cases xs; simp
@[simp] theorem find?_isSome {xs : Array α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
@[simp, grind =] theorem find?_isSome {xs : Array α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
cases xs; simp
@[grind ]
theorem find?_some {xs : Array α} (h : find? p xs = some a) : p a := by
cases xs
simp at h
exact List.find?_some h
@[grind ]
theorem mem_of_find?_eq_some {xs : Array α} (h : find? p xs = some a) : a xs := by
cases xs
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]
@[simp] theorem find?_filter {xs : Array α} (p q : α Bool) :
@[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
@[simp] theorem getElem?_zero_filter {p : α Bool} {xs : Array α} :
@[simp, grind =] theorem getElem?_zero_filter {p : α Bool} {xs : Array α} :
(xs.filter p)[0]? = xs.find? p := by
cases xs; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filter {p : α Bool} {xs : Array α} (h) :
@[simp, grind =] theorem getElem_zero_filter {p : α Bool} {xs : Array α} (h) :
(xs.filter p)[0] =
(xs.find? p).get (by cases xs; simpa [ List.countP_eq_length_filter] using h) := by
cases xs
simp [List.getElem_zero_eq_head]
@[simp] theorem back?_filter {p : α Bool} {xs : Array α} : (xs.filter p).back? = xs.findRev? p := by
@[simp, grind =] theorem back?_filter {p : α Bool} {xs : Array α} : (xs.filter p).back? = xs.findRev? p := by
cases xs; simp
@[simp] theorem back!_filter [Inhabited α] {p : α Bool} {xs : Array α} :
@[simp, grind =] theorem back!_filter [Inhabited α] {p : α Bool} {xs : Array α} :
(xs.filter p).back! = (xs.findRev? p).get! := by
cases xs; simp [Option.get!_eq_getD]
@[simp] theorem find?_filterMap {xs : Array α} {f : α Option β} {p : β Bool} :
@[simp, grind =] theorem find?_filterMap {xs : Array α} {f : α Option β} {p : β Bool} :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
cases xs; simp
@[simp] theorem find?_map {f : β α} {xs : Array β} :
@[simp, grind =] theorem find?_map {f : β α} {xs : Array β} :
find? p (xs.map f) = (xs.find? (p f)).map f := by
cases xs; simp
@[simp] theorem find?_append {xs ys : Array α} :
@[simp, grind =] theorem find?_append {xs ys : Array α} :
(xs ++ ys).find? p = (xs.find? p).or (ys.find? p) := by
cases xs
cases ys
simp
@[simp] theorem find?_flatten {xss : Array (Array α)} {p : α Bool} :
xss.flatten.find? p = xss.findSome? (·.find? p) := by
@[simp, grind _=_] theorem find?_flatten {xss : Array (Array α)} {p : α Bool} :
xss.flatten.find? p = xss.findSome? (find? p) := by
cases xss using array₂_induction
simp [List.findSome?_map, Function.comp_def]
@@ -270,7 +305,7 @@ theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
@[simp] theorem find?_flatMap {xs : Array α} {f : α Array β} {p : β Bool} :
@[simp, grind =] theorem find?_flatMap {xs : Array α} {f : α Array β} {p : β Bool} :
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
cases xs
simp [List.find?_flatMap, Array.flatMap_toArray]
@@ -282,6 +317,7 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
@[grind =]
theorem find?_replicate :
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
simp [ List.toArray_replicate, List.find?_replicate]
@@ -334,6 +370,7 @@ abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
@[deprecated get_find?_replicate (since := "2025-03-18")]
abbrev get_find?_mkArray := @get_find?_replicate
@[grind =]
theorem find?_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
(H : (a : α), a xs P a) {p : β Bool} :
(xs.pmap f H).find? p = (xs.attach.find? (fun a, m => p (f a (H a m)))).map fun a, m => f a (H a m) := by
@@ -347,12 +384,15 @@ theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
/-! ### findIdx -/
@[grind =]
theorem findIdx_empty : findIdx p #[] = 0 := rfl
@[grind =]
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)
@@ -361,6 +401,8 @@ theorem findIdx_getElem {xs : Array α} {w : xs.findIdx p < xs.size} :
p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
grind_pattern findIdx_getElem => xs[xs.findIdx p]
theorem findIdx_lt_size_of_exists {xs : Array α} (h : x xs, p x) :
xs.findIdx p < xs.size := by
rcases xs with xs
@@ -387,18 +429,24 @@ theorem findIdx_le_size {p : α → Bool} {xs : Array α} : xs.findIdx p ≤ xs.
· simp at e
exact Nat.le_of_eq (findIdx_eq_size.mpr e)
grind_pattern findIdx_le_size => xs.findIdx p, xs.size
@[simp]
theorem findIdx_lt_size {p : α Bool} {xs : Array α} :
xs.findIdx p < xs.size x xs, p x := by
rcases xs with xs
simp
grind_pattern findIdx_lt_size => xs.findIdx p, xs.size
/-- `p` does not hold for elements with indices less than `xs.findIdx p`. -/
theorem not_of_lt_findIdx {p : α Bool} {xs : Array α} {i : Nat} (h : i < xs.findIdx p) :
p (xs[i]'(Nat.le_trans h findIdx_le_size)) = false := by
rcases xs with xs
simpa using List.not_of_lt_findIdx (by simpa using h)
grind_pattern not_of_lt_findIdx => xs.findIdx p, xs[i]
/-- If `¬ p xs[j]` for all `j < i`, then `i ≤ xs.findIdx p`. -/
theorem le_findIdx_of_not {p : α Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
(h2 : j (hji : j < i), p (xs[j]'(Nat.lt_trans hji h)) = false) : i xs.findIdx p := by
@@ -426,6 +474,7 @@ theorem findIdx_eq {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
simp at h3
simp_all [not_of_lt_findIdx h3]
@[grind =]
theorem findIdx_append {p : α Bool} {xs ys : Array α} :
(xs ++ ys).findIdx p =
if xs.findIdx p < xs.size then xs.findIdx p else ys.findIdx p + xs.size := by
@@ -433,6 +482,7 @@ theorem findIdx_append {p : α → Bool} {xs ys : Array α} :
rcases ys with ys
simp [List.findIdx_append]
@[grind =]
theorem findIdx_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx p = if xs.findIdx p < xs.size then xs.findIdx p else xs.size + if p a then 0 else 1 := by
simp only [push_eq_append, findIdx_append]
@@ -455,7 +505,7 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
rcases xs with xs
exact List.false_of_mem_take_findIdx (by simpa using h)
@[simp] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α Bool} :
@[simp, grind =] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α Bool} :
(xs.extract 0 i).findIdx p = min i (xs.findIdx p) := by
cases xs
simp
@@ -467,24 +517,24 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
theorem findIdx?_singleton {a : α} {p : α Bool} :
@[simp, grind =] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
@[grind =] theorem findIdx?_singleton {a : α} {p : α Bool} :
#[a].findIdx? p = if p a then some 0 else none := by
simp
@[simp]
@[simp, grind =]
theorem findIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
xs.findIdx? p = none x, x xs p x = false := by
rcases xs with xs
simp
@[simp]
@[simp, grind =]
theorem findIdx?_isSome {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isSome = xs.any p := by
rcases xs with xs
simp [List.findIdx?_isSome]
@[simp]
@[simp, grind =]
theorem findIdx?_isNone {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
rcases xs with xs
@@ -526,18 +576,19 @@ theorem of_findIdx?_eq_none {xs : Array α} {p : α → Bool} (w : xs.findIdx? p
rcases xs with xs
simpa using List.of_findIdx?_eq_none (by simpa using w)
@[simp] theorem findIdx?_map {f : β α} {xs : Array β} {p : α Bool} :
@[simp, grind =] theorem findIdx?_map {f : β α} {xs : Array β} {p : α Bool} :
findIdx? p (xs.map f) = xs.findIdx? (p f) := by
rcases xs with xs
simp [List.findIdx?_map]
@[simp] theorem findIdx?_append :
@[simp, grind =] theorem findIdx?_append :
(xs ++ ys : Array α).findIdx? p =
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.size) := by
rcases xs with xs
rcases ys with ys
simp [List.findIdx?_append]
@[grind =]
theorem findIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx? p = (xs.findIdx? p).or (if p a then some xs.size else none) := by
simp only [push_eq_append, findIdx?_append]
@@ -553,7 +604,7 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
cases xss using array₂_induction
simp [List.findIdx?_flatten, Function.comp_def]
@[simp] theorem findIdx?_replicate :
@[simp, grind =] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by
rw [ List.toArray_replicate]
simp only [List.findIdx?_toArray]
@@ -578,6 +629,7 @@ theorem findIdx?_eq_none_of_findIdx?_eq_none {xs : Array α} {p q : α → Bool}
rcases xs with xs
simpa using List.findIdx?_eq_none_of_findIdx?_eq_none (by simpa using w)
@[grind =]
theorem findIdx_eq_getD_findIdx? {xs : Array α} {p : α Bool} :
xs.findIdx p = (xs.findIdx? p).getD xs.size := by
rcases xs with xs
@@ -594,15 +646,17 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
cases xs
simp [hf]
@[simp] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α Bool} :
@[simp, grind =] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α Bool} :
(xs.take i).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun j => j < i)) := by
cases xs
simp
/-! ### findFinIdx? -/
@[grind =]
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp
@[grind =]
theorem findFinIdx?_singleton {a : α} {p : α Bool} :
#[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp
@@ -620,7 +674,7 @@ theorem findFinIdx?_eq_pmap_findIdx? {xs : Array α} {p : α → Bool} :
(fun i h => h) := by
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
@[simp] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
@[simp, grind =] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
xs.findFinIdx? p = none x, x xs ¬ p x := by
simp [findFinIdx?_eq_pmap_findIdx?]
@@ -636,12 +690,14 @@ theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.si
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[grind =]
theorem findFinIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or (if p a then some xs.size, by simp else none) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_push, Option.pmap_or]
split <;> rename_i h _ <;> split <;> simp [h]
@[grind =]
theorem findFinIdx?_append {xs ys : Array α} {p : α Bool} :
(xs ++ ys).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or
@@ -651,13 +707,13 @@ theorem findFinIdx?_append {xs ys : Array α} {p : α → Bool} :
· simp [h, Option.pmap_map, Option.map_pmap, Nat.add_comm]
· simp [h]
@[simp]
@[simp, grind =]
theorem isSome_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isSome = xs.any p := by
rcases xs with xs
simp [Array.size]
@[simp]
@[simp, grind =]
theorem isNone_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
rcases xs with xs
@@ -678,6 +734,7 @@ The verification API for `idxOf` is still incomplete.
The lemmas below should be made consistent with those for `findIdx` (and proved using them).
-/
@[grind =]
theorem idxOf_append [BEq α] [LawfulBEq α] {xs ys : Array α} {a : α} :
(xs ++ ys).idxOf a = if a xs then xs.idxOf a else ys.idxOf a + xs.size := by
rw [idxOf, findIdx_append]
@@ -691,10 +748,23 @@ theorem idxOf_eq_size [BEq α] [LawfulBEq α] {xs : Array α} (h : a ∉ xs) : x
rcases xs with xs
simp [List.idxOf_eq_length (by simpa using h)]
theorem idxOf_lt_length [BEq α] [LawfulBEq α] {xs : Array α} (h : a xs) : xs.idxOf a < xs.size := by
theorem idxOf_lt_length_of_mem [BEq α] [LawfulBEq α] {xs : Array α} (h : a xs) : xs.idxOf a < xs.size := by
rcases xs with xs
simp [List.idxOf_lt_length (by simpa using h)]
simp [List.idxOf_lt_length_of_mem (by simpa using h)]
theorem idxOf_le_size [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf a xs.size := by
rcases xs with xs
simp [List.idxOf_le_length]
grind_pattern idxOf_le_size => xs.idxOf a, xs.size
theorem idxOf_lt_size_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf a < xs.size a xs := by
rcases xs with xs
simp [List.idxOf_lt_length_iff]
grind_pattern idxOf_lt_size_iff => xs.idxOf a, xs.size
/-! ### idxOf?
@@ -702,19 +772,20 @@ The verification API for `idxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
-/
theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
@[grind =] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
@[simp, grind =] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf? a = none a xs := by
rcases xs with xs
simp [List.idxOf?_eq_none_iff]
@[simp]
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isSome a xs := by
rcases xs with xs
simp
@[grind =]
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isNone = ¬ a xs := by
simp
@@ -729,9 +800,9 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[grind =] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
@[simp, grind =] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.finIdxOf? a = none a xs := by
rcases xs with xs
simp [List.finIdxOf?_eq_none_iff, Array.size]
@@ -742,14 +813,16 @@ theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by sim
unfold Array.size at i
simp [List.finIdxOf?_eq_some_iff]
@[simp]
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isSome a xs := by
@[simp, grind =]
theorem isSome_finIdxOf? [BEq α] [PartialEquivBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isSome = xs.contains a := by
rcases xs with xs
simp [Array.size]
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isNone = ¬ a xs := by
simp
@[simp, grind =]
theorem isNone_finIdxOf? [BEq α] [PartialEquivBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isNone = !xs.contains a := by
rcases xs with xs
simp [Array.size]
end Array

View File

@@ -47,11 +47,16 @@ theorem insertIdx_zero {xs : Array α} {x : α} : xs.insertIdx 0 x = #[x] ++ xs
simp at h
simp [List.length_insertIdx, h]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
theorem eraseIdx_insertIdx_self {i : Nat} {xs : Array α} (h : i xs.size) :
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
rcases xs with xs
simp_all
@[deprecated eraseIdx_insertIdx_self (since := "2025-06-15")]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
simp [eraseIdx_insertIdx_self]
theorem insertIdx_eraseIdx_of_ge {as : Array α}
(w₁ : i < as.size) (w₂ : j (as.eraseIdx i).size) (h : i j) :
(as.eraseIdx i).insertIdx j a =
@@ -66,6 +71,18 @@ theorem insertIdx_eraseIdx_of_le {as : Array α}
cases as
simpa using List.insertIdx_eraseIdx_of_le (by simpa) (by simpa)
@[grind =]
theorem insertIdx_eraseIdx {as : Array α} (h₁ : i < as.size) (h₂ : j (as.eraseIdx i).size) :
(as.eraseIdx i).insertIdx j a =
if h : i j then
(as.insertIdx (j + 1) a (by simp_all; omega)).eraseIdx i (by simp_all; omega)
else
(as.insertIdx j a).eraseIdx (i + 1) (by simp_all) := by
split <;> rename_i h'
· rw [insertIdx_eraseIdx_of_ge] <;> omega
· rw [insertIdx_eraseIdx_of_le] <;> omega
@[grind =]
theorem insertIdx_comm (a b : α) {i j : Nat} {xs : Array α} (_ : i j) (_ : j xs.size) :
(xs.insertIdx i a).insertIdx (j + 1) b (by simpa) =
(xs.insertIdx j b).insertIdx i a (by simp; omega) := by
@@ -81,6 +98,7 @@ theorem insertIdx_size_self {xs : Array α} {x : α} : xs.insertIdx xs.size x =
rcases xs with xs
simp
@[grind =]
theorem getElem_insertIdx {xs : Array α} {x : α} {i k : Nat} (w : i xs.size) (h : k < (xs.insertIdx i x).size) :
(xs.insertIdx i x)[k] =
if h₁ : k < i then
@@ -106,6 +124,7 @@ theorem getElem_insertIdx_of_gt {xs : Array α} {x : α} {i k : Nat} (w : k ≤
simp [getElem_insertIdx, w, h]
rw [dif_neg (by omega), dif_neg (by omega)]
@[grind =]
theorem getElem?_insertIdx {xs : Array α} {x : α} {i k : Nat} (h : i xs.size) :
(xs.insertIdx i x)[k]? =
if k < i then

View File

@@ -89,6 +89,8 @@ theorem size_pos_of_mem {a : α} {xs : Array α} (h : a ∈ xs) : 0 < xs.size :=
simp only [mem_toArray] at h
simpa using List.length_pos_of_mem h
grind_pattern size_pos_of_mem => a xs, xs.size
theorem exists_mem_of_size_pos {xs : Array α} (h : 0 < xs.size) : a, a xs := by
cases xs
simpa using List.exists_mem_of_length_pos h
@@ -1498,6 +1500,19 @@ theorem forall_mem_filter {p : α → Bool} {xs : Array α} {P : α → Prop} :
( (i) (_ : i xs.filter p), P i) (j) (_ : j xs), p j P j := by
simp
@[grind] theorem getElem_filter {xs : Array α} {p : α Bool} {i : Nat} (h : i < (xs.filter p).size) :
p (xs.filter p)[i] :=
(mem_filter.mp (getElem_mem h)).2
theorem getElem?_filter {xs : Array α} {p : α Bool} {i : Nat} (h : i < (xs.filter p).size)
(w : (xs.filter p)[i]? = some a) : p a := by
rw [getElem?_eq_getElem] at w
simp only [Option.some.injEq] at w
rw [ w]
apply getElem_filter h
grind_pattern getElem?_filter => (xs.filter p)[i]?, some a
@[simp] theorem filter_filter {p q : α Bool} {xs : Array α} :
filter p (filter q xs) = filter (fun a => p a && q a) xs := by
apply ext'
@@ -3621,8 +3636,8 @@ We can prove that two folds over the same array are related (by some arbitrary r
if we know that the initial elements are related and the folding function, for each element of the array,
preserves the relation.
-/
theorem foldl_rel {xs : Array α} {f g : β α β} {a b : β} {r : β β Prop}
(h : r a b) (h' : (a : α), a xs (c c' : β), r c c' r (f c a) (g c' a)) :
theorem foldl_rel {xs : Array α} {f : β α β} {g : γ α γ} {a : β} {b : γ} {r : β γ Prop}
(h : r a b) (h' : (a : α), a xs (c : β) (c' : γ), r c c' r (f c a) (g c' a)) :
r (xs.foldl (fun acc a => f acc a) a) (xs.foldl (fun acc a => g acc a) b) := by
rcases xs with xs
simpa using List.foldl_rel h (by simpa using h')
@@ -3632,8 +3647,8 @@ We can prove that two folds over the same array are related (by some arbitrary r
if we know that the initial elements are related and the folding function, for each element of the array,
preserves the relation.
-/
theorem foldr_rel {xs : Array α} {f g : α β β} {a b : β} {r : β β Prop}
(h : r a b) (h' : (a : α), a xs (c c' : β), r c c' r (f a c) (g a c')) :
theorem foldr_rel {xs : Array α} {f : α β β} {g : α γ γ} {a : β} {b : γ} {r : β γ Prop}
(h : r a b) (h' : (a : α), a xs (c : β) (c' : γ), r c c' r (f a c) (g a c')) :
r (xs.foldr (fun a acc => f a acc) a) (xs.foldr (fun a acc => g a acc) b) := by
rcases xs with xs
simpa using List.foldr_rel h (by simpa using h')
@@ -4526,7 +4541,7 @@ abbrev contains_def [DecidableEq α] {a : α} {xs : Array α} : xs.contains a
(zip xs ys).size = min xs.size ys.size :=
size_zipWith
@[simp] theorem getElem_zipWith {xs : Array α} {ys : Array β} {f : α β γ} {i : Nat}
@[simp, grind =] theorem getElem_zipWith {xs : Array α} {ys : Array β} {f : α β γ} {i : Nat}
(hi : i < (zipWith f xs ys).size) :
(zipWith f xs ys)[i] = f (xs[i]'(by simp at hi; omega)) (ys[i]'(by simp at hi; omega)) := by
cases xs

View File

@@ -51,27 +51,27 @@ theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) → α → (h : i < xs.siz
i h, p i ((Array.mapFinIdx xs f)[i]) h :=
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => hs .., trivial).2
@[simp] theorem size_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] theorem size_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
(xs.mapFinIdx f).size = xs.size :=
(mapFinIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
@[simp] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
@[simp, grind =] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
Array.size_mapFinIdx
@[deprecated size_zipIdx (since := "2025-01-21")] abbrev size_zipWithIndex := @size_zipIdx
@[simp] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
@[simp, grind =] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
(h : i < (xs.mapFinIdx f).size) :
(xs.mapFinIdx f)[i] = f i (xs[i]'(by simp_all)) (by simp_all) :=
(mapFinIdx_spec (p := fun i b h => b = f i xs[i] h) fun _ _ => rfl).2 i _
@[simp] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat} :
@[simp, grind =] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat} :
(xs.mapFinIdx f)[i]? =
xs[i]?.pbind fun b h => some <| f i b (getElem?_eq_some_iff.1 h).1 := by
simp only [getElem?_def, size_mapFinIdx, getElem_mapFinIdx]
split <;> simp_all
@[simp] theorem toList_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] theorem toList_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
(xs.mapFinIdx f).toList = xs.toList.mapFinIdx (fun i a h => f i a (by simpa)) := by
apply List.ext_getElem <;> simp
@@ -91,20 +91,20 @@ theorem mapIdx_spec {f : Nat → α → β} {xs : Array α}
i h, p i ((xs.mapIdx f)[i]) h :=
(mapIdx_induction (motive := fun _ => True) trivial fun _ _ _ => hs .., trivial).2
@[simp] theorem size_mapIdx {f : Nat α β} {xs : Array α} : (xs.mapIdx f).size = xs.size :=
@[simp, grind =] theorem size_mapIdx {f : Nat α β} {xs : Array α} : (xs.mapIdx f).size = xs.size :=
(mapIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
@[simp] theorem getElem_mapIdx {f : Nat α β} {xs : Array α} {i : Nat}
@[simp, grind =] theorem getElem_mapIdx {f : Nat α β} {xs : Array α} {i : Nat}
(h : i < (xs.mapIdx f).size) :
(xs.mapIdx f)[i] = f i (xs[i]'(by simp_all)) :=
(mapIdx_spec (p := fun i b h => b = f i xs[i]) fun _ _ => rfl).2 i (by simp_all)
@[simp] theorem getElem?_mapIdx {f : Nat α β} {xs : Array α} {i : Nat} :
@[simp, grind =] theorem getElem?_mapIdx {f : Nat α β} {xs : Array α} {i : Nat} :
(xs.mapIdx f)[i]? =
xs[i]?.map (f i) := by
simp [getElem?_def, size_mapIdx, getElem_mapIdx]
@[simp] theorem toList_mapIdx {f : Nat α β} {xs : Array α} :
@[simp, grind =] theorem toList_mapIdx {f : Nat α β} {xs : Array α} :
(xs.mapIdx f).toList = xs.toList.mapIdx (fun i a => f i a) := by
apply List.ext_getElem <;> simp
@@ -126,7 +126,7 @@ namespace Array
/-! ### zipIdx -/
@[simp] theorem getElem_zipIdx {xs : Array α} {k : Nat} {i : Nat} (h : i < (xs.zipIdx k).size) :
@[simp, grind =] theorem getElem_zipIdx {xs : Array α} {k : Nat} {i : Nat} (h : i < (xs.zipIdx k).size) :
(xs.zipIdx k)[i] = (xs[i]'(by simp_all), k + i) := by
simp [zipIdx]
@@ -140,7 +140,7 @@ abbrev getElem_zipWithIndex := @getElem_zipIdx
@[deprecated zipIdx_toArray (since := "2025-01-21")]
abbrev zipWithIndex_toArray := @zipIdx_toArray
@[simp] theorem toList_zipIdx {xs : Array α} {k : Nat} :
@[simp, grind =] theorem toList_zipIdx {xs : Array α} {k : Nat} :
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
rcases xs with xs
simp
@@ -185,7 +185,7 @@ abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
subst w
rfl
@[simp]
@[simp, grind =]
theorem mapFinIdx_empty {f : (i : Nat) α (h : i < 0) β} : mapFinIdx #[] f = #[] :=
rfl
@@ -195,6 +195,7 @@ theorem mapFinIdx_eq_ofFn {xs : Array α} {f : (i : Nat) → α → (h : i < xs.
simp only [List.mapFinIdx_toArray, List.mapFinIdx_eq_ofFn, Fin.getElem_fin, List.getElem_toArray]
simp [Array.size]
@[grind =]
theorem mapFinIdx_append {xs ys : Array α} {f : (i : Nat) α (h : i < (xs ++ ys).size) β} :
(xs ++ ys).mapFinIdx f =
xs.mapFinIdx (fun i a h => f i a (by simp; omega)) ++
@@ -203,7 +204,7 @@ theorem mapFinIdx_append {xs ys : Array α} {f : (i : Nat) → α → (h : i < (
cases ys
simp [List.mapFinIdx_append, Array.size]
@[simp]
@[simp, grind =]
theorem mapFinIdx_push {xs : Array α} {a : α} {f : (i : Nat) α (h : i < (xs.push a).size) β} :
mapFinIdx (xs.push a) f =
(mapFinIdx xs (fun i a h => f i a (by simp; omega))).push (f xs.size a (by simp)) := by
@@ -237,7 +238,7 @@ theorem exists_of_mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) → α
rcases xs with xs
exact List.exists_of_mem_mapFinIdx (by simpa using h)
@[simp] theorem mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] theorem mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
b xs.mapFinIdx f (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
rcases xs with xs
simp
@@ -290,7 +291,7 @@ theorem mapFinIdx_eq_mapFinIdx_iff {xs : Array α} {f g : (i : Nat) → α → (
rw [eq_comm, mapFinIdx_eq_iff]
simp
@[simp] theorem mapFinIdx_mapFinIdx {xs : Array α}
@[simp, grind =] theorem mapFinIdx_mapFinIdx {xs : Array α}
{f : (i : Nat) α (h : i < xs.size) β}
{g : (i : Nat) β (h : i < (xs.mapFinIdx f).size) γ} :
(xs.mapFinIdx f).mapFinIdx g = xs.mapFinIdx (fun i a h => g i (f i a h) (by simpa using h)) := by
@@ -305,14 +306,14 @@ theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) → α → (h
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapFinIdx_eq_mkArray_iff := @mapFinIdx_eq_replicate_iff
@[simp] theorem mapFinIdx_reverse {xs : Array α} {f : (i : Nat) α (h : i < xs.reverse.size) β} :
@[simp, grind =] theorem mapFinIdx_reverse {xs : Array α} {f : (i : Nat) α (h : i < xs.reverse.size) β} :
xs.reverse.mapFinIdx f = (xs.mapFinIdx (fun i a h => f (xs.size - 1 - i) a (by simp; omega))).reverse := by
rcases xs with l
simp [List.mapFinIdx_reverse, Array.size]
/-! ### mapIdx -/
@[simp]
@[simp, grind =]
theorem mapIdx_empty {f : Nat α β} : mapIdx f #[] = #[] :=
rfl
@@ -332,13 +333,14 @@ theorem mapIdx_eq_zipIdx_map {xs : Array α} {f : Nat → α → β} :
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
@[grind =]
theorem mapIdx_append {xs ys : Array α} :
(xs ++ ys).mapIdx f = xs.mapIdx f ++ ys.mapIdx (fun i => f (i + xs.size)) := by
rcases xs with xs
rcases ys with ys
simp [List.mapIdx_append]
@[simp]
@[simp, grind =]
theorem mapIdx_push {xs : Array α} {a : α} :
mapIdx f (xs.push a) = (mapIdx f xs).push (f xs.size a) := by
simp [ append_singleton, mapIdx_append]
@@ -360,7 +362,7 @@ theorem exists_of_mem_mapIdx {b : β} {xs : Array α}
rw [mapIdx_eq_mapFinIdx] at h
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
@[simp] theorem mem_mapIdx {b : β} {xs : Array α} :
@[simp, grind =] theorem mem_mapIdx {b : β} {xs : Array α} :
b mapIdx f xs (i : Nat) (h : i < xs.size), f i xs[i] = b := by
constructor
· intro h
@@ -414,7 +416,7 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
rcases xs with xs
simp [List.mapIdx_eq_mapIdx_iff]
@[simp] theorem mapIdx_set {f : Nat α β} {xs : Array α} {i : Nat} {h : i < xs.size} {a : α} :
@[simp, grind =] theorem mapIdx_set {f : Nat α β} {xs : Array α} {i : Nat} {h : i < xs.size} {a : α} :
(xs.set i a).mapIdx f = (xs.mapIdx f).set i (f i a) (by simpa) := by
rcases xs with xs
simp [List.mapIdx_set]
@@ -424,17 +426,17 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
rcases xs with xs
simp [List.mapIdx_set]
@[simp] theorem back?_mapIdx {xs : Array α} {f : Nat α β} :
@[simp, grind =] theorem back?_mapIdx {xs : Array α} {f : Nat α β} :
(mapIdx f xs).back? = (xs.back?).map (f (xs.size - 1)) := by
rcases xs with xs
simp [List.getLast?_mapIdx]
@[simp] theorem back_mapIdx {xs : Array α} {f : Nat α β} (h) :
@[simp, grind =] theorem back_mapIdx {xs : Array α} {f : Nat α β} (h) :
(xs.mapIdx f).back h = f (xs.size - 1) (xs.back (by simpa using h)) := by
rcases xs with xs
simp [List.getLast_mapIdx]
@[simp] theorem mapIdx_mapIdx {xs : Array α} {f : Nat α β} {g : Nat β γ} :
@[simp, grind =] theorem mapIdx_mapIdx {xs : Array α} {f : Nat α β} {g : Nat β γ} :
(xs.mapIdx f).mapIdx g = xs.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff]
@@ -447,7 +449,7 @@ theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat → α → β} {b : β}
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapIdx_eq_mkArray_iff := @mapIdx_eq_replicate_iff
@[simp] theorem mapIdx_reverse {xs : Array α} {f : Nat α β} :
@[simp, grind =] theorem mapIdx_reverse {xs : Array α} {f : Nat α β} :
xs.reverse.mapIdx f = (mapIdx (fun i => f (xs.size - 1 - i)) xs).reverse := by
rcases xs with xs
simp [List.mapIdx_reverse]
@@ -456,7 +458,7 @@ end Array
namespace List
@[grind] theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind =] theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : (i : Nat) α (h : i < l.length) m β} :
l.toArray.mapFinIdxM f = toArray <$> l.mapFinIdxM f := by
let rec go (i : Nat) (acc : Array β) (inv : i + acc.size = l.length) :
@@ -477,7 +479,7 @@ namespace List
simp only [Array.mapFinIdxM, mapFinIdxM]
exact go _ #[] _
@[grind] theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind =] theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : Nat α m β} :
l.toArray.mapIdxM f = toArray <$> l.mapIdxM f := by
let rec go (bs : List α) (acc : Array β) (inv : bs.length + acc.size = l.length) :

View File

@@ -23,7 +23,7 @@ namespace Array
/-! ### ofFn -/
@[simp] theorem ofFn_zero {f : Fin 0 α} : ofFn f = #[] := by
@[simp, grind =] theorem ofFn_zero {f : Fin 0 α} : ofFn f = #[] := by
simp [ofFn, ofFn.go]
theorem ofFn_succ {f : Fin (n+1) α} :
@@ -42,10 +42,10 @@ theorem ofFn_add {n m} {f : Fin (n + m) → α} :
| zero => simp
| succ m ih => simp [ofFn_succ, ih]
@[simp] theorem _root_.List.toArray_ofFn {f : Fin n α} : (List.ofFn f).toArray = Array.ofFn f := by
@[simp, grind =] theorem _root_.List.toArray_ofFn {f : Fin n α} : (List.ofFn f).toArray = Array.ofFn f := by
ext <;> simp
@[simp] theorem toList_ofFn {f : Fin n α} : (Array.ofFn f).toList = List.ofFn f := by
@[simp, grind =] theorem toList_ofFn {f : Fin n α} : (Array.ofFn f).toList = List.ofFn f := by
apply List.ext_getElem <;> simp
theorem ofFn_succ' {f : Fin (n+1) α} :
@@ -58,7 +58,7 @@ theorem ofFn_eq_empty_iff {f : Fin n → α} : ofFn f = #[] ↔ n = 0 := by
rw [ Array.toList_inj]
simp
@[simp 500]
@[simp 500, grind =]
theorem mem_ofFn {n} {f : Fin n α} {a : α} : a ofFn f i, f i = a := by
constructor
· intro w
@@ -73,7 +73,7 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
def ofFnM {n} [Monad m] (f : Fin n m α) : m (Array α) :=
Fin.foldlM n (fun xs i => xs.push <$> f i) (Array.emptyWithCapacity n)
@[simp]
@[simp, grind =]
theorem ofFnM_zero [Monad m] {f : Fin 0 m α} : ofFnM f = pure #[] := by
simp [ofFnM]
@@ -109,7 +109,7 @@ theorem ofFnM_add {n m} [Monad m] [LawfulMonad m] {f : Fin (n + k) → m α} :
funext x
simp
@[simp] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
@[simp, grind =] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
toList <$> ofFnM f = List.ofFnM f := by
induction n with
| zero => simp

View File

@@ -91,17 +91,26 @@ theorem Perm.mem_iff {a : α} {xs ys : Array α} (p : xs ~ ys) : a ∈ xs ↔ a
simp only [perm_iff_toList_perm] at p
simpa using p.mem_iff
grind_pattern Perm.mem_iff => xs ~ ys, a xs
grind_pattern Perm.mem_iff => xs ~ ys, a ys
theorem Perm.append {xs ys as bs : Array α} (p₁ : xs ~ ys) (p₂ : as ~ bs) :
xs ++ as ~ ys ++ bs := by
cases xs; cases ys; cases as; cases bs
simp only [append_toArray, perm_iff_toList_perm] at p₁ p₂
exact p₁.append p₂
grind_pattern Perm.append => xs ~ ys, as ~ bs, xs ++ as
grind_pattern Perm.append => xs ~ ys, as ~ bs, ys ++ bs
theorem Perm.push (x : α) {xs ys : Array α} (p : xs ~ ys) :
xs.push x ~ ys.push x := by
rw [push_eq_append_singleton]
exact p.append .rfl
grind_pattern Perm.push => xs ~ ys, xs.push x
grind_pattern Perm.push => xs ~ ys, ys.push x
theorem Perm.push_comm (x y : α) {xs ys : Array α} (p : xs ~ ys) :
(xs.push x).push y ~ (ys.push y).push x := by
cases xs; cases ys

View File

@@ -128,6 +128,16 @@ theorem erase_range' :
simp only [ List.toArray_range', List.erase_toArray]
simp [List.erase_range']
@[simp, grind =]
theorem count_range' {a s n step} (h : 0 < step := by simp) :
count a (range' s n step) = if i, i < n a = s + step * i then 1 else 0 := by
rw [ List.toArray_range', List.count_toArray, List.count_range' h]
@[simp, grind =]
theorem count_range_1' {a s n} :
count a (range' s n) = if s a a < s + n then 1 else 0 := by
rw [ List.toArray_range', List.count_toArray, List.count_range_1']
/-! ### range -/
@[grind _=_]
@@ -179,11 +189,11 @@ theorem self_mem_range_succ {n : Nat} : n ∈ range (n + 1) := by simp
@[simp, grind =] theorem take_range {i n : Nat} : take (range n) i = range (min i n) := by
ext <;> simp
@[simp] theorem find?_range_eq_some {n : Nat} {i : Nat} {p : Nat Bool} :
@[simp, grind =] 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']
@[simp] theorem find?_range_eq_none {n : Nat} {p : Nat Bool} :
@[simp, grind =] theorem find?_range_eq_none {n : Nat} {p : Nat Bool} :
(range n).find? p = none i, i < n !p i := by
simp only [ List.toArray_range, List.find?_toArray, List.find?_range_eq_none]
@@ -191,6 +201,10 @@ theorem self_mem_range_succ {n : Nat} : n ∈ range (n + 1) := by simp
theorem erase_range : (range n).erase i = range (min n i) ++ range' (i + 1) (n - (i + 1)) := by
simp [range_eq_range', erase_range']
@[simp, grind =]
theorem count_range {a n} :
count a (range n) = if a < n then 1 else 0 := by
rw [ List.toArray_range, List.count_toArray, List.count_range]
/-! ### zipIdx -/
@@ -199,7 +213,7 @@ theorem zipIdx_eq_empty_iff {xs : Array α} {i : Nat} : xs.zipIdx i = #[] ↔ xs
cases xs
simp
@[simp]
@[simp, grind =]
theorem getElem?_zipIdx {xs : Array α} {i j} : (zipIdx xs i)[j]? = xs[j]?.map fun a => (a, i + j) := by
simp [getElem?_def]
@@ -242,7 +256,7 @@ theorem zipIdx_eq_map_add {xs : Array α} {i : Nat} :
simp only [zipIdx_toArray, List.map_toArray, mk.injEq]
rw [List.zipIdx_eq_map_add]
@[simp]
@[simp, grind =]
theorem zipIdx_singleton {x : α} {k : Nat} : zipIdx #[x] k = #[(x, k)] :=
rfl
@@ -290,6 +304,7 @@ theorem zipIdx_map {xs : Array α} {k : Nat} {f : α → β} :
cases xs
simp [List.zipIdx_map]
@[grind =]
theorem zipIdx_append {xs ys : Array α} {k : Nat} :
zipIdx (xs ++ ys) k = zipIdx xs k ++ zipIdx ys (k + xs.size) := by
cases xs

View File

@@ -45,6 +45,7 @@ theorem zipWith_self {f : αα → δ} {xs : Array α} : zipWith f xs xs =
See also `getElem?_zipWith'` for a variant
using `Option.map` and `Option.bind` rather than a `match`.
-/
@[grind =]
theorem getElem?_zipWith {f : α β γ} {i : Nat} :
(zipWith f as bs)[i]? = match as[i]?, bs[i]? with
| some a, some b => some (f a b) | _, _ => none := by
@@ -76,31 +77,35 @@ theorem getElem?_zip_eq_some {as : Array α} {bs : Array β} {z : α × β} {i :
· rintro h₀, h₁
exact _, _, h₀, h₁, rfl
@[simp]
@[simp, grind =]
theorem zipWith_map {μ} {f : γ δ μ} {g : α γ} {h : β δ} {as : Array α} {bs : Array β} :
zipWith f (as.map g) (bs.map h) = zipWith (fun a b => f (g a) (h b)) as bs := by
cases as
cases bs
simp [List.zipWith_map]
@[grind =]
theorem zipWith_map_left {as : Array α} {bs : Array β} {f : α α'} {g : α' β γ} :
zipWith g (as.map f) bs = zipWith (fun a b => g (f a) b) as bs := by
cases as
cases bs
simp [List.zipWith_map_left]
@[grind =]
theorem zipWith_map_right {as : Array α} {bs : Array β} {f : β β'} {g : α β' γ} :
zipWith g as (bs.map f) = zipWith (fun a b => g a (f b)) as bs := by
cases as
cases bs
simp [List.zipWith_map_right]
@[grind =]
theorem zipWith_foldr_eq_zip_foldr {f : α β γ} {i : δ} :
(zipWith f as bs).foldr g i = (zip as bs).foldr (fun p r => g (f p.1 p.2) r) i := by
cases as
cases bs
simp [List.zipWith_foldr_eq_zip_foldr]
@[grind =]
theorem zipWith_foldl_eq_zip_foldl {f : α β γ} {i : δ} :
(zipWith f as bs).foldl g i = (zip as bs).foldl (fun r p => g r (f p.1 p.2)) i := by
cases as
@@ -111,22 +116,26 @@ theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} :
theorem zipWith_eq_empty_iff {f : α β γ} {as : Array α} {bs : Array β} : zipWith f as bs = #[] as = #[] bs = #[] := by
cases as <;> cases bs <;> simp
@[grind =]
theorem map_zipWith {δ : Type _} {f : α β} {g : γ δ α} {cs : Array γ} {ds : Array δ} :
map f (zipWith g cs ds) = zipWith (fun x y => f (g x y)) cs ds := by
cases cs
cases ds
simp [List.map_zipWith]
@[grind =]
theorem take_zipWith : (zipWith f as bs).take i = zipWith f (as.take i) (bs.take i) := by
cases as
cases bs
simp [List.take_zipWith]
@[grind =]
theorem extract_zipWith : (zipWith f as bs).extract i j = zipWith f (as.extract i j) (bs.extract i j) := by
cases as
cases bs
simp [List.drop_zipWith, List.take_zipWith]
@[grind =]
theorem zipWith_append {f : α β γ} {as as' : Array α} {bs bs' : Array β}
(h : as.size = bs.size) :
zipWith f (as ++ as') (bs ++ bs') = zipWith f as bs ++ zipWith f as' bs' := by
@@ -152,7 +161,7 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array
· rintro ws, xs, ys, zs, h, rfl, rfl, h₁, h₂
exact ws, xs, ys, zs, by simp_all
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
@[simp, grind =] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
simp [ List.toArray_replicate]
@@ -184,6 +193,7 @@ theorem zipWith_eq_zipWith_take_min (as : Array α) (bs : Array β) :
simp
rw [List.zipWith_eq_zipWith_take_min]
@[grind =]
theorem reverse_zipWith (h : as.size = bs.size) :
(zipWith f as bs).reverse = zipWith f as.reverse bs.reverse := by
cases as
@@ -200,7 +210,7 @@ theorem lt_size_right_of_zip {i : Nat} {as : Array α} {bs : Array β} (h : i <
i < bs.size :=
lt_size_right_of_zipWith h
@[simp]
@[simp, grind =]
theorem getElem_zip {as : Array α} {bs : Array β} {i : Nat} {h : i < (zip as bs).size} :
(zip as bs)[i] =
(as[i]'(lt_size_left_of_zip h), bs[i]'(lt_size_right_of_zip h)) :=
@@ -211,18 +221,22 @@ theorem zip_eq_zipWith {as : Array α} {bs : Array β} : zip as bs = zipWith Pro
cases bs
simp [List.zip_eq_zipWith]
@[grind _=_]
theorem zip_map {f : α γ} {g : β δ} {as : Array α} {bs : Array β} :
zip (as.map f) (bs.map g) = (zip as bs).map (Prod.map f g) := by
cases as
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]
@[grind =]
theorem zip_append {as bs : Array α} {cs ds : Array β} (_h : as.size = cs.size) :
zip (as ++ bs) (cs ++ ds) = zip as cs ++ zip bs ds := by
cases as
@@ -231,6 +245,7 @@ theorem zip_append {as bs : Array α} {cs ds : Array β} (_h : as.size = cs.size
cases ds
simp_all [List.zip_append]
@[grind =]
theorem zip_map' {f : α β} {g : α γ} {xs : Array α} :
zip (xs.map f) (xs.map g) = xs.map fun a => (f a, g a) := by
cases xs
@@ -276,7 +291,7 @@ theorem zip_eq_append_iff {as : Array α} {bs : Array β} :
as₁ as₂ bs₁ bs₂, as₁.size = bs₁.size as = as₁ ++ as₂ bs = bs₁ ++ bs₂ xs = zip as₁ bs₁ ys = zip as₂ bs₂ := by
simp [zip_eq_zipWith, zipWith_eq_append_iff]
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
@[simp, grind =] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
simp [ List.toArray_replicate]
@@ -293,6 +308,7 @@ theorem zip_eq_zip_take_min {as : Array α} {bs : Array β} :
/-! ### zipWithAll -/
@[grind =]
theorem getElem?_zipWithAll {f : Option α Option β γ} {i : Nat} :
(zipWithAll f as bs)[i]? = match as[i]?, bs[i]? with
| none, none => .none | a?, b? => some (f a? b?) := by
@@ -301,31 +317,35 @@ theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
simp [List.getElem?_zipWithAll]
rfl
@[grind =]
theorem zipWithAll_map {μ} {f : Option γ Option δ μ} {g : α γ} {h : β δ} {as : Array α} {bs : Array β} :
zipWithAll f (as.map g) (bs.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) as bs := by
cases as
cases bs
simp [List.zipWithAll_map]
@[grind =]
theorem zipWithAll_map_left {as : Array α} {bs : Array β} {f : α α'} {g : Option α' Option β γ} :
zipWithAll g (as.map f) bs = zipWithAll (fun a b => g (f <$> a) b) as bs := by
cases as
cases bs
simp [List.zipWithAll_map_left]
@[grind =]
theorem zipWithAll_map_right {as : Array α} {bs : Array β} {f : β β'} {g : Option α Option β' γ} :
zipWithAll g as (bs.map f) = zipWithAll (fun a b => g a (f <$> b)) as bs := by
cases as
cases bs
simp [List.zipWithAll_map_right]
@[grind =]
theorem map_zipWithAll {δ : Type _} {f : α β} {g : Option γ Option δ α} {cs : Array γ} {ds : Array δ} :
map f (zipWithAll g cs ds) = zipWithAll (fun x y => f (g x y)) cs ds := by
cases cs
cases ds
simp [List.map_zipWithAll]
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
@[simp, grind =] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (replicate n a) (replicate n b) = replicate n (f (some a) (some b)) := by
simp [ List.toArray_replicate]
@@ -342,6 +362,7 @@ theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
simp
@[grind =]
theorem unzip_eq_map {xs : Array (α × β)} : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
cases xs
simp [List.unzip_eq_map]
@@ -375,9 +396,11 @@ theorem zip_of_prod {as : Array α} {bs : Array β} {xs : Array (α × β)} (hl
(hr : xs.map Prod.snd = bs) : xs = as.zip bs := by
rw [ hl, hr, zip_unzip xs, fst_unzip, snd_unzip, zip_unzip, zip_unzip]
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
@[simp, grind =] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
ext1 <;> simp
@[deprecated unzip_replicate (since := "2025-03-18")]
abbrev unzip_mkArray := @unzip_replicate
end Array

View File

@@ -6,7 +6,10 @@ Authors: Kim Morrison
module
prelude
import Init.Data.BitVec.BasicAux
import Init.Data.BitVec.Basic
import Init.Data.BitVec.Bootstrap
import Init.Data.BitVec.Bitblast
import Init.Data.BitVec.Folds
import Init.Data.BitVec.Decidable
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Folds

View File

@@ -74,25 +74,27 @@ section getXsb
/--
Returns the `i`th least significant bit.
This will be renamed `getLsb` after the existing deprecated alias is removed.
-/
@[inline, expose] def getLsb' (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
@[inline, expose] def getLsb (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
@[deprecated getLsb (since := "2025-06-17"), inherit_doc getLsb]
abbrev getLsb' := @getLsb
/-- Returns the `i`th least significant bit, or `none` if `i ≥ w`. -/
@[inline, expose] def getLsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getLsb' x i, h) else none
if h : i < w then some (getLsb x i, h) else none
/--
Returns the `i`th most significant bit.
This will be renamed `BitVec.getMsb` after the existing deprecated alias is removed.
-/
@[inline] def getMsb' (x : BitVec w) (i : Fin w) : Bool := x.getLsb' w-1-i, by omega
@[inline] def getMsb (x : BitVec w) (i : Fin w) : Bool := x.getLsb w-1-i, by omega
@[deprecated getMsb (since := "2025-06-17"), inherit_doc getMsb]
abbrev getMsb' := @getMsb
/-- Returns the `i`th most significant bit or `none` if `i ≥ w`. -/
@[inline] def getMsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getMsb' x i, h) else none
if h : i < w then some (getMsb x i, h) else none
/-- Returns the `i`th least significant bit or `false` if `i ≥ w`. -/
@[inline, expose] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
@@ -110,11 +112,11 @@ end getXsb
section getElem
instance : GetElem (BitVec w) Nat Bool fun _ i => i < w where
getElem xs i h := xs.getLsb' i, h
getElem xs i h := xs.getLsb i, h
/-- We prefer `x[i]` as the simp normal form for `getLsb'` -/
@[simp] theorem getLsb'_eq_getElem (x : BitVec w) (i : Fin w) :
x.getLsb' i = x[i] := rfl
@[simp] theorem getLsb_eq_getElem (x : BitVec w) (i : Fin w) :
x.getLsb i = x[i] := rfl
/-- We prefer `x[i]?` as the simp normal form for `getLsb?` -/
@[simp] theorem getLsb?_eq_getElem? (x : BitVec w) (i : Nat) :
@@ -174,7 +176,7 @@ recommended_spelling "zero" for "0#n" in [BitVec.ofNat, «term__#__»]
recommended_spelling "one" for "1#n" in [BitVec.ofNat, «term__#__»]
/-- Unexpander for bitvector literals. -/
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNat] meta def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
| `($(_) $n $i:num) => `($i:num#$n)
| _ => throw ()
@@ -183,7 +185,7 @@ scoped syntax:max term:max noWs "#'" noWs term:max : term
macro_rules | `($i#'$p) => `(BitVec.ofNatLT $i $p)
/-- Unexpander for bitvector literals without truncation. -/
@[app_unexpander BitVec.ofNatLT] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNatLT] meta def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
| `($(_) $i $p) => `($i#'$p)
| _ => throw ()
@@ -723,6 +725,12 @@ def twoPow (w : Nat) (i : Nat) : BitVec w := 1#w <<< i
end bitwise
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
def intMin (w : Nat) := twoPow w (w - 1)
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) := (twoPow w (w - 1)) - 1
/--
Computes a hash of a bitvector, combining 64-bit words using `mixHash`.
-/

View File

@@ -6,12 +6,14 @@ Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix, Siddharth Bhat
module
prelude
import Init.Data.BitVec.Folds
import all Init.Data.Nat.Bitwise.Basic
import Init.Data.Nat.Mod
import all Init.Data.Int.DivMod
import Init.Data.Int.LemmasAux
import all Init.Data.BitVec.Lemmas
import all Init.Data.BitVec.Basic
import Init.Data.BitVec.Decidable
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Folds
/-!
# Bit blasting of bitvectors
@@ -518,9 +520,6 @@ theorem msb_neg {w : Nat} {x : BitVec w} :
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
omega
@[simp] theorem setWidth_neg_of_le {x : BitVec v} (h : w v) : BitVec.setWidth w (-x) = -BitVec.setWidth w x := by
simp [ BitVec.signExtend_eq_setWidth_of_le _ h, BitVec.signExtend_neg_of_le h]
/-! ### abs -/
theorem msb_abs {w : Nat} {x : BitVec w} :
@@ -548,54 +547,14 @@ theorem ult_eq_not_carry (x y : BitVec w) : x.ult y = !carry w x (~~~y) true :=
rw [Nat.mod_eq_of_lt (by omega)]
omega
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
simp [BitVec.ule, BitVec.ult, decide_not]
theorem ule_eq_carry (x y : BitVec w) : x.ule y = carry w y (~~~x) true := by
simp [ule_eq_not_ult, ult_eq_not_carry]
/-- If two bitvectors have the same `msb`, then signed and unsigned comparisons coincide -/
theorem slt_eq_ult_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) :
x.slt y = x.ult y := by
simp only [BitVec.slt, toInt_eq_msb_cond, BitVec.ult, decide_eq_decide, h]
cases y.msb <;> simp
/-- If two bitvectors have different `msb`s, then unsigned comparison is determined by this bit -/
theorem ult_eq_msb_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.ult y = y.msb := by
simp only [BitVec.ult, msb_eq_decide, ne_eq, decide_eq_decide] at *
omega
/-- If two bitvectors have different `msb`s, then signed and unsigned comparisons are opposites -/
theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.slt y = !x.ult y := by
simp only [BitVec.slt, toInt_eq_msb_cond, Bool.eq_not_of_ne h, ult_eq_msb_of_msb_neq h]
cases y.msb <;> (simp [-Int.natCast_pow]; omega)
theorem slt_eq_ult {x y : BitVec w} :
x.slt y = (x.msb != y.msb).xor (x.ult y) := by
by_cases h : x.msb = y.msb
· simp [h, slt_eq_ult_of_msb_eq]
· have h' : x.msb != y.msb := by simp_all
simp [slt_eq_not_ult_of_msb_neq h, h']
theorem slt_eq_not_carry {x y : BitVec w} :
x.slt y = (x.msb == y.msb).xor (carry w x (~~~y) true) := by
simp only [slt_eq_ult, bne, ult_eq_not_carry]
cases x.msb == y.msb <;> simp
theorem sle_eq_not_slt {x y : BitVec w} : x.sle y = !y.slt x := by
simp only [BitVec.sle, BitVec.slt, decide_not, decide_eq_decide]; omega
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x x.msb = false := by
simp [zero_sle_eq_not_msb]
theorem toNat_toInt_of_sle {w : Nat} {x : BitVec w} (hx : BitVec.sle 0#w x) : x.toInt.toNat = x.toNat :=
toNat_toInt_of_msb x (zero_sle_iff_msb_eq_false.1 hx)
theorem sle_eq_carry {x y : BitVec w} :
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
@@ -618,12 +577,6 @@ theorem neg_sle_zero (h : 0 < w) {x : BitVec w} :
rw [sle_eq_slt_or_eq, neg_slt_zero h, sle_eq_slt_or_eq]
simp [Bool.beq_eq_decide_eq (-x), Bool.beq_eq_decide_eq _ x, Eq.comm (a := x), Bool.or_assoc]
theorem sle_eq_ule {x y : BitVec w} : x.sle y = (x.msb != y.msb ^^ x.ule y) := by
rw [sle_eq_not_slt, slt_eq_ult, Bool.xor_not, ule_eq_not_ult, bne_comm]
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]
/-! ### mul recurrence for bit blasting -/
/--
@@ -1023,7 +976,7 @@ theorem DivModState.toNat_shiftRight_sub_one_eq
{args : DivModArgs w} {qr : DivModState w} (h : qr.Poised args) :
args.n.toNat >>> (qr.wn - 1)
= (args.n.toNat >>> qr.wn) * 2 + (args.n.getLsbD (qr.wn - 1)).toNat := by
show BitVec.toNat (args.n >>> (qr.wn - 1)) = _
change BitVec.toNat (args.n >>> (qr.wn - 1)) = _
have {..} := h -- break the structure down for `omega`
rw [shiftRight_sub_one_eq_shiftConcat args.n h.hwn_lt]
rw [toNat_shiftConcat_eq_of_lt (k := w - qr.wn)]

View File

@@ -0,0 +1,146 @@
/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth Bhat
-/
module
prelude
import all Init.Data.BitVec.Basic
namespace BitVec
theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsbD i := rfl
@[simp] theorem getLsbD_ofFin (x : Fin (2^n)) (i : Nat) :
getLsbD (BitVec.ofFin x) i = x.val.testBit i := rfl
@[simp] 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
have p : 2^w 2^i := Nat.pow_le_pow_right (by omega) ge
omega
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toNat_eq {n} : {x y : BitVec n}, x.toNat = y.toNat x = y
| _, _, _, _, rfl => rfl
theorem eq_of_getLsbD_eq {x y : BitVec w}
(pred : i, i < w x.getLsbD i = y.getLsbD i) : x = y := by
apply eq_of_toNat_eq
apply Nat.eq_of_testBit_eq
intro i
if i_lt : i < w then
exact pred i i_lt
else
have p : i w := Nat.le_of_not_gt i_lt
simp [testBit_toNat, getLsbD_of_ge _ _ p]
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat]
@[ext] theorem eq_of_getElem_eq {x y : BitVec n} :
( i (hi : i < n), x[i] = y[i]) x = y :=
fun h => BitVec.eq_of_getLsbD_eq (h ·)
@[simp] theorem toNat_append (x : BitVec m) (y : BitVec n) :
(x ++ y).toNat = x.toNat <<< n ||| y.toNat :=
rfl
@[simp] theorem toNat_ofBool (b : Bool) : (ofBool b).toNat = b.toNat := by
cases b <;> rfl
@[simp, bitvec_to_nat] theorem toNat_cast (h : w = v) (x : BitVec w) : (x.cast h).toNat = x.toNat := rfl
@[simp, bitvec_to_nat] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val := rfl
@[simp] theorem toNat_ofNatLT (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[simp] theorem toNat_cons (b : Bool) (x : BitVec w) :
(cons b x).toNat = (b.toNat <<< w) ||| x.toNat := by
let x, _ := x
simp only [cons, toNat_cast, toNat_append, toNat_ofBool, toNat_ofFin]
theorem getElem_cons {b : Bool} {n} {x : BitVec n} {i : Nat} (h : i < n + 1) :
(cons b x)[i] = if h : i = n then b else x[i] := by
simp only [getElem_eq_testBit_toNat, toNat_cons, Nat.testBit_or, getLsbD]
rw [Nat.testBit_shiftLeft]
rcases Nat.lt_trichotomy i n with i_lt_n | i_eq_n | n_lt_i
· have p1 : ¬(n i) := by omega
have p2 : i n := by omega
simp [p1, p2]
· simp only [i_eq_n, ge_iff_le, Nat.le_refl, decide_true, Nat.sub_self, Nat.testBit_zero,
Bool.true_and, testBit_toNat, getLsbD_of_ge, Bool.or_false, reduceIte]
cases b <;> trivial
· have p1 : i n := by omega
have p2 : i - n 0 := by omega
simp [p1, p2, Nat.testBit_bool_to_nat]
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_right (by trivial : 0 < 2) le)
@[simp, bitvec_to_nat] theorem toNat_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
(setWidth' p x).toNat = x.toNat := by
simp only [setWidth', toNat_ofNatLT]
@[simp, bitvec_to_nat] theorem toNat_setWidth (i : Nat) (x : BitVec n) :
BitVec.toNat (setWidth i x) = x.toNat % 2^i := by
let x, lt_n := x
simp only [setWidth]
if n_le_i : n i then
have x_lt_two_i : x < 2 ^ i := lt_two_pow_of_le lt_n n_le_i
simp [n_le_i, Nat.mod_eq_of_lt, x_lt_two_i]
else
simp [n_le_i, toNat_ofNat]
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : BitVec.ofNat m x.toNat = setWidth m x := by
apply eq_of_toNat_eq
simp only [toNat_ofNat, toNat_setWidth]
theorem getElem_setWidth' (x : BitVec w) (i : Nat) (h : w v) (hi : i < v) :
(setWidth' h x)[i] = x.getLsbD i := by
rw [getElem_eq_testBit_toNat, toNat_setWidth', getLsbD]
@[simp]
theorem getElem_setWidth (m : Nat) (x : BitVec n) (i : Nat) (h : i < m) :
(setWidth m x)[i] = x.getLsbD i := by
rw [setWidth]
split
· rw [getElem_setWidth']
· simp only [ofNat_toNat, getElem_eq_testBit_toNat, toNat_setWidth, Nat.testBit_mod_two_pow,
getLsbD, Bool.and_eq_right_iff_imp, decide_eq_true_eq]
omega
@[simp] theorem cons_msb_setWidth (x : BitVec (w+1)) : (cons x.msb (x.setWidth w)) = x := by
ext i
simp only [getElem_cons]
split <;> rename_i h
· simp [BitVec.msb, getMsbD, h]
· by_cases h' : i < w
· simp_all only [getElem_setWidth, getLsbD_eq_getElem]
· omega
@[simp, bitvec_to_nat] theorem toNat_neg (x : BitVec n) : (- x).toNat = (2^n - x.toNat) % 2^n := by
simp [Neg.neg, BitVec.neg]
@[simp] theorem setWidth_neg_of_le {x : BitVec v} (h : w v) : BitVec.setWidth w (-x) = -BitVec.setWidth w x := by
apply BitVec.eq_of_toNat_eq
simp only [toNat_setWidth, toNat_neg]
rw [Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 h)]
rw [Nat.mod_eq_mod_iff]
rw [Nat.mod_def]
refine 1 + x.toNat / 2^w, 2^(v-w), ?_
rw [ Nat.pow_add]
have : v - w + w = v := by omega
rw [this]
rw [Nat.add_mul, Nat.one_mul, Nat.mul_comm (2^w)]
have sub_sub : (a : Nat) {b c : Nat} (h : c b), a - (b - c) = a + c - b := by omega
rw [sub_sub _ (Nat.div_mul_le_self x.toNat (2 ^ w))]
have : x.toNat / 2 ^ w * 2 ^ w x.toNat := Nat.div_mul_le_self x.toNat (2 ^ w)
have : x.toNat < 2 ^w x.toNat - 2 ^ w < x.toNat / 2 ^ w * 2 ^ w := by
have := Nat.lt_div_mul_add (a := x.toNat) (b := 2 ^ w) (Nat.two_pow_pos w)
omega
omega
end BitVec

View File

@@ -0,0 +1,79 @@
/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth Bhat
-/
module
prelude
import Init.Data.BitVec.Bootstrap
set_option linter.missingDocs true
namespace BitVec
/-! ### Decidable quantifiers -/
theorem forall_zero_iff {P : BitVec 0 Prop} :
( v, P v) P 0#0 := by
constructor
· intro h
apply h
· intro h v
obtain (rfl : v = 0#0) := (by ext i )
apply h
theorem forall_cons_iff {P : BitVec (n + 1) Prop} :
( v : BitVec (n + 1), P v) ( (x : Bool) (v : BitVec n), P (v.cons x)) := by
constructor
· intro h _ _
apply h
· intro h v
have w : v = (v.setWidth n).cons v.msb := by simp only [cons_msb_setWidth]
rw [w]
apply h
instance instDecidableForallBitVecZero (P : BitVec 0 Prop) :
[Decidable (P 0#0)], Decidable ( v, P v)
| .isTrue h => .isTrue fun v => by
obtain (rfl : v = 0#0) := (by ext i )
exact h
| .isFalse h => .isFalse (fun w => h (w _))
instance instDecidableForallBitVecSucc (P : BitVec (n+1) Prop) [DecidablePred P]
[Decidable ( (x : Bool) (v : BitVec n), P (v.cons x))] : Decidable ( v, P v) :=
decidable_of_iff' ( x (v : BitVec n), P (v.cons x)) forall_cons_iff
instance instDecidableExistsBitVecZero (P : BitVec 0 Prop) [Decidable (P 0#0)] :
Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
instance instDecidableExistsBitVecSucc (P : BitVec (n+1) Prop) [DecidablePred P]
[Decidable ( (x : Bool) (v : BitVec n), ¬ P (v.cons x))] : Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
/--
For small numerals this isn't necessary (as typeclass search can use the above two instances),
but for large numerals this provides a shortcut.
Note, however, that for large numerals the decision procedure may be very slow,
and you should use `bv_decide` if possible.
-/
instance instDecidableForallBitVec :
(n : Nat) (P : BitVec n Prop) [DecidablePred P], Decidable ( v, P v)
| 0, _, _ => inferInstance
| n + 1, _, _ =>
have := instDecidableForallBitVec n
inferInstance
/--
For small numerals this isn't necessary (as typeclass search can use the above two instances),
but for large numerals this provides a shortcut.
Note, however, that for large numerals the decision procedure may be very slow.
-/
instance instDecidableExistsBitVec :
(n : Nat) (P : BitVec n Prop) [DecidablePred P], Decidable ( v, P v)
| 0, _, _ => inferInstance
| _ + 1, _, _ => inferInstance
end BitVec

View File

@@ -2,7 +2,6 @@
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth Bhat
-/
module
@@ -19,6 +18,7 @@ import Init.Data.Int.Bitwise.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.Pow
import Init.Data.Int.LemmasAux
import Init.Data.BitVec.Bootstrap
set_option linter.missingDocs true
@@ -27,19 +27,9 @@ namespace BitVec
@[simp] theorem mk_zero : BitVec.ofFin (w := w) 0, h = 0#w := rfl
@[simp] theorem ofNatLT_zero : BitVec.ofNatLT (w := w) 0 h = 0#w := rfl
@[simp] theorem getLsbD_ofFin (x : Fin (2^n)) (i : Nat) :
getLsbD (BitVec.ofFin x) i = x.val.testBit i := rfl
@[simp] theorem getElem_ofFin (x : Fin (2^n)) (i : Nat) (h : i < n) :
(BitVec.ofFin x)[i] = x.val.testBit i := rfl
@[simp] 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
have p : 2^w 2^i := Nat.pow_le_pow_right (by omega) ge
omega
@[simp] 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]
@@ -127,10 +117,6 @@ 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]
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toNat_eq {n} : {x y : BitVec n}, x.toNat = y.toNat x = y
| _, _, _, _, rfl => rfl
/-- 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
constructor
@@ -153,26 +139,28 @@ protected theorem toNat_lt_twoPow_of_le (h : m ≤ n) {x : BitVec m} :
apply Nat.pow_le_pow_of_le
<;> omega
theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsbD i := rfl
theorem two_pow_le_toNat_of_getElem_eq_true {i : Nat} {x : BitVec w}
(hi : i < w) (hx : x[i] = true) : 2^i x.toNat := by
apply Nat.ge_two_pow_of_testBit
rw [ getElem_eq_testBit_toNat x i hi]
exact hx
theorem getMsb'_eq_getLsb' (x : BitVec w) (i : Fin w) :
x.getMsb' i = x.getLsb' w - 1 - i, by omega := by
simp only [getMsb', getLsb']
theorem getMsb_eq_getLsb (x : BitVec w) (i : Fin w) :
x.getMsb i = x.getLsb w - 1 - i, by omega := by
simp only [getMsb, getLsb]
theorem getMsb?_eq_getLsb? (x : BitVec w) (i : Nat) :
x.getMsb? i = if i < w then x.getLsb? (w - 1 - i) else none := by
simp only [getMsb?, getLsb?_eq_getElem?]
split <;> simp [getMsb'_eq_getLsb']
split <;> simp [getMsb_eq_getLsb]
theorem getMsbD_eq_getLsbD (x : BitVec w) (i : Nat) : x.getMsbD i = (decide (i < w) && x.getLsbD (w - 1 - i)) := by
rw [getMsbD, getLsbD]
@[deprecated getMsb_eq_getLsb (since := "2025-06-17")]
theorem getMsb'_eq_getLsb' (x : BitVec w) (i : Nat) : x.getMsbD i = (decide (i < w) && x.getLsbD (w - 1 - i)) := by
rw [getMsbD, getLsbD]
theorem getLsbD_eq_getMsbD (x : BitVec w) (i : Nat) : x.getLsbD i = (decide (i < w) && x.getMsbD (w - 1 - i)) := by
rw [getMsbD]
by_cases h₁ : i < w <;> by_cases h₂ : w - 1 - i < w <;>
@@ -241,21 +229,6 @@ theorem getMsbD_eq_getMsb?_getD (x : BitVec w) (i : Nat) :
intros
omega
theorem eq_of_getLsbD_eq {x y : BitVec w}
(pred : i, i < w x.getLsbD i = y.getLsbD i) : x = y := by
apply eq_of_toNat_eq
apply Nat.eq_of_testBit_eq
intro i
if i_lt : i < w then
exact pred i i_lt
else
have p : i w := Nat.le_of_not_gt i_lt
simp [testBit_toNat, getLsbD_of_ge _ _ p]
@[ext] theorem eq_of_getElem_eq {x y : BitVec n} :
( i (hi : i < n), x[i] = y[i]) x = y :=
fun h => BitVec.eq_of_getLsbD_eq (h ·)
theorem eq_of_getLsbD_eq_iff {w : Nat} {x y : BitVec w} :
x = y (i : Nat), i < w x.getLsbD i = y.getLsbD i := by
have iff := @BitVec.eq_of_getElem_eq_iff w x y
@@ -342,9 +315,6 @@ open Fin.NatCast in
@[simp, norm_cast] theorem toFin_natCast (n : Nat) : toFin (n : BitVec w) = (n : Fin (2^w)) := by
rfl
@[simp] theorem toNat_ofBool (b : Bool) : (ofBool b).toNat = b.toNat := by
cases b <;> rfl
@[simp] theorem toInt_ofBool (b : Bool) : (ofBool b).toInt = -b.toInt := by
cases b <;> simp
@@ -368,10 +338,6 @@ theorem ofBool_eq_iff_eq : ∀ {b b' : Bool}, BitVec.ofBool b = BitVec.ofBool b'
@[simp] theorem ofBool_xor_ofBool : ofBool b ^^^ ofBool b' = ofBool (b ^^ b') := by
cases b <;> cases b' <;> rfl
@[simp, bitvec_to_nat] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val := rfl
@[simp] theorem toNat_ofNatLT (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@@ -391,9 +357,6 @@ theorem getLsbD_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := getMsbD_ofNatLT h
@[simp, bitvec_to_nat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat]
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
@@ -581,7 +544,6 @@ theorem msb_eq_getMsbD_zero (x : BitVec w) : x.msb = x.getMsbD 0 := by
/-! ### cast -/
@[simp, bitvec_to_nat] theorem toNat_cast (h : w = v) (x : BitVec w) : (x.cast h).toNat = x.toNat := rfl
@[simp] theorem toFin_cast (h : w = v) (x : BitVec w) :
(x.cast h).toFin = x.toFin.cast (by rw [h]) :=
rfl
@@ -882,6 +844,19 @@ theorem slt_eq_sle_and_ne {x y : BitVec w} : x.slt y = (x.sle y && x != y) := by
apply Bool.eq_iff_iff.2
simp [BitVec.slt, BitVec.sle, Int.lt_iff_le_and_ne, BitVec.toInt_inj]
/-- For all bitvectors `x, y`, either `x` is signed less than `y`,
or is equal to `y`, or is signed greater than `y`. -/
theorem slt_trichotomy (x y : BitVec w) : x.slt y x = y y.slt x := by
simpa [slt_iff_toInt_lt, toInt_inj]
using Int.lt_trichotomy x.toInt y.toInt
/-- For all bitvectors `x, y`, either `x` is unsigned less than `y`,
or is equal to `y`, or is unsigned greater than `y`. -/
theorem lt_trichotomy (x y : BitVec w) :
x < y x = y y < x := by
simpa [ ult_iff_lt, ult_eq_decide, decide_eq_true_eq, toNat_inj]
using Nat.lt_trichotomy x.toNat y.toNat
/-! ### setWidth, zeroExtend and truncate -/
@[simp]
@@ -892,20 +867,6 @@ theorem truncate_eq_setWidth {v : Nat} {x : BitVec w} :
theorem zeroExtend_eq_setWidth {v : Nat} {x : BitVec w} :
zeroExtend v x = setWidth v x := rfl
@[simp, bitvec_to_nat] theorem toNat_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
(setWidth' p x).toNat = x.toNat := by
simp [setWidth']
@[simp, bitvec_to_nat] theorem toNat_setWidth (i : Nat) (x : BitVec n) :
BitVec.toNat (setWidth i x) = x.toNat % 2^i := by
let x, lt_n := x
simp only [setWidth]
if n_le_i : n i then
have x_lt_two_i : x < 2 ^ i := lt_two_pow_of_le lt_n n_le_i
simp [n_le_i, Nat.mod_eq_of_lt, x_lt_two_i]
else
simp [n_le_i, toNat_ofNat]
@[simp] theorem toInt_setWidth (x : BitVec w) :
(x.setWidth v).toInt = Int.bmod x.toNat (2^v) := by
simp [toInt_eq_toNat_bmod, toNat_setWidth, Int.emod_bmod, -Int.natCast_pow]
@@ -923,10 +884,6 @@ theorem zeroExtend_eq_setWidth {v : Nat} {x : BitVec w} :
apply eq_of_toNat_eq
simp [toNat_setWidth]
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : BitVec.ofNat m x.toNat = setWidth m x := by
apply eq_of_toNat_eq
simp
/-- Moves one-sided left toNat equality to BitVec equality. -/
theorem toNat_eq_nat {x : BitVec w} {y : Nat}
: (x.toNat = y) (y < 2^w (x = BitVec.ofNat w y)) := by
@@ -942,19 +899,6 @@ theorem nat_eq_toNat {x : BitVec w} {y : Nat}
rw [@eq_comm _ _ x.toNat]
apply toNat_eq_nat
theorem getElem_setWidth' (x : BitVec w) (i : Nat) (h : w v) (hi : i < v) :
(setWidth' h x)[i] = x.getLsbD i := by
rw [getElem_eq_testBit_toNat, toNat_setWidth', getLsbD]
@[simp]
theorem getElem_setWidth (m : Nat) (x : BitVec n) (i : Nat) (h : i < m) :
(setWidth m x)[i] = x.getLsbD i := by
rw [setWidth]
split
· rw [getElem_setWidth']
· simp [getElem_eq_testBit_toNat, getLsbD]
omega
theorem getElem?_setWidth' (x : BitVec w) (i : Nat) (h : w v) :
(setWidth' h x)[i]? = if i < v then some (x.getLsbD i) else none := by
simp [getElem?_eq, getElem_setWidth']
@@ -1919,6 +1863,63 @@ theorem shiftLeftZeroExtend_eq {x : BitVec w} :
exact Nat.mul_lt_mul_of_pos_right x.isLt (Nat.two_pow_pos _)
· omega
@[simp]
theorem toNat_shiftLeftZeroExtend {x : BitVec w} :
(shiftLeftZeroExtend x n).toNat = x.toNat <<< n := by
rcases n with _|n
· simp [shiftLeftZeroExtend]
· simp only [shiftLeftZeroExtend_eq, toNat_shiftLeft, toNat_setWidth]
have := Nat.pow_lt_pow_of_lt (a := 2) (n := w) (m := w + (n + 1)) (by omega) (by omega)
have : x.toNat <<< (n + 1) < 2 ^ (w + (n + 1)) := by
rw [Nat.shiftLeft_eq, Nat.pow_add (m := w) (n := n + 1), Nat.mul_lt_mul_right (by apply Nat.two_pow_pos (w := n + 1))]
omega
rw [Nat.mod_eq_of_lt (by rw [Nat.mod_eq_of_lt (by omega)]; omega), Nat.mod_eq_of_lt (by omega)]
@[simp]
theorem toInt_shiftLeftZeroExtend {x : BitVec w} :
(shiftLeftZeroExtend x n).toInt = x.toInt * 2 ^ n := by
rw [shiftLeftZeroExtend_eq]
rcases w with _|w
· simp [of_length_zero, shiftLeftZeroExtend_eq]
· rcases n with _|n
· simp [shiftLeftZeroExtend_eq]
· have := Nat.pow_pos (a := 2) (n := n + 1) (by omega)
have : x.toNat <<< (n + 1) < 2 ^ (w + 1 + (n + 1)) := by
rw [Nat.shiftLeft_eq, Nat.pow_add (a := 2) (m := w + 1) (n := n + 1), Nat.mul_lt_mul_right (by omega)]
omega
simp only [shiftLeftZeroExtend_eq, toInt_shiftLeft, toNat_setWidth, Nat.lt_add_right_iff_pos,
Nat.zero_lt_succ, toNat_mod_cancel_of_lt, Int.bmod_def]
by_cases hmsb : x.msb
· have hge := toNat_ge_of_msb_true hmsb
simp only [Nat.add_one_sub_one, ge_iff_le] at hge
rw [Int.emod_eq_of_lt (by norm_cast; rw [Nat.shiftLeft_eq]; omega) (by omega)]
rw_mod_cast [ Nat.add_assoc]
rw [show (2 ^ (w + 1 + n + 1) + 1) / 2 = 2 ^ (w + 1 + n) by omega, Int.natCast_pow,
Int.cast_ofNat_Int, Nat.shiftLeft_eq, Nat.add_assoc, Nat.pow_add (a := 2) (m := w) (n := 1 + n),
Nat.add_comm 1 n]
simp only [Nat.mul_lt_mul_right (by omega), show ¬x.toNat < 2 ^ w by omega, reduceIte,
Int.natCast_mul, Int.natCast_pow, Int.cast_ofNat_Int, toInt_eq_toNat_cond,
show ¬2 * x.toNat < 2 ^ (w + 1) by simp [Nat.pow_add, Nat.mul_comm (2 ^ w) 2, hge]]
norm_cast
simp [Int.natCast_mul, Int.natCast_pow, Int.cast_ofNat_Int, Int.sub_mul,
Int.sub_right_inj, show w + (n + 1) + 1 = (w + 1) + (n + 1) by omega, Nat.pow_add]
· simp only [Bool.not_eq_true] at hmsb
have hle := toNat_lt_of_msb_false (x := x) hmsb
simp only [Nat.add_one_sub_one] at hle
rw [Int.emod_eq_of_lt (by norm_cast; rw [Nat.shiftLeft_eq]; omega) (by omega)]
rw_mod_cast [ Nat.add_assoc]
rw [show (2 ^ (w + 1 + n + 1) + 1) / 2 = 2 ^ (w + 1 + n) by omega, Int.natCast_pow,
Int.cast_ofNat_Int, Nat.shiftLeft_eq, Nat.add_assoc, Nat.pow_add (a := 2) (m := w) (n := 1 + n), Nat.add_comm 1 n]
simp [Nat.mul_lt_mul_right (b := x.toNat) (c := 2 ^ w) (a := 2 ^ (n + 1)) (by omega), hle,
reduceIte, Int.natCast_mul, Int.natCast_pow, Int.cast_ofNat_Int, toInt_eq_toNat_of_msb hmsb]
theorem toFin_shiftLeftZeroExtend {x : BitVec w} :
(shiftLeftZeroExtend x n).toFin = Fin.ofNat (2 ^ (w + n)) (x.toNat * 2 ^ n) := by
rcases w with _|w
· simp [of_length_zero, shiftLeftZeroExtend_eq]
· have := Nat.pow_le_pow_of_le (a := 2) (n := w + 1) (m := w + 1 + n) (by omega) (by omega)
rw [shiftLeftZeroExtend_eq, toFin_shiftLeft, toNat_setWidth, Nat.mod_eq_of_lt (by omega), Nat.shiftLeft_eq]
@[simp] theorem getElem_shiftLeftZeroExtend {x : BitVec m} {n : Nat} (h : i < m + n) :
(shiftLeftZeroExtend x n)[i] = if h' : i < n then false else x[i - n] := by
rw [shiftLeftZeroExtend_eq]
@@ -2660,10 +2661,6 @@ theorem toFin_signExtend (x : BitVec w) :
theorem append_def (x : BitVec v) (y : BitVec w) :
x ++ y = (shiftLeftZeroExtend x w ||| setWidth' (Nat.le_add_left w v) y) := rfl
@[simp] theorem toNat_append (x : BitVec m) (y : BitVec n) :
(x ++ y).toNat = x.toNat <<< n ||| y.toNat :=
rfl
theorem getLsbD_append {x : BitVec n} {y : BitVec m} :
getLsbD (x ++ y) i = if i < m then getLsbD y i else getLsbD x (i - m) := by
simp only [append_def, getLsbD_or, getLsbD_shiftLeftZeroExtend, getLsbD_setWidth']
@@ -3048,11 +3045,6 @@ theorem getMsbD_rev (x : BitVec w) (i : Fin w) :
/-! ### cons -/
@[simp] theorem toNat_cons (b : Bool) (x : BitVec w) :
(cons b x).toNat = (b.toNat <<< w) ||| x.toNat := by
let x, _ := x
simp [cons, toNat_append, toNat_ofBool]
/-- Variant of `toNat_cons` using `+` instead of `|||`. -/
theorem toNat_cons' {x : BitVec w} :
(cons a x).toNat = (a.toNat <<< w) + x.toNat := by
@@ -3072,21 +3064,6 @@ theorem getLsbD_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
have p2 : i - n 0 := by omega
simp [p1, p2, Nat.testBit_bool_to_nat]
theorem getElem_cons {b : Bool} {n} {x : BitVec n} {i : Nat} (h : i < n + 1) :
(cons b x)[i] = if h : i = n then b else x[i] := by
simp only [getElem_eq_testBit_toNat, toNat_cons, Nat.testBit_or, getLsbD]
rw [Nat.testBit_shiftLeft]
rcases Nat.lt_trichotomy i n with i_lt_n | i_eq_n | n_lt_i
· have p1 : ¬(n i) := by omega
have p2 : i n := by omega
simp [p1, p2]
· simp only [i_eq_n, ge_iff_le, Nat.le_refl, decide_true, Nat.sub_self, Nat.testBit_zero,
Bool.true_and, testBit_toNat, getLsbD_of_ge, Bool.or_false, reduceIte]
cases b <;> trivial
· have p1 : i n := by omega
have p2 : i - n 0 := by omega
simp [p1, p2, Nat.testBit_bool_to_nat]
@[simp] theorem msb_cons : (cons a x).msb = a := by
simp [cons, msb_cast, msb_append]
@@ -3106,15 +3083,6 @@ theorem setWidth_succ (x : BitVec w) :
have j_lt : j < i := Nat.lt_of_le_of_ne (Nat.le_of_succ_le_succ h) j_eq
simp [j_eq, j_lt]
@[simp] theorem cons_msb_setWidth (x : BitVec (w+1)) : (cons x.msb (x.setWidth w)) = x := by
ext i
simp only [getElem_cons]
split <;> rename_i h
· simp [BitVec.msb, getMsbD, h]
· by_cases h' : i < w
· simp_all
· omega
@[simp] theorem not_cons (x : BitVec w) (b : Bool) : ~~~(cons b x) = cons (!b) (~~~x) := by
simp [cons]
@@ -3341,6 +3309,17 @@ theorem toNat_add_of_not_uaddOverflow {x y : BitVec w} (h : ¬ uaddOverflow x y)
· simp only [uaddOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h
rw [toNat_add, Nat.mod_eq_of_lt h]
/--
Unsigned addition overflow reassociation.
If `(x + y)` and `(y + z)` do not overflow, then `(x + y) + z` overflows iff `x + (y + z)` overflows.
-/
theorem uaddOverflow_assoc {x y z : BitVec w} (h : ¬ x.uaddOverflow y) (h' : ¬ y.uaddOverflow z) :
(x + y).uaddOverflow z = x.uaddOverflow (y + z) := by
simp only [uaddOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h h'
simp only [uaddOverflow, toNat_add, ge_iff_le, decide_eq_decide]
repeat rw [Nat.mod_eq_of_lt (by omega)]
omega
protected theorem add_assoc (x y z : BitVec n) : x + y + z = x + (y + z) := by
apply eq_of_toNat_eq ; simp [Nat.add_assoc]
instance : Std.Associative (α := BitVec n) (· + ·) := BitVec.add_assoc
@@ -3379,6 +3358,20 @@ theorem toInt_add_of_not_saddOverflow {x y : BitVec w} (h : ¬ saddOverflow x y)
_root_.not_or, Int.not_le, Int.not_lt] at h
rw [toInt_add, Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
/--
Signed addition overflow reassociation.
If `(x + y)` and `(y + z)` do not overflow, then `(x + y) + z` overflows iff `x + (y + z)` overflows.
-/
theorem saddOverflow_assoc {x y z : BitVec w} (h : ¬ x.saddOverflow y) (h' : ¬ y.saddOverflow z) :
(x + y).saddOverflow z = x.saddOverflow (y + z) := by
rcases w with _|w
· simp [of_length_zero]
· simp only [saddOverflow, Nat.add_one_sub_one, ge_iff_le, Bool.or_eq_true, decide_eq_true_eq,
_root_.not_or, Int.not_le, Int.not_lt] at h h'
simp only [bool_to_prop, saddOverflow, toInt_add, ge_iff_le, Nat.add_one_sub_one]
repeat rw [Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
omega
@[simp]
theorem shiftLeft_add_distrib {x y : BitVec w} {n : Nat} :
(x + y) <<< n = x <<< n + y <<< n := by
@@ -3486,9 +3479,6 @@ theorem ofNat_sub_ofNat {n} (x y : Nat) : BitVec.ofNat n x - BitVec.ofNat n y =
· simp
· exact Nat.le_of_lt x.isLt
@[simp, bitvec_to_nat] theorem toNat_neg (x : BitVec n) : (- x).toNat = (2^n - x.toNat) % 2^n := by
simp [Neg.neg, BitVec.neg]
theorem toNat_neg_of_pos {x : BitVec n} (h : 0#n < x) :
(- x).toNat = 2^n - x.toNat := by
change 0 < x.toNat at h
@@ -3807,6 +3797,18 @@ theorem toNat_mul_of_not_umulOverflow {x y : BitVec w} (h : ¬ umulOverflow x y)
· simp only [umulOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h
rw [toNat_mul, Nat.mod_eq_of_lt h]
/--
Unsigned multiplication overflow reassociation.
If `(x * y)` and `(y * z)` do not overflow, then `(x * y) * z` overflows iff `x * (y * z)` overflows.
-/
theorem umulOverflow_assoc {x y z : BitVec w} (h : ¬ x.umulOverflow y) (h' : ¬ y.umulOverflow z) :
(x * y).umulOverflow z = x.umulOverflow (y * z) := by
simp only [umulOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h h'
simp only [umulOverflow, toNat_mul, ge_iff_le, decide_eq_decide]
repeat rw [Nat.mod_eq_of_lt (by omega)]
rw [Nat.mul_assoc]
@[simp]
theorem toInt_mul_of_not_smulOverflow {x y : BitVec w} (h : ¬ smulOverflow x y) :
(x * y).toInt = x.toInt * y.toInt := by
@@ -3816,6 +3818,20 @@ theorem toInt_mul_of_not_smulOverflow {x y : BitVec w} (h : ¬ smulOverflow x y)
_root_.not_or, Int.not_le, Int.not_lt] at h
rw [toInt_mul, Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
/--
Signed multiplication overflow reassociation.
If `(x * y)` and `(y * z)` do not overflow, then `(x * y) * z` overflows iff `x * (y * z)` overflows.
-/
theorem smulOverflow_assoc {x y z : BitVec w} (h : ¬ x.smulOverflow y) (h' : ¬ y.smulOverflow z) :
(x * y).smulOverflow z = x.smulOverflow (y * z) := by
rcases w with _|w
· simp [of_length_zero]
· simp only [smulOverflow, Nat.add_one_sub_one, ge_iff_le, Bool.or_eq_true, decide_eq_true_eq,
_root_.not_or, Int.not_le, Int.not_lt] at h h'
simp only [smulOverflow, toInt_mul, Nat.add_one_sub_one, ge_iff_le, bool_to_prop]
repeat rw [Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
rw [Int.mul_assoc]
theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
BitVec.ofInt n x * BitVec.ofInt n y := by
apply eq_of_toInt_eq
@@ -5108,9 +5124,6 @@ theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y)
/-! ### intMin -/
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
def intMin (w : Nat) := twoPow w (w - 1)
theorem getLsbD_intMin (w : Nat) : (intMin w).getLsbD i = decide (i + 1 = w) := by
simp only [intMin, getLsbD_twoPow, bool_to_prop]
omega
@@ -5261,9 +5274,6 @@ theorem neg_le_intMin_of_msb_eq_true {x : BitVec w} (hx : x.msb = true) : -x ≤
/-! ### intMax -/
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) := (twoPow w (w - 1)) - 1
@[simp, bitvec_to_nat]
theorem toNat_intMax : (intMax w).toNat = 2 ^ (w - 1) - 1 := by
simp only [intMax]
@@ -5615,68 +5625,54 @@ theorem msb_replicate {n w : Nat} {x : BitVec w} :
simp only [BitVec.msb, getMsbD_replicate, Nat.zero_mod]
cases n <;> cases w <;> simp
/-! ### Decidable quantifiers -/
theorem forall_zero_iff {P : BitVec 0 Prop} :
( v, P v) P 0#0 := by
constructor
· intro h
apply h
· intro h v
obtain (rfl : v = 0#0) := (by ext i )
apply h
/-! ### Inequalities (le / lt) -/
theorem forall_cons_iff {P : BitVec (n + 1) Prop} :
( v : BitVec (n + 1), P v) ( (x : Bool) (v : BitVec n), P (v.cons x)) := by
constructor
· intro h _ _
apply h
· intro h v
have w : v = (v.setWidth n).cons v.msb := by simp
rw [w]
apply h
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
simp [BitVec.ule, BitVec.ult, decide_not]
instance instDecidableForallBitVecZero (P : BitVec 0 Prop) :
[Decidable (P 0#0)], Decidable ( v, P v)
| .isTrue h => .isTrue fun v => by
obtain (rfl : v = 0#0) := (by ext i )
exact h
| .isFalse h => .isFalse (fun w => h (w _))
/-- If two bitvectors have the same `msb`, then signed and unsigned comparisons coincide -/
theorem slt_eq_ult_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) :
x.slt y = x.ult y := by
simp only [BitVec.slt, toInt_eq_msb_cond, BitVec.ult, decide_eq_decide, h]
cases y.msb <;> simp
instance instDecidableForallBitVecSucc (P : BitVec (n+1) Prop) [DecidablePred P]
[Decidable ( (x : Bool) (v : BitVec n), P (v.cons x))] : Decidable ( v, P v) :=
decidable_of_iff' ( x (v : BitVec n), P (v.cons x)) forall_cons_iff
/-- If two bitvectors have different `msb`s, then unsigned comparison is determined by this bit -/
theorem ult_eq_msb_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.ult y = y.msb := by
simp only [BitVec.ult, msb_eq_decide, ne_eq, decide_eq_decide] at *
omega
instance instDecidableExistsBitVecZero (P : BitVec 0 Prop) [Decidable (P 0#0)] :
Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
/-- If two bitvectors have different `msb`s, then signed and unsigned comparisons are opposites -/
theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb y.msb) :
x.slt y = !x.ult y := by
simp only [BitVec.slt, toInt_eq_msb_cond, Bool.eq_not_of_ne h, ult_eq_msb_of_msb_neq h]
cases y.msb <;> (simp [-Int.natCast_pow]; omega)
instance instDecidableExistsBitVecSucc (P : BitVec (n+1) Prop) [DecidablePred P]
[Decidable ( (x : Bool) (v : BitVec n), ¬ P (v.cons x))] : Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
theorem slt_eq_ult {x y : BitVec w} :
x.slt y = (x.msb != y.msb).xor (x.ult y) := by
by_cases h : x.msb = y.msb
· simp [h, slt_eq_ult_of_msb_eq]
· have h' : x.msb != y.msb := by simp_all
simp [slt_eq_not_ult_of_msb_neq h, h']
/--
For small numerals this isn't necessary (as typeclass search can use the above two instances),
but for large numerals this provides a shortcut.
Note, however, that for large numerals the decision procedure may be very slow,
and you should use `bv_decide` if possible.
-/
instance instDecidableForallBitVec :
(n : Nat) (P : BitVec n Prop) [DecidablePred P], Decidable ( v, P v)
| 0, _, _ => inferInstance
| n + 1, _, _ =>
have := instDecidableForallBitVec n
inferInstance
theorem sle_eq_not_slt {x y : BitVec w} : x.sle y = !y.slt x := by
simp only [BitVec.sle, BitVec.slt, decide_not, decide_eq_decide]; omega
/--
For small numerals this isn't necessary (as typeclass search can use the above two instances),
but for large numerals this provides a shortcut.
Note, however, that for large numerals the decision procedure may be very slow.
-/
instance instDecidableExistsBitVec :
(n : Nat) (P : BitVec n Prop) [DecidablePred P], Decidable ( v, P v)
| 0, _, _ => inferInstance
| _ + 1, _, _ => inferInstance
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x x.msb = false := by
simp [zero_sle_eq_not_msb]
theorem toNat_toInt_of_sle {w : Nat} {x : BitVec w} (hx : BitVec.sle 0#w x) : x.toInt.toNat = x.toNat :=
toNat_toInt_of_msb x (zero_sle_iff_msb_eq_false.1 hx)
theorem sle_eq_ule {x y : BitVec w} : x.sle y = (x.msb != y.msb ^^ x.ule y) := by
rw [sle_eq_not_slt, slt_eq_ult, Bool.xor_not, ule_eq_not_ult, bne_comm]
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]
/-! ### Deprecations -/

View File

@@ -183,10 +183,7 @@ theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) → α → m α) (x
| zero =>
rw [foldrM_loop_zero, foldrM_loop_succ, pure_bind]
conv => rhs; rw [bind_pure (f 0 x)]
congr
try -- TODO: block can be deleted after bootstrapping
funext
simp [foldrM_loop_zero]
rfl
| succ i ih =>
rw [foldrM_loop_succ, foldrM_loop_succ, bind_assoc]
congr; funext; exact ih ..

View File

@@ -1079,6 +1079,17 @@ theorem val_neg {n : Nat} [NeZero n] (x : Fin n) :
have := Fin.val_ne_zero_iff.mpr h
omega
protected theorem sub_eq_add_neg {n : Nat} (x y : Fin n) : x - y = x + -y := by
by_cases h : n = 0
· subst h
apply elim0 x
· replace h : NeZero n := h
ext
rw [Fin.coe_sub, Fin.val_add, val_neg]
split
· simp_all
· simp [Nat.add_comm]
/-! ### mul -/
theorem ofNat_mul [NeZero n] (x : Nat) (y : Fin n) :

View File

@@ -3,7 +3,6 @@ Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Mario Carneiro
-/
module
prelude
@@ -99,7 +98,7 @@ theorem ofNat_emod (m n : Nat) : (↑(m % n) : Int) = m % n := natCast_emod m n
theorem emod_add_ediv : a b : Int, a % b + b * (a / b) = a
| ofNat _, ofNat _ => congrArg ofNat <| Nat.mod_add_div ..
| ofNat m, -[n+1] => by
show (m % succ n + -(succ n) * -(m / succ n) : Int) = m
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
rw [Int.neg_mul_neg]; exact congrArg ofNat <| Nat.mod_add_div ..
| -[_+1], 0 => by rw [emod_zero]; rfl
| -[m+1], succ n => aux m n.succ
@@ -149,7 +148,7 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
fun {k n} => @fun
| ofNat _ => congrArg ofNat <| Nat.add_mul_div_right _ _ k.succ_pos
| -[m+1] => by
show ((n * k.succ : Nat) - m.succ : Int).ediv k.succ = n - (m / k.succ + 1 : Nat)
change ((n * k.succ : Nat) - m.succ : Int).ediv k.succ = n - (m / k.succ + 1 : Nat)
by_cases h : m < n * k.succ
· rw [ Int.ofNat_sub h, Int.ofNat_sub ((Nat.div_lt_iff_lt_mul k.succ_pos).2 h)]
apply congrArg ofNat
@@ -158,7 +157,7 @@ theorem add_mul_ediv_right (a b : Int) {c : Int} (H : c ≠ 0) : (a + b * c) / c
have H {a b : Nat} (h : a b) : (a : Int) + -((b : Int) + 1) = -[b - a +1] := by
rw [negSucc_eq, Int.ofNat_sub h]
simp only [Int.sub_eq_add_neg, Int.neg_add, Int.neg_neg, Int.add_left_comm, Int.add_assoc]
show ediv ((n * succ k) + -((m : Int) + 1)) (succ k) = n + -((m / succ k) + 1 : Int)
change ediv ((n * succ k) + -((m : Int) + 1)) (succ k) = n + -((m / succ k) + 1 : Int)
rw [H h, H ((Nat.le_div_iff_mul_le k.succ_pos).2 h)]
apply congrArg negSucc
rw [Nat.mul_comm, Nat.sub_mul_div_of_le]; rwa [Nat.mul_comm]

View File

@@ -3,7 +3,6 @@ Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Mario Carneiro, Kim Morrison, Markus Himmel
-/
module
prelude
@@ -203,6 +202,9 @@ theorem tdiv_eq_ediv_of_nonneg : ∀ {a b : Int}, 0 ≤ a → a.tdiv b = a / b
| succ _, succ _, _ => rfl
| succ _, -[_+1], _ => rfl
@[simp] theorem natCast_tdiv_eq_ediv {a : Nat} {b : Int} : (a : Int).tdiv b = a / b :=
tdiv_eq_ediv_of_nonneg (by simp)
theorem tdiv_eq_ediv {a b : Int} :
a.tdiv b = a / b + if 0 a b a then 0 else sign b := by
simp only [dvd_iff_emod_eq_zero]
@@ -329,17 +331,17 @@ theorem fdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.fdiv b = a / b := by
theorem tmod_add_tdiv : a b : Int, tmod a b + b * (a.tdiv b) = a
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
| ofNat m, -[n+1] => by
show (m % succ n + -(succ n) * -(m / succ n) : Int) = m
change (m % succ n + -(succ n) * -(m / succ n) : Int) = m
rw [Int.neg_mul_neg]; exact congrArg ofNat (Nat.mod_add_div ..)
| -[m+1], 0 => by
show -(((succ m) % 0) : Int) + 0 * -(succ m / 0) = -(succ m)
change -(((succ m) % 0) : Int) + 0 * -(succ m / 0) = -(succ m)
rw [Nat.mod_zero, Int.zero_mul, Int.add_zero]
| -[m+1], ofNat n => by
show -(((succ m) % n) : Int) + n * -(succ m / n) = -(succ m)
change -(((succ m) % n) : Int) + n * -(succ m / n) = -(succ m)
rw [Int.mul_neg, Int.neg_add]
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
| -[m+1], -[n+1] => by
show -((succ m % succ n) : Int) + -(succ n) * (succ m / succ n) = -(succ m)
change -((succ m % succ n) : Int) + -(succ n) * (succ m / succ n) = -(succ m)
rw [Int.neg_mul, Int.neg_add]
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
@@ -361,17 +363,17 @@ theorem fmod_add_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
show subNatNat (m % succ n) n + ((succ n * (m / succ n)) + n + 1) = (m + 1)
change subNatNat (m % succ n) n + ((succ n * (m / succ n)) + n + 1) = (m + 1)
rw [Int.add_comm _ n, Int.add_assoc, Int.add_assoc,
Int.subNatNat_eq_coe, Int.sub_add_cancel]
exact congrArg (ofNat · + 1) <| Nat.mod_add_div ..
| -[_+1], 0 => by rw [fmod_zero]; rfl
| -[m+1], succ n => by
show subNatNat .. - ((succ n * (m / succ n)) + (succ n)) = -(succ m)
change subNatNat .. - ((succ n * (m / succ n)) + (succ n)) = -(succ m)
rw [Int.subNatNat_eq_coe, Int.sub_sub, Int.neg_sub, Int.sub_sub, Int.sub_sub_self]
exact congrArg (-ofNat ·) <| Nat.succ_add .. Nat.mod_add_div .. rfl
| -[m+1], -[n+1] => by
show -((succ m % succ n) : Int) + -(succ n * (succ m / succ n)) = -(succ m)
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. -/
@@ -572,7 +574,7 @@ theorem neg_one_ediv (b : Int) : -1 / b = -b.sign :=
· refine Nat.le_trans ?_ (Nat.le_add_right _ _)
rw [ Nat.mul_div_mul_left _ _ m.succ_pos]
apply Nat.div_mul_le_self
· show m.succ * n.succ _
· change m.succ * n.succ _
rw [Nat.mul_left_comm]
apply Nat.mul_le_mul_left
apply (Nat.div_lt_iff_lt_mul k.succ_pos).1
@@ -2745,7 +2747,7 @@ theorem bmod_lt {x : Int} {m : Nat} (h : 0 < m) : bmod x m < (m + 1) / 2 := by
split
· assumption
· apply Int.lt_of_lt_of_le
· show _ < 0
· change _ < 0
have : x % m < m := emod_lt_of_pos x (natCast_pos.mpr h)
exact Int.sub_neg_of_lt this
· exact Int.le.intro_sub _ rfl

View File

@@ -339,7 +339,7 @@ protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
match m with
| 0 => rfl
| succ m =>
show ofNat (n - succ m) = subNatNat n (succ m)
change ofNat (n - succ m) = subNatNat n (succ m)
rw [subNatNat, Nat.sub_eq_zero_of_le h]
@[deprecated negSucc_eq (since := "2025-03-11")]

View File

@@ -1665,7 +1665,7 @@ theorem natCast_sub (x y : Nat)
(NatCast.natCast x : Int) + -1*NatCast.natCast y
else
(0 : Int) := by
show ((x - y) : Int) = if (y : Int) + (-1)*x 0 then x + (-1)*y else 0
change ((x - y) : Int) = if (y : Int) + (-1)*x 0 then (x : Int) + (-1)*y else 0
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
rw [Int.neg_mul, Int.sub_eq_add_neg, Int.one_mul]
split

View File

@@ -19,6 +19,13 @@ 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 zero_pow {n : Nat} (h : n 0) : (0 : Int) ^ n = 0 := by
match n, h with
| n + 1, _ => simp [Int.pow_succ]
protected theorem one_pow {n : Nat} : (1 : Int) ^ n = 1 := by
induction n with simp_all [Int.pow_succ]
protected theorem pow_pos {n : Int} {m : Nat} : 0 < n 0 < n ^ m := by
induction m with
| zero => simp

View File

@@ -9,6 +9,7 @@ prelude
import Init.SimpLemmas
import Init.Data.Nat.Basic
import Init.Data.List.Notation
import Init.Data.Nat.Div.Basic
@[expose] section
@@ -672,7 +673,7 @@ instance : Std.Associative (α := List α) (· ++ ·) := ⟨append_assoc⟩
theorem append_cons (as : List α) (b : α) (bs : List α) : as ++ b :: bs = as ++ [b] ++ bs := by
simp
@[simp] theorem concat_eq_append {as : List α} {a : α} : as.concat a = as ++ [a] := by
@[simp, grind =] theorem concat_eq_append {as : List α} {a : α} : as.concat a = as ++ [a] := by
induction as <;> simp [concat, *]
theorem reverseAux_eq_append {as bs : List α} : reverseAux as bs = reverseAux as [] ++ bs := by
@@ -1624,8 +1625,8 @@ def find? (p : α → Bool) : List α → Option α
| true => some a
| false => find? p as
@[simp] theorem find?_nil : ([] : List α).find? p = none := rfl
theorem find?_cons : (a::as).find? p = match p a with | true => some a | false => as.find? p :=
@[simp, grind =] theorem find?_nil : ([] : List α).find? p = none := rfl
@[grind =]theorem find?_cons : (a::as).find? p = match p a with | true => some a | false => as.find? p :=
rfl
/-! ### findSome? -/
@@ -1845,8 +1846,8 @@ def lookup [BEq α] : α → List (α × β) → Option β
| true => some b
| false => lookup a as
@[simp] theorem lookup_nil [BEq α] : ([] : List (α × β)).lookup a = none := rfl
theorem lookup_cons [BEq α] {k : α} :
@[simp, grind =] theorem lookup_nil [BEq α] : ([] : List (α × β)).lookup a = none := rfl
@[grind =] theorem lookup_cons [BEq α] {k : α} :
((k, b)::as).lookup a = match a == k with | true => some b | false => as.lookup a :=
rfl

View File

@@ -64,8 +64,8 @@ theorem length_eq_countP_add_countP (p : α → Bool) {l : List α} : length l =
· rfl
· simp [h]
@[grind =]
theorem countP_eq_length_filter {l : List α} : countP p l = length (filter p l) := by
@[grind _=_] -- This to quite aggressive, as it introduces `filter` based reasoning whenever we see `countP`.
theorem countP_eq_length_filter {l : List α} : countP p l = (filter p l).length := by
induction l with
| nil => rfl
| cons x l ih =>
@@ -82,7 +82,7 @@ theorem countP_le_length : countP p l ≤ l.length := by
simp only [countP_eq_length_filter]
apply length_filter_le
@[simp] theorem countP_append {l₁ l₂ : List α} : countP p (l₁ ++ l₂) = countP p l₁ + countP p l₂ := by
@[simp, grind =] theorem countP_append {l₁ l₂ : List α} : countP p (l₁ ++ l₂) = countP p l₁ + countP p l₂ := by
simp only [countP_eq_length_filter, filter_append, length_append]
@[simp] theorem countP_pos_iff {p} : 0 < countP p l a l, p a := by
@@ -120,10 +120,24 @@ theorem Sublist.countP_le (s : l₁ <+ l₂) : countP p l₁ ≤ countP p l₂ :
simp only [countP_eq_length_filter]
apply s.filter _ |>.length_le
grind_pattern Sublist.countP_le => l₁ <+ l₂, countP p l₁
grind_pattern Sublist.countP_le => l₁ <+ l₂, countP p l₂
theorem IsPrefix.countP_le (s : l₁ <+: l₂) : countP p l₁ countP p l₂ := s.sublist.countP_le
grind_pattern IsPrefix.countP_le => l₁ <+: l₂, countP p l₁
grind_pattern IsPrefix.countP_le => l₁ <+: l₂, countP p l₂
theorem IsSuffix.countP_le (s : l₁ <:+ l₂) : countP p l₁ countP p l₂ := s.sublist.countP_le
grind_pattern IsSuffix.countP_le => l₁ <:+ l₂, countP p l₁
grind_pattern IsSuffix.countP_le => l₁ <:+ l₂, countP p l₂
theorem IsInfix.countP_le (s : l₁ <:+: l₂) : countP p l₁ countP p l₂ := s.sublist.countP_le
grind_pattern IsInfix.countP_le => l₁ <:+: l₂, countP p l₁
grind_pattern IsInfix.countP_le => l₁ <:+: l₂, countP p l₂
-- See `Init.Data.List.Nat.Count` for `Sublist.le_countP : countP p l₂ - (l₂.length - l₁.length) ≤ countP p l₁`.
@[grind]
@@ -174,7 +188,7 @@ theorem countP_flatMap {p : β → Bool} {l : List α} {f : α → List β} :
countP p (l.flatMap f) = sum (map (countP p f) l) := by
rw [List.flatMap, countP_flatten, map_map]
@[simp] theorem countP_reverse {l : List α} : countP p l.reverse = countP p l := by
@[simp, grind =] theorem countP_reverse {l : List α} : countP p l.reverse = countP p l := by
simp [countP_eq_length_filter, filter_reverse]
theorem countP_mono_left (h : x l, p x q x) : countP p l countP q l := by
@@ -203,18 +217,22 @@ section count
variable [BEq α]
@[simp] theorem count_nil {a : α} : count a [] = 0 := rfl
@[simp, grind =] theorem count_nil {a : α} : count a [] = 0 := rfl
@[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]
@[grind =] theorem count_eq_countP {a : α} {l : List α} : count a l = countP (· == a) l := rfl
theorem count_eq_countP {a : α} {l : List α} : count a l = countP (· == a) l := rfl
theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
funext l
apply count_eq_countP
@[grind =]
theorem count_eq_length_filter {a : α} {l : List α} : count a l = (filter (· == a) l).length := by
simp [count, countP_eq_length_filter]
@[grind]
theorem count_tail : {l : List α} {a : α},
l.tail.count a = l.count a - if l.head? == some a then 1 else 0
@@ -223,12 +241,28 @@ theorem count_tail : ∀ {l : List α} {a : α},
theorem count_le_length {a : α} {l : List α} : count a l l.length := countP_le_length
grind_pattern count_le_length => count a l
theorem Sublist.count_le (a : α) (h : l₁ <+ l₂) : count a l₁ count a l₂ := h.countP_le
grind_pattern Sublist.count_le => l₁ <+ l₂, count a l₁
grind_pattern Sublist.count_le => l₁ <+ l₂, count a l₂
theorem IsPrefix.count_le (a : α) (h : l₁ <+: l₂) : count a l₁ count a l₂ := h.sublist.count_le a
grind_pattern IsPrefix.count_le => l₁ <+: l₂, count a l₁
grind_pattern IsPrefix.count_le => l₁ <+: l₂, count a l₂
theorem IsSuffix.count_le (a : α) (h : l₁ <:+ l₂) : count a l₁ count a l₂ := h.sublist.count_le a
grind_pattern IsSuffix.count_le => l₁ <:+ l₂, count a l₁
grind_pattern IsSuffix.count_le => l₁ <:+ l₂, count a l₂
theorem IsInfix.count_le (a : α) (h : l₁ <:+: l₂) : count a l₁ count a l₂ := h.sublist.count_le a
grind_pattern IsInfix.count_le => l₁ <:+: l₂, count a l₁
grind_pattern IsInfix.count_le => l₁ <:+: l₂, count a l₂
-- See `Init.Data.List.Nat.Count` for `Sublist.le_count : count a l₂ - (l₂.length - l₁.length) ≤ countP a l₁`.
theorem count_tail_le {a : α} {l : List α} : count a l.tail count a l :=
@@ -245,10 +279,11 @@ theorem count_singleton {a b : α} : count a [b] = if b == a then 1 else 0 := by
@[simp, grind =] theorem count_append {a : α} {l₁ l₂ : List α} : count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=
countP_append
@[grind =]
theorem count_flatten {a : α} {l : List (List α)} : count a l.flatten = (l.map (count a)).sum := by
simp only [count_eq_countP, countP_flatten, count_eq_countP']
@[simp] theorem count_reverse {a : α} {l : List α} : count a l.reverse = count a l := by
@[simp, grind =] theorem count_reverse {a : α} {l : List α} : count a l.reverse = count a l := by
simp only [count_eq_countP, countP_eq_length_filter, filter_reverse, length_reverse]
@[grind]

View File

@@ -23,9 +23,9 @@ open Nat
/-! ### eraseP -/
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
@[simp, grind =] theorem eraseP_nil : [].eraseP p = [] := rfl
theorem eraseP_cons {a : α} {l : List α} :
@[grind =] theorem eraseP_cons {a : α} {l : List α} :
(a :: l).eraseP p = bif p a then l else a :: l.eraseP p := rfl
@[simp] theorem eraseP_cons_of_pos {l : List α} {p} (h : p a) : (a :: l).eraseP p = l := by
@@ -92,7 +92,7 @@ theorem exists_or_eq_self_of_eraseP (p) (l : List α) :
let _, l₁, l₂, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [length_append, e₁]
theorem length_eraseP {l : List α} : (l.eraseP p).length = if l.any p then l.length - 1 else l.length := by
@[grind =] theorem length_eraseP {l : List α} : (l.eraseP p).length = if l.any p then l.length - 1 else l.length := by
split <;> rename_i h
· simp only [any_eq_true] at h
obtain x, m, h := h
@@ -106,8 +106,13 @@ theorem eraseP_sublist {l : List α} : l.eraseP p <+ l := by
| .inl h => rw [h]; apply Sublist.refl
| .inr c, l₁, l₂, _, _, h₃, h₄ => rw [h₄, h₃]; simp
grind_pattern eraseP_sublist => l.eraseP p, List.Sublist
theorem eraseP_subset {l : List α} : l.eraseP p l := eraseP_sublist.subset
grind_pattern eraseP_subset => l.eraseP p, List.Subset
@[grind ]
protected theorem Sublist.eraseP : l₁ <+ l₂ l₁.eraseP p <+ l₂.eraseP p
| .slnil => Sublist.refl _
| .cons a s => by
@@ -147,10 +152,12 @@ theorem mem_of_mem_eraseP {l : List α} : a ∈ l.eraseP p → a ∈ l := (erase
· intro; obtain x, m, h := h; simp_all
· simp_all
@[grind _=_]
theorem eraseP_map {f : β α} : {l : List β}, (map f l).eraseP p = map f (l.eraseP (p f))
| [] => rfl
| b::l => by by_cases h : p (f b) <;> simp [h, eraseP_map, eraseP_cons_of_pos]
@[grind =]
theorem eraseP_filterMap {f : α Option β} : {l : List α},
(filterMap f l).eraseP p = filterMap f (l.eraseP (fun x => match f x with | some y => p y | none => false))
| [] => rfl
@@ -165,6 +172,7 @@ theorem eraseP_filterMap {f : α → Option β} : ∀ {l : List α},
· simp only [w, cond_false]
rw [filterMap_cons_some h, eraseP_filterMap]
@[grind =]
theorem eraseP_filter {f : α Bool} {l : List α} :
(filter f l).eraseP p = filter f (l.eraseP (fun x => p x && f x)) := by
rw [ filterMap_eq_filter, eraseP_filterMap]
@@ -174,18 +182,19 @@ theorem eraseP_filter {f : α → Bool} {l : List α} :
split <;> split at * <;> simp_all
theorem eraseP_append_left {a : α} (pa : p a) :
{l₁ : List α} l₂, a l₁ (l₁++l₂).eraseP p = l₁.eraseP p ++ l₂
{l₁ : List α} l₂, a l₁ (l₁ ++ l₂).eraseP p = l₁.eraseP p ++ l₂
| x :: xs, l₂, h => by
by_cases h' : p x <;> simp [h']
rw [eraseP_append_left pa l₂ ((mem_cons.1 h).resolve_left (mt _ h'))]
intro | rfl => exact pa
theorem eraseP_append_right :
{l₁ : List α} l₂, ( b l₁, ¬p b) eraseP p (l₁++l₂) = l₁ ++ l₂.eraseP p
{l₁ : List α} l₂, ( b l₁, ¬p b) eraseP p (l₁ ++ l₂) = l₁ ++ l₂.eraseP p
| [], _, _ => rfl
| _ :: _, _, h => by
simp [(forall_mem_cons.1 h).1, eraseP_append_right _ (forall_mem_cons.1 h).2]
@[grind =]
theorem eraseP_append {l₁ l₂ : List α} :
(l₁ ++ l₂).eraseP p = if l₁.any p then l₁.eraseP p ++ l₂ else l₁ ++ l₂.eraseP p := by
split <;> rename_i h
@@ -196,6 +205,7 @@ theorem eraseP_append {l₁ l₂ : List α} :
rw [eraseP_append_right _]
simp_all
@[grind =]
theorem eraseP_replicate {n : Nat} {a : α} {p : α Bool} :
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
induction n with
@@ -212,6 +222,7 @@ theorem eraseP_replicate {n : Nat} {a : α} {p : α → Bool} :
(replicate n a).eraseP p = replicate n a := by
rw [eraseP_of_forall_not (by simp_all)]
@[grind ]
protected theorem IsPrefix.eraseP (h : l₁ <+: l₂) : l₁.eraseP p <+: l₂.eraseP p := by
rw [IsPrefix] at h
obtain t, rfl := h
@@ -258,13 +269,15 @@ 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 ]
theorem Nodup.eraseP (p) : Nodup l Nodup (l.eraseP p) :=
Pairwise.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
induction l with
@@ -357,6 +370,7 @@ theorem exists_erase_eq [LawfulBEq α] {a : α} {l : List α} (h : a ∈ l) :
length (l.erase a) = length l - 1 := by
rw [erase_eq_eraseP]; exact length_eraseP_of_mem h (beq_self_eq_true a)
@[grind =]
theorem length_erase [LawfulBEq α] {a : α} {l : List α} :
length (l.erase a) = if a l then length l - 1 else length l := by
rw [erase_eq_eraseP, length_eraseP]
@@ -365,11 +379,17 @@ theorem length_erase [LawfulBEq α] {a : α} {l : List α} :
theorem erase_sublist {a : α} {l : List α} : l.erase a <+ l :=
erase_eq_eraseP' a l eraseP_sublist ..
grind_pattern length_erase => l.erase a, List.Sublist
theorem erase_subset {a : α} {l : List α} : l.erase a l := erase_sublist.subset
grind_pattern erase_subset => l.erase a, List.Subset
@[grind ]
theorem Sublist.erase (a : α) {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₁.erase a <+ l₂.erase a := by
simp only [erase_eq_eraseP']; exact h.eraseP
@[grind ]
theorem IsPrefix.erase (a : α) {l₁ l₂ : List α} (h : l₁ <+: l₂) : l₁.erase a <+: l₂.erase a := by
simp only [erase_eq_eraseP']; exact h.eraseP
@@ -391,6 +411,7 @@ theorem mem_of_mem_erase {a b : α} {l : List α} (h : a ∈ l.erase b) : a ∈
rw [erase_eq_eraseP', eraseP_eq_self_iff]
simp [forall_mem_ne']
@[grind _=_]
theorem erase_filter [LawfulBEq α] {f : α Bool} {l : List α} :
(filter f l).erase a = filter f (l.erase a) := by
induction l with
@@ -418,10 +439,12 @@ theorem erase_append_right [LawfulBEq α] {a : α} {l₁ : List α} (l₂ : List
rw [erase_eq_eraseP, erase_eq_eraseP, eraseP_append_right]
intros b h' h''; rw [eq_of_beq h''] at h; exact h h'
@[grind =]
theorem erase_append [LawfulBEq α] {a : α} {l₁ l₂ : List α} :
(l₁ ++ l₂).erase a = if a l₁ then l₁.erase a ++ l₂ else l₁ ++ l₂.erase a := by
simp [erase_eq_eraseP, eraseP_append]
@[grind =]
theorem erase_replicate [LawfulBEq α] {n : Nat} {a b : α} :
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
rw [erase_eq_eraseP]
@@ -429,6 +452,7 @@ theorem erase_replicate [LawfulBEq α] {n : Nat} {a b : α} :
-- The arguments `a b` are explicit,
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
@[grind =]
theorem erase_comm [LawfulBEq α] (a b : α) {l : List α} :
(l.erase a).erase b = (l.erase b).erase a := by
if ab : a == b then rw [eq_of_beq ab] else ?_
@@ -468,6 +492,7 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {l : List α} :
rw [erase_of_not_mem]
simp_all
@[grind ]
theorem Pairwise.erase [LawfulBEq α] {l : List α} (a) : Pairwise p l Pairwise p (l.erase a) :=
Pairwise.sublist <| erase_sublist
@@ -520,6 +545,7 @@ end erase
/-! ### eraseIdx -/
@[grind =]
theorem length_eraseIdx {l : List α} {i : Nat} :
(l.eraseIdx i).length = if i < l.length then l.length - 1 else l.length := by
induction l generalizing i with
@@ -537,8 +563,9 @@ theorem length_eraseIdx_of_lt {l : List α} {i} (h : i < length l) :
(l.eraseIdx i).length = length l - 1 := by
simp [length_eraseIdx, h]
@[simp] theorem eraseIdx_zero {l : List α} : eraseIdx l 0 = l.tail := by cases l <;> rfl
@[simp, grind =] theorem eraseIdx_zero {l : List α} : eraseIdx l 0 = l.tail := by cases l <;> rfl
@[grind =]
theorem eraseIdx_eq_take_drop_succ :
(l : List α) (i : Nat), l.eraseIdx i = l.take i ++ l.drop (i + 1)
| nil, _ => by simp
@@ -565,6 +592,7 @@ theorem eraseIdx_ne_nil_iff {l : List α} {i : Nat} : eraseIdx l i ≠ [] ↔ 2
@[deprecated eraseIdx_ne_nil_iff (since := "2025-01-30")]
abbrev eraseIdx_ne_nil := @eraseIdx_ne_nil_iff
@[grind]
theorem eraseIdx_sublist : (l : List α) (k : Nat), eraseIdx l k <+ l
| [], _ => by simp
| a::l, 0 => by simp
@@ -573,6 +601,7 @@ theorem eraseIdx_sublist : ∀ (l : List α) (k : Nat), eraseIdx l 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
@@ -612,6 +641,15 @@ theorem eraseIdx_append_of_length_le {l : List α} {k : Nat} (hk : length l ≤
| zero => simp_all
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_sub_succ]
@[grind =]
theorem eraseIdx_append :
eraseIdx (l ++ l') k = if k < length l then eraseIdx l k ++ l' else l ++ eraseIdx l' (k - length l) := by
split <;> rename_i h
· simp [eraseIdx_append_of_lt_length h]
· rw [eraseIdx_append_of_length_le]
omega
@[grind =]
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} :
(replicate n a).eraseIdx k = if k < n then replicate (n - 1) a else replicate n a := by
split <;> rename_i h
@@ -623,12 +661,15 @@ 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 ]
theorem Nodup.eraseIdx {l : List α} (k) : Nodup l Nodup (l.eraseIdx k) :=
Pairwise.eraseIdx k
@[grind ]
protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
eraseIdx l k <+: eraseIdx l' k := by
rcases h with t, rfl

View File

@@ -23,14 +23,14 @@ Examples:
-/
def finRange (n : Nat) : List (Fin n) := ofFn fun i => i
@[simp] theorem length_finRange {n : Nat} : (List.finRange n).length = n := by
@[simp, grind =] theorem length_finRange {n : Nat} : (List.finRange n).length = n := by
simp [List.finRange]
@[simp] theorem getElem_finRange {i : Nat} (h : i < (List.finRange n).length) :
@[simp, grind =] theorem getElem_finRange {i : Nat} (h : i < (List.finRange n).length) :
(finRange n)[i] = Fin.cast length_finRange i, h := by
simp [List.finRange]
@[simp] theorem finRange_zero : finRange 0 = [] := by simp [finRange]
@[simp, grind =] theorem finRange_zero : finRange 0 = [] := by simp [finRange]
theorem finRange_succ {n} : finRange (n+1) = 0 :: (finRange n).map Fin.succ := by
apply List.ext_getElem; simp; intro i; cases i <;> simp
@@ -46,6 +46,7 @@ theorem finRange_succ_last {n} :
· rfl
· next h => exact Fin.eq_last_of_not_lt h
@[grind _=_]
theorem finRange_reverse {n} : (finRange n).reverse = (finRange n).map Fin.rev := by
induction n with
| zero => simp

View File

@@ -45,7 +45,7 @@ theorem exists_of_findSome?_eq_some {l : List α} {f : α → Option β} (w : l.
simp_all only [findSome?_cons, mem_cons, exists_eq_or_imp]
split at w <;> simp_all
@[simp] theorem findSome?_eq_none_iff : findSome? p l = none x l, p x = none := by
@[simp, grind =] theorem findSome?_eq_none_iff : findSome? p l = none x l, p x = none := by
induction l <;> simp [findSome?_cons]; split <;> simp [*]
@[simp] theorem findSome?_isSome_iff {f : α Option β} {l : List α} :
@@ -91,7 +91,7 @@ theorem findSome?_eq_some_iff {f : α → Option β} {l : List α} {b : β} :
obtain rfl, rfl, rfl := h₁
exact l₁, a, l₂, rfl, h₂, fun a' w => h₃ a' (mem_cons_of_mem p w)
@[simp] theorem findSome?_guard {l : List α} : findSome? (Option.guard fun x => p x) l = find? p l := by
@[simp, grind =] theorem findSome?_guard {l : List α} : findSome? (Option.guard p) l = find? p l := by
induction l with
| nil => simp
| cons x xs ih =>
@@ -103,32 +103,33 @@ theorem findSome?_eq_some_iff {f : α → Option β} {l : List α} {b : β} :
· simp only [Option.guard_eq_none_iff] at h
simp [ih, h]
theorem find?_eq_findSome?_guard {l : List α} : find? p l = findSome? (Option.guard fun x => p x) l :=
theorem find?_eq_findSome?_guard {l : List α} : find? p l = findSome? (Option.guard p) l :=
findSome?_guard.symm
@[simp] theorem head?_filterMap {f : α Option β} {l : List α} : (l.filterMap f).head? = l.findSome? f := by
@[simp, grind =] theorem head?_filterMap {f : α Option β} {l : List α} : (l.filterMap f).head? = l.findSome? f := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [filterMap_cons, findSome?_cons]
split <;> simp [*]
@[simp] theorem head_filterMap {f : α Option β} {l : List α} (h) :
@[simp, grind =] theorem head_filterMap {f : α Option β} {l : List α} (h) :
(l.filterMap f).head h = (l.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [head_eq_iff_head?_eq_some]
@[simp] theorem getLast?_filterMap {f : α Option β} {l : List α} : (l.filterMap f).getLast? = l.reverse.findSome? f := by
@[simp, grind =] theorem getLast?_filterMap {f : α Option β} {l : List α} : (l.filterMap f).getLast? = l.reverse.findSome? f := by
rw [getLast?_eq_head?_reverse]
simp [ filterMap_reverse]
@[simp] theorem getLast_filterMap {f : α Option β} {l : List α} (h) :
@[simp, grind =] theorem getLast_filterMap {f : α Option β} {l : List α} (h) :
(l.filterMap f).getLast h = (l.reverse.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [getLast_eq_iff_getLast?_eq_some]
@[simp] theorem map_findSome? {f : α Option β} {g : β γ} {l : List α} :
@[simp, grind _=_] theorem map_findSome? {f : α Option β} {g : β γ} {l : List α} :
(l.findSome? f).map g = l.findSome? (Option.map g f) := by
induction l <;> simp [findSome?_cons]; split <;> simp [*]
@[grind _=_]
theorem findSome?_map {f : β γ} {l : List β} : findSome? p (l.map f) = l.findSome? (p f) := by
induction l with
| nil => simp
@@ -136,15 +137,18 @@ theorem findSome?_map {f : β → γ} {l : List β} : findSome? p (l.map f) = l.
simp only [map_cons, findSome?]
split <;> simp_all
@[grind =]
theorem head_flatten {L : List (List α)} (h : l, l L l []) :
(flatten L).head (by simpa using h) = (L.findSome? fun l => l.head?).get (by simpa using h) := by
(flatten L).head (by simpa using h) = (L.findSome? head?).get (by simpa using h) := by
simp [head_eq_iff_head?_eq_some, head?_flatten]
@[grind =]
theorem getLast_flatten {L : List (List α)} (h : l, l L l []) :
(flatten L).getLast (by simpa using h) =
(L.reverse.findSome? fun l => l.getLast?).get (by simpa using h) := by
(L.reverse.findSome? getLast?).get (by simpa using h) := by
simp [getLast_eq_iff_getLast?_eq_some, getLast?_flatten]
@[grind =]
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
cases n with
| zero => simp
@@ -174,6 +178,9 @@ theorem Sublist.findSome?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
· simp_all
· exact ih
grind_pattern Sublist.findSome?_isSome => l₁ <+ l₂, l₁.findSome? f
grind_pattern Sublist.findSome?_isSome => l₁ <+ l₂, l₂.findSome? f
theorem Sublist.findSome?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
l₂.findSome? f = none l₁.findSome? f = none := by
simp only [List.findSome?_eq_none_iff, Bool.not_eq_true]
@@ -185,16 +192,30 @@ theorem IsPrefix.findSome?_eq_some {l₁ l₂ : List α} {f : α → Option β}
obtain t, rfl := h
simp +contextual [findSome?_append]
grind_pattern IsPrefix.findSome?_eq_some => l₁ <+: l₂, l₁.findSome? f, some b
grind_pattern IsPrefix.findSome?_eq_some => l₁ <+: l₂, l₂.findSome? f, some b
theorem IsPrefix.findSome?_eq_none {l₁ l₂ : List α} {f : α Option β} (h : l₁ <+: l₂) :
List.findSome? f l₂ = none List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
grind_pattern IsPrefix.findSome?_eq_none => l₁ <+: l₂, l₂.findSome? f
grind_pattern IsPrefix.findSome?_eq_none => l₁ <+: l₂, l₁.findSome? f
theorem IsSuffix.findSome?_eq_none {l₁ l₂ : List α} {f : α Option β} (h : l₁ <:+ l₂) :
List.findSome? f l₂ = none List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
grind_pattern IsSuffix.findSome?_eq_none => l₁ <+: l₂, l₂.findSome? f
grind_pattern IsSuffix.findSome?_eq_none => l₁ <+: l₂, l₁.findSome? f
theorem IsInfix.findSome?_eq_none {l₁ l₂ : List α} {f : α Option β} (h : l₁ <:+: l₂) :
List.findSome? f l₂ = none List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
grind_pattern IsInfix.findSome?_eq_none => l₁ <+: l₂, l₂.findSome? f
grind_pattern IsInfix.findSome?_eq_none => l₁ <+: l₂, l₁.findSome? f
/-! ### find? -/
@[simp] theorem find?_cons_of_pos {l} (h : p a) : find? p (a :: l) = some a := by
@@ -203,7 +224,7 @@ theorem IsInfix.findSome?_eq_none {l₁ l₂ : List α} {f : α → Option β} (
@[simp] theorem find?_cons_of_neg {l} (h : ¬p a) : find? p (a :: l) = find? p l := by
simp [find?, h]
@[simp] theorem find?_eq_none : find? p l = none x l, ¬ p x := by
@[simp, grind =] theorem find?_eq_none : find? p l = none x l, ¬ p x := by
induction l <;> simp [find?_cons]; split <;> simp [*]
theorem find?_eq_some_iff_append :
@@ -248,25 +269,28 @@ theorem find?_cons_eq_some : (a :: xs).find? p = some b ↔ (p a ∧ a = b)
rw [find?_cons]
split <;> simp_all
@[simp] theorem find?_isSome {xs : List α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
@[simp, grind =] theorem find?_isSome {xs : List α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, mem_cons, exists_eq_or_imp]
split <;> simp_all
@[grind ]
theorem find?_some : {l}, find? p l = some a p a
| b :: l, H => by
by_cases h : p b <;> simp [find?, h] at H
· exact H h
· exact find?_some H
@[grind ]
theorem mem_of_find?_eq_some : {l}, find? p l = some a a l
| b :: l, H => by
by_cases h : p b <;> simp [find?, h] at H
· 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
@@ -278,7 +302,7 @@ theorem get_find?_mem {xs : List α} {p : α → Bool} (h) : (xs.find? p).get h
right
apply ih
@[simp] theorem find?_filter {xs : List α} {p : α Bool} {q : α Bool} :
@[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
| nil => simp
@@ -288,22 +312,22 @@ theorem get_find?_mem {xs : List α} {p : α → Bool} (h) : (xs.find? p).get h
· simp only [find?_cons]
split <;> simp_all
@[simp] theorem head?_filter {p : α Bool} {l : List α} : (l.filter p).head? = l.find? p := by
@[simp, grind =] theorem head?_filter {p : α Bool} {l : List α} : (l.filter p).head? = l.find? p := by
rw [ filterMap_eq_filter, head?_filterMap, findSome?_guard]
@[simp] theorem head_filter {p : α Bool} {l : List α} (h) :
@[simp, grind =] theorem head_filter {p : α Bool} {l : List α} (h) :
(l.filter p).head h = (l.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [head_eq_iff_head?_eq_some]
@[simp] theorem getLast?_filter {p : α Bool} {l : List α} : (l.filter p).getLast? = l.reverse.find? p := by
@[simp, grind =] theorem getLast?_filter {p : α Bool} {l : List α} : (l.filter p).getLast? = l.reverse.find? p := by
rw [getLast?_eq_head?_reverse]
simp [ filter_reverse]
@[simp] theorem getLast_filter {p : α Bool} {l : List α} (h) :
@[simp, grind =] theorem getLast_filter {p : α Bool} {l : List α} (h) :
(l.filter p).getLast h = (l.reverse.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [getLast_eq_iff_getLast?_eq_some]
@[simp] theorem find?_filterMap {xs : List α} {f : α Option β} {p : β Bool} :
@[simp, grind =] theorem find?_filterMap {xs : List α} {f : α Option β} {p : β Bool} :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
induction xs with
| nil => simp
@@ -313,15 +337,15 @@ theorem get_find?_mem {xs : List α} {p : α → Bool} (h) : (xs.find? p).get h
· simp only [find?_cons]
split <;> simp_all
@[simp] theorem find?_map {f : β α} {l : List β} : find? p (l.map f) = (l.find? (p f)).map f := by
@[simp, grind =] theorem find?_map {f : β α} {l : List β} : find? p (l.map f) = (l.find? (p f)).map f := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, find?]
by_cases h : p (f x) <;> simp [h, ih]
@[simp] theorem find?_flatten {xss : List (List α)} {p : α Bool} :
xss.flatten.find? p = xss.findSome? (·.find? p) := by
@[simp, grind _=_] theorem find?_flatten {xss : List (List α)} {p : α Bool} :
xss.flatten.find? p = xss.findSome? (find? p) := by
induction xss with
| nil => simp
| cons _ _ ih =>
@@ -378,7 +402,7 @@ theorem find?_flatten_eq_some_iff {xs : List (List α)} {p : α → Bool} {a :
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
@[simp] theorem find?_flatMap {xs : List α} {f : α List β} {p : β Bool} :
@[simp, grind =] theorem find?_flatMap {xs : List α} {f : α List β} {p : β Bool} :
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
simp [flatMap_def, findSome?_map]; rfl
@@ -386,6 +410,7 @@ theorem find?_flatMap_eq_none_iff {xs : List α} {f : α → List β} {p : β
(xs.flatMap f).find? p = none x xs, y f x, !p y := by
simp
@[grind =]
theorem find?_replicate : find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
cases n
· simp
@@ -430,6 +455,9 @@ theorem Sublist.find?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.fi
· simp
· simpa using ih
grind_pattern Sublist.find?_isSome => l₁ <+ l₂, l₁.find? p
grind_pattern Sublist.find?_isSome => l₁ <+ l₂, l₂.find? p
theorem Sublist.find?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₂.find? p = none l₁.find? p = none := by
simp only [List.find?_eq_none, Bool.not_eq_true]
exact fun w x m => w x (Sublist.mem m h)
@@ -440,16 +468,31 @@ theorem IsPrefix.find?_eq_some {l₁ l₂ : List α} {p : α → Bool} (h : l₁
obtain t, rfl := h
simp +contextual [find?_append]
grind_pattern IsPrefix.find?_eq_some => l₁ <+: l₂, l₁.find? p, some b
grind_pattern IsPrefix.find?_eq_some => l₁ <+: l₂, l₂.find? p, some b
theorem IsPrefix.find?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.find? p l₂ = none List.find? p l₁ = none :=
h.sublist.find?_eq_none
grind_pattern Sublist.find?_eq_none => l₁ <+ l₂, l₂.find? p
grind_pattern Sublist.find?_eq_none => l₁ <+ l₂, l₁.find? p
theorem IsSuffix.find?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+ l₂) :
List.find? p l₂ = none List.find? p l₁ = none :=
h.sublist.find?_eq_none
grind_pattern IsPrefix.find?_eq_none => l₁ <+: l₂, l₂.find? p
grind_pattern IsPrefix.find?_eq_none => l₁ <+: l₂, l₁.find? p
theorem IsInfix.find?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+: l₂) :
List.find? p l₂ = none List.find? p l₁ = none :=
h.sublist.find?_eq_none
grind_pattern IsSuffix.find?_eq_none => l₁ <:+ l₂, l₂.find? p
grind_pattern IsSuffix.find?_eq_none => l₁ <:+ l₂, l₁.find? p
@[grind =]
theorem find?_pmap {P : α Prop} {f : (a : α) P a β} {xs : List α}
(H : (a : α), a xs P a) {p : β Bool} :
(xs.pmap f H).find? p = (xs.attach.find? (fun a, m => p (f a (H a m)))).map fun a, m => f a (H a m) := by
@@ -482,9 +525,9 @@ private theorem findIdx?_go_eq {p : α → Bool} {xs : List α} {i : Nat} :
ext
simp only [Nat.add_comm i, Function.comp_apply, Nat.add_assoc]
@[simp] theorem findIdx?_nil : ([] : List α).findIdx? p = none := rfl
@[simp, grind =] theorem findIdx?_nil : ([] : List α).findIdx? p = none := rfl
theorem findIdx?_cons :
@[grind =] theorem findIdx?_cons :
(x :: xs).findIdx? p = if p x then some 0 else (xs.findIdx? p).map fun i => i + 1 := by
simp [findIdx?, findIdx?_go_eq]
@@ -493,6 +536,7 @@ theorem findIdx?_cons :
/-! ### findIdx -/
@[grind =]
theorem findIdx_cons {p : α Bool} {b : α} {l : List α} :
(b :: l).findIdx p = bif p b then 0 else (l.findIdx p) + 1 := by
cases H : p b with
@@ -511,6 +555,7 @@ 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
@@ -520,6 +565,8 @@ theorem findIdx_getElem {xs : List α} {w : xs.findIdx p < xs.length} :
p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
grind_pattern findIdx_getElem => xs[xs.findIdx p]
theorem findIdx_lt_length_of_exists {xs : List α} (h : x xs, p x) :
xs.findIdx p < xs.length := by
induction xs with
@@ -558,6 +605,8 @@ theorem findIdx_le_length {p : α → Bool} {xs : List α} : xs.findIdx p ≤ xs
· simp at e
exact Nat.le_of_eq (findIdx_eq_length.mpr e)
grind_pattern findIdx_le_length => xs.findIdx p, xs.length
@[simp]
theorem findIdx_lt_length {p : α Bool} {xs : List α} :
xs.findIdx p < xs.length x xs, p x := by
@@ -567,6 +616,8 @@ theorem findIdx_lt_length {p : α → Bool} {xs : List α} :
rw [ this, findIdx_eq_length, not_exists]
simp only [Bool.not_eq_true, not_and]
grind_pattern findIdx_lt_length => xs.findIdx p, xs.length
/-- `p` does not hold for elements with indices less than `xs.findIdx p`. -/
theorem not_of_lt_findIdx {p : α Bool} {xs : List α} {i : Nat} (h : i < xs.findIdx p) :
p (xs[i]'(Nat.le_trans h findIdx_le_length)) = false := by
@@ -591,6 +642,8 @@ theorem not_of_lt_findIdx {p : α → Bool} {xs : List α} {i : Nat} (h : i < xs
rw [ ipm, Nat.succ_lt_succ_iff] at h
simpa using ih h
grind_pattern not_of_lt_findIdx => xs.findIdx p, xs[i]
/-- If `¬ p xs[j]` for all `j < i`, then `i ≤ xs.findIdx p`. -/
theorem le_findIdx_of_not {p : α Bool} {xs : List α} {i : Nat} (h : i < xs.length)
(h2 : j (hji : j < i), p (xs[j]'(Nat.lt_trans hji h)) = false) : i xs.findIdx p := by
@@ -618,6 +671,7 @@ theorem findIdx_eq {p : α → Bool} {xs : List α} {i : Nat} (h : i < xs.length
simp at h3
simp_all [not_of_lt_findIdx h3]
@[grind =]
theorem findIdx_append {p : α Bool} {l₁ l₂ : List α} :
(l₁ ++ l₂).findIdx p =
if l₁.findIdx p < l₁.length then l₁.findIdx p else l₂.findIdx p + l₁.length := by
@@ -639,6 +693,9 @@ theorem IsPrefix.findIdx_le {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+
· exact Nat.le_refl ..
· simp_all [findIdx_eq_length_of_false]
grind_pattern IsPrefix.findIdx_le => l₁ <:+ l₂, l₁.findIdx p
grind_pattern IsPrefix.findIdx_le => l₁ <:+ l₂, l₂.findIdx p
theorem IsPrefix.findIdx_eq_of_findIdx_lt_length {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂)
(lt : l₁.findIdx p < l₁.length) : l₂.findIdx p = l₁.findIdx p := by
rw [IsPrefix] at h
@@ -648,6 +705,8 @@ theorem IsPrefix.findIdx_eq_of_findIdx_lt_length {l₁ l₂ : List α} {p : α
· rfl
· simp_all
grind_pattern IsPrefix.findIdx_eq_of_findIdx_lt_length => l₁ <:+ l₂, l₁.findIdx p, l₂.findIdx p
theorem findIdx_le_findIdx {l : List α} {p q : α Bool} (h : x l, p x q x) : l.findIdx q l.findIdx p := by
induction l with
| nil => simp
@@ -671,7 +730,7 @@ theorem findIdx_le_findIdx {l : List α} {p q : α → Bool} (h : ∀ x ∈ l, p
/-! ### findIdx? -/
@[simp]
@[simp, grind =]
theorem findIdx?_eq_none_iff {xs : List α} {p : α Bool} :
xs.findIdx? p = none x, x xs p x = false := by
induction xs with
@@ -680,7 +739,7 @@ theorem findIdx?_eq_none_iff {xs : List α} {p : α → Bool} :
simp only [findIdx?_cons]
split <;> simp_all [cond_eq_if]
@[simp]
@[simp, grind =]
theorem findIdx?_isSome {xs : List α} {p : α Bool} :
(xs.findIdx? p).isSome = xs.any p := by
induction xs with
@@ -689,7 +748,7 @@ theorem findIdx?_isSome {xs : List α} {p : α → Bool} :
simp only [findIdx?_cons]
split <;> simp_all
@[simp]
@[simp, grind =]
theorem findIdx?_isNone {xs : List α} {p : α Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
induction xs with
@@ -795,14 +854,14 @@ theorem of_findIdx?_eq_none {xs : List α} {p : α → Bool} (w : xs.findIdx? p
@[deprecated of_findIdx?_eq_none (since := "2025-02-02")]
abbrev findIdx?_of_eq_none := @of_findIdx?_eq_none
@[simp] theorem findIdx?_map {f : β α} {l : List β} : findIdx? p (l.map f) = l.findIdx? (p f) := by
@[simp, grind _=_] theorem findIdx?_map {f : β α} {l : List β} : findIdx? p (l.map f) = l.findIdx? (p f) := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, findIdx?_cons]
split <;> simp_all
@[simp] theorem findIdx?_append :
@[simp, grind =] theorem findIdx?_append :
(xs ++ ys : List α).findIdx? p =
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.length) := by
induction xs with simp [findIdx?_cons]
@@ -824,7 +883,7 @@ theorem findIdx?_flatten {l : List (List α)} {p : α → Bool} :
· rw [Option.or_of_isNone (by simp_all [findIdx?_isNone])]
simp [Function.comp_def, Nat.add_comm, Nat.add_assoc]
@[simp] theorem findIdx?_replicate :
@[simp, grind =] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by
cases n with
| zero => simp
@@ -878,22 +937,38 @@ theorem Sublist.findIdx?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
simp only [findIdx?_eq_none_iff]
exact fun w x m => w x (h.mem m)
grind_pattern Sublist.findIdx?_eq_none => l₁ <+ l₂, l₁.findIdx? p
grind_pattern Sublist.findIdx?_eq_none => l₁ <+ l₂, l₂.findIdx? p
theorem IsPrefix.findIdx?_eq_some {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.findIdx? p l₁ = some i List.findIdx? p l₂ = some i := by
rw [IsPrefix] at h
obtain t, rfl := h
intro h
simp [findIdx?_append, h]
theorem IsPrefix.findIdx?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.findIdx? p l₂ = none List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
grind_pattern IsPrefix.findIdx?_eq_none => l₁ <+: l₂, l₁.findIdx? p
grind_pattern IsPrefix.findIdx?_eq_none => l₁ <+: l₂, l₂.findIdx? p
theorem IsSuffix.findIdx?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+ l₂) :
List.findIdx? p l₂ = none List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
grind_pattern IsSuffix.findIdx?_eq_none => l₁ <:+ l₂, l₁.findIdx? p
grind_pattern IsSuffix.findIdx?_eq_none => l₁ <:+ l₂, l₂.findIdx? p
theorem IsInfix.findIdx?_eq_none {l₁ l₂ : List α} {p : α Bool} (h : l₁ <:+: l₂) :
List.findIdx? p l₂ = none List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
grind_pattern IsInfix.findIdx?_eq_none => l₁ <:+: l₂, l₁.findIdx? p
grind_pattern IsInfix.findIdx?_eq_none => l₁ <:+: l₂, l₂.findIdx? p
@[grind =]
theorem findIdx_eq_getD_findIdx? {xs : List α} {p : α Bool} :
xs.findIdx p = (xs.findIdx? p).getD xs.length := by
induction xs with
@@ -914,7 +989,7 @@ theorem findIdx_eq_getD_findIdx? {xs : List α} {p : α → Bool} :
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_nil {p : α Bool} : findFinIdx? p [] = none := rfl
@[simp, grind =] theorem findFinIdx?_nil {p : α Bool} : findFinIdx? p [] = none := rfl
theorem findIdx?_go_eq_map_findFinIdx?_go_val {xs : List α} {p : α Bool} {i : Nat} {h} :
List.findIdx?.go p xs i =
@@ -940,6 +1015,7 @@ theorem findFinIdx?_eq_pmap_findIdx? {xs : List α} {p : α → Bool} :
(fun i h => h) := by
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
@[grind =]
theorem findFinIdx?_cons {p : α Bool} {x : α} {xs : List α} :
findFinIdx? p (x :: xs) = if p x then some 0 else (findFinIdx? p xs).map Fin.succ := by
rw [ Option.map_inj_right (f := Fin.val) (fun a b => Fin.eq_of_val_eq)]
@@ -950,6 +1026,7 @@ theorem findFinIdx?_cons {p : α → Bool} {x : α} {xs : List α} :
· rw [findIdx?_eq_map_findFinIdx?_val]
simp [Function.comp_def]
@[grind =]
theorem findFinIdx?_append {xs ys : List α} {p : α Bool} :
(xs ++ ys).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or
@@ -959,11 +1036,11 @@ theorem findFinIdx?_append {xs ys : List α} {p : α → Bool} :
· simp [h, Option.pmap_map, Option.map_pmap, Nat.add_comm]
· simp [h]
@[simp] theorem findFinIdx?_singleton {a : α} {p : α Bool} :
@[simp, grind =] theorem findFinIdx?_singleton {a : α} {p : α Bool} :
[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp [findFinIdx?_cons, findFinIdx?_nil]
@[simp] theorem findFinIdx?_eq_none_iff {l : List α} {p : α Bool} :
@[simp, grind =] theorem findFinIdx?_eq_none_iff {l : List α} {p : α Bool} :
l.findFinIdx? p = none x l, ¬ p x := by
simp [findFinIdx?_eq_pmap_findIdx?]
@@ -979,7 +1056,7 @@ theorem findFinIdx?_eq_some_iff {xs : List α} {p : α → Bool} {i : Fin xs.len
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[simp]
@[simp, grind =]
theorem isSome_findFinIdx? {l : List α} {p : α Bool} :
(l.findFinIdx? p).isSome = l.any p := by
induction l with
@@ -988,7 +1065,7 @@ theorem isSome_findFinIdx? {l : List α} {p : α → Bool} :
simp only [findFinIdx?_cons]
split <;> simp_all
@[simp]
@[simp, grind =]
theorem isNone_findFinIdx? {l : List α} {p : α Bool} :
(l.findFinIdx? p).isNone = l.all (fun x => ¬ p x) := by
induction l with
@@ -1013,6 +1090,7 @@ The verification API for `idxOf` is still incomplete.
The lemmas below should be made consistent with those for `findIdx` (and proved using them).
-/
@[grind =]
theorem idxOf_cons [BEq α] :
(x :: xs : List α).idxOf y = bif x == y then 0 else xs.idxOf y + 1 := by
dsimp [idxOf]
@@ -1027,6 +1105,7 @@ abbrev indexOf_cons := @idxOf_cons
@[deprecated idxOf_cons_self (since := "2025-01-29")]
abbrev indexOf_cons_self := @idxOf_cons_self
@[grind =]
theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
(l₁ ++ l₂).idxOf a = if a l₁ then l₁.idxOf a else l₂.idxOf a + l₁.length := by
rw [idxOf, findIdx_append]
@@ -1050,7 +1129,7 @@ theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.
@[deprecated idxOf_eq_length (since := "2025-01-29")]
abbrev indexOf_eq_length := @idxOf_eq_length
theorem idxOf_lt_length [BEq α] [EquivBEq α] {l : List α} (h : a l) : l.idxOf a < l.length := by
theorem idxOf_lt_length_of_mem [BEq α] [EquivBEq α] {l : List α} (h : a l) : l.idxOf a < l.length := by
induction l with
| nil => simp at h
| cons x xs ih =>
@@ -1063,8 +1142,23 @@ theorem idxOf_lt_length [BEq α] [EquivBEq α] {l : List α} (h : a ∈ l) : l.i
· exact zero_lt_succ xs.length
· exact Nat.add_lt_add_right ih 1
@[deprecated idxOf_lt_length (since := "2025-01-29")]
abbrev indexOf_lt_length := @idxOf_lt_length
theorem idxOf_le_length [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.idxOf a l.length := by
simpa [idxOf] using findIdx_le_length
grind_pattern idxOf_le_length => l.idxOf a, l.length
theorem idxOf_lt_length_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.idxOf a < l.length a l := by
constructor
· intro h
simpa [idxOf] using h
· exact idxOf_lt_length_of_mem
grind_pattern idxOf_lt_length_iff => l.idxOf a, l.length
@[deprecated idxOf_lt_length_of_mem (since := "2025-01-29")]
abbrev indexOf_lt_length := @idxOf_lt_length_of_mem
/-! ### finIdxOf?
@@ -1076,14 +1170,14 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
@[simp] theorem finIdxOf?_nil [BEq α] : ([] : List α).finIdxOf? a = none := rfl
@[simp, grind =] theorem finIdxOf?_nil [BEq α] : ([] : List α).finIdxOf? a = none := rfl
theorem finIdxOf?_cons [BEq α] {a : α} {xs : List α} :
@[grind =] theorem finIdxOf?_cons [BEq α] {a : α} {xs : List α} :
(a :: xs).finIdxOf? b =
if a == b then some 0, by simp else (xs.finIdxOf? b).map (·.succ) := by
simp [finIdxOf?, findFinIdx?_cons]
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
@[simp, grind =] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.finIdxOf? a = none a l := by
simp only [finIdxOf?, findFinIdx?_eq_none_iff, beq_iff_eq]
constructor
@@ -1096,18 +1190,19 @@ theorem finIdxOf?_cons [BEq α] {a : α} {xs : List α} :
l.finIdxOf? a = some i l[i] = a j (_ : j < i), ¬l[j] = a := by
simp only [finIdxOf?, findFinIdx?_eq_some_iff, beq_iff_eq]
@[simp]
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isSome a l := by
@[simp, grind =]
theorem isSome_finIdxOf? [BEq α] [PartialEquivBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isSome = l.contains a := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [finIdxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
split <;> simp_all [BEq.comm]
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isNone = ¬ a l := by
simp
@[simp]
theorem isNone_finIdxOf? [BEq α] [PartialEquivBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isNone = !l.contains a := by
rw [ isSome_finIdxOf?, Option.not_isSome]
/-! ### idxOf?
@@ -1115,16 +1210,16 @@ The verification API for `idxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
-/
@[simp] theorem idxOf?_nil [BEq α] : ([] : List α).idxOf? a = none := rfl
@[simp, grind =] theorem idxOf?_nil [BEq α] : ([] : List α).idxOf? a = none := rfl
theorem idxOf?_cons [BEq α] {a : α} {xs : List α} {b : α} :
@[grind =] theorem idxOf?_cons [BEq α] {a : α} {xs : List α} {b : α} :
(a :: xs).idxOf? b = if a == b then some 0 else (xs.idxOf? b).map (· + 1) := by
simp [idxOf?, findIdx?_cons]
@[simp] theorem idxOf?_singleton [BEq α] {a b : α} : [a].idxOf? b = if a == b then some 0 else none := by
simp [idxOf?_cons, idxOf?_nil]
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
@[simp, grind =] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.idxOf? a = none a l := by
simp only [idxOf?, findIdx?_eq_none_iff, beq_eq_false_iff_ne, ne_eq]
constructor
@@ -1137,7 +1232,7 @@ theorem idxOf?_cons [BEq α] {a : α} {xs : List α} {b : α} :
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
@[simp]
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isSome a l := by
induction l with
@@ -1146,6 +1241,7 @@ theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
simp only [idxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
@[grind =]
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isNone = ¬ a l := by
simp
@@ -1172,7 +1268,7 @@ theorem lookup_eq_findSome? {l : List (α × β)} {k : α} :
simp only [lookup_cons, findSome?_cons]
split <;> simp_all
@[simp] theorem lookup_eq_none_iff {l : List (α × β)} {k : α} :
@[simp, grind =] theorem lookup_eq_none_iff {l : List (α × β)} {k : α} :
l.lookup k = none p l, k != p.1 := by
simp [lookup_eq_findSome?]
@@ -1192,10 +1288,12 @@ theorem lookup_eq_some_iff {l : List (α × β)} {k : α} {b : β} :
· rintro l₁, l₂, rfl, h
exact l₁, (k, b), l₂, rfl, by simp, by simpa using h
@[grind =]
theorem lookup_append {l₁ l₂ : List (α × β)} {k : α} :
(l₁ ++ l₂).lookup k = (l₁.lookup k).or (l₂.lookup k) := by
simp [lookup_eq_findSome?, findSome?_append]
@[grind =]
theorem lookup_replicate {k : α} :
(replicate n (a,b)).lookup k = if n = 0 then none else if k == a then some b else none := by
induction n with
@@ -1230,6 +1328,9 @@ theorem Sublist.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <+ l₂)
simp only [lookup_eq_findSome?]
exact h.findSome?_eq_none
grind_pattern Sublist.lookup_isSome => l₁ <+ l₂, l₁.lookup k
grind_pattern Sublist.lookup_isSome => l₁ <+ l₂, l₂.lookup k
theorem IsPrefix.lookup_eq_some {l₁ l₂ : List (α × β)} (h : l₁ <+: l₂) :
List.lookup k l₁ = some b List.lookup k l₂ = some b := by
simp only [lookup_eq_findSome?]
@@ -1238,13 +1339,24 @@ theorem IsPrefix.lookup_eq_some {l₁ l₂ : List (α × β)} (h : l₁ <+: l₂
theorem IsPrefix.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <+: l₂) :
List.lookup k l₂ = none List.lookup k l₁ = none :=
h.sublist.lookup_eq_none
grind_pattern IsPrefix.lookup_eq_none => l₁ <+: l₂, l₁.lookup k
grind_pattern IsPrefix.lookup_eq_none => l₁ <+: l₂, l₂.lookup k
theorem IsSuffix.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <:+ l₂) :
List.lookup k l₂ = none List.lookup k l₁ = none :=
h.sublist.lookup_eq_none
grind_pattern IsSuffix.lookup_eq_none => l₁ <:+ l₂, l₁.lookup k
grind_pattern IsSuffix.lookup_eq_none => l₁ <:+ l₂, l₂.lookup k
theorem IsInfix.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <:+: l₂) :
List.lookup k l₂ = none List.lookup k l₁ = none :=
h.sublist.lookup_eq_none
grind_pattern IsInfix.lookup_eq_none => l₁ <:+: l₂, l₁.lookup k
grind_pattern IsInfix.lookup_eq_none => l₁ <:+: l₂, l₂.lookup k
end lookup
end List

View File

@@ -261,11 +261,11 @@ Examples:
/-- Tail recursive implementation of `findRev?`. This is only used at runtime. -/
def findRev?TR (p : α Bool) (l : List α) : Option α := l.reverse.find? p
@[simp] theorem find?_singleton {a : α} : [a].find? p = if p a then some a else none := by
@[simp, grind =] theorem find?_singleton {a : α} : [a].find? p = if p a then some a else none := by
simp only [find?]
split <;> simp_all
@[simp] theorem find?_append {xs ys : List α} : (xs ++ ys).find? p = (xs.find? p).or (ys.find? p) := by
@[simp, grind =] theorem find?_append {xs ys : List α} : (xs ++ ys).find? p = (xs.find? p).or (ys.find? p) := by
induction xs with
| nil => simp [find?]
| cons x xs ih =>
@@ -287,12 +287,12 @@ def findRev?TR (p : α → Bool) (l : List α) : Option α := l.reverse.find? p
/-- Tail recursive implementation of `finSomedRev?`. This is only used at runtime. -/
def findSomeRev?TR (f : α Option β) (l : List α) : Option β := l.reverse.findSome? f
@[simp] theorem findSome?_singleton {a : α} :
@[simp, grind =] theorem findSome?_singleton {a : α} :
[a].findSome? f = f a := by
simp only [findSome?_cons, findSome?_nil]
split <;> simp_all
@[simp] theorem findSome?_append {xs ys : List α} : (xs ++ ys).findSome? f = (xs.findSome? f).or (ys.findSome? f) := by
@[simp, grind =] theorem findSome?_append {xs ys : List α} : (xs ++ ys).findSome? f = (xs.findSome? f).or (ys.findSome? f) := by
induction xs with
| nil => simp [findSome?]
| cons x xs ih =>

View File

@@ -109,9 +109,11 @@ abbrev length_eq_zero := @length_eq_zero_iff
theorem eq_nil_iff_length_eq_zero : l = [] length l = 0 :=
length_eq_zero_iff.symm
@[grind ] theorem length_pos_of_mem {a : α} : {l : List α}, a l 0 < length l
theorem length_pos_of_mem {a : α} : {l : List α}, a l 0 < length l
| _::_, _ => Nat.zero_lt_succ _
grind_pattern length_pos_of_mem => a l, length l
theorem exists_mem_of_length_pos : {l : List α}, 0 < length l a, a l
| _::_, _ => _, .head ..
@@ -1318,6 +1320,19 @@ 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) :
p (xs.filter p)[i] :=
(mem_filter.mp (getElem_mem h)).2
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
simp only [Option.some.injEq] at w
rw [ w]
apply getElem_filter h
grind_pattern getElem?_filter => (xs.filter p)[i]?, some a
@[simp] theorem filter_filter : {l}, filter p (filter q l) = filter (fun a => p a && q a) l
| [] => rfl
| a :: l => by by_cases hp : p a <;> by_cases hq : q a <;> simp [hp, hq, filter_filter]
@@ -2727,11 +2742,11 @@ def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α
foldlRecOn tl op (hl b hb hd mem_cons_self)
fun y hy x hx => hl y hy x (mem_cons_of_mem hd hx)
@[simp] theorem foldlRecOn_nil {motive : β Sort _} {op : β α β} (hb : motive b)
@[simp, grind =] theorem foldlRecOn_nil {motive : β Sort _} {op : β α β} (hb : motive b)
(hl : (b : β) (_ : motive b) (a : α) (_ : a []), motive (op b a)) :
foldlRecOn [] op hb hl = hb := rfl
@[simp] theorem foldlRecOn_cons {motive : β Sort _} {op : β α β} (hb : motive b)
@[simp, grind =] theorem foldlRecOn_cons {motive : β Sort _} {op : β α β} (hb : motive b)
(hl : (b : β) (_ : motive b) (a : α) (_ : a x :: l), motive (op b a)) :
foldlRecOn (x :: l) op hb hl =
foldlRecOn l op (hl b hb x mem_cons_self)
@@ -2762,11 +2777,11 @@ def foldrRecOn {motive : β → Sort _} : ∀ (l : List α) (op : α → β →
hl (foldr op b l)
(foldrRecOn l op hb fun b c a m => hl b c a (mem_cons_of_mem x m)) x mem_cons_self
@[simp] theorem foldrRecOn_nil {motive : β Sort _} {op : α β β} (hb : motive b)
@[simp, grind =] theorem foldrRecOn_nil {motive : β Sort _} {op : α β β} (hb : motive b)
(hl : (b : β) (_ : motive b) (a : α) (_ : a []), motive (op a b)) :
foldrRecOn [] op hb hl = hb := rfl
@[simp] theorem foldrRecOn_cons {motive : β Sort _} {op : α β β} (hb : motive b)
@[simp, grind =] theorem foldrRecOn_cons {motive : β Sort _} {op : α β β} (hb : motive b)
(hl : (b : β) (_ : motive b) (a : α) (_ : a x :: l), motive (op a b)) :
foldrRecOn (x :: l) op hb hl =
hl _ (foldrRecOn l op hb fun b c a m => hl b c a (mem_cons_of_mem x m))
@@ -2778,8 +2793,8 @@ We can prove that two folds over the same list are related (by some arbitrary re
if we know that the initial elements are related and the folding function, for each element of the list,
preserves the relation.
-/
theorem foldl_rel {l : List α} {f g : β α β} {a b : β} {r : β β Prop}
(h : r a b) (h' : (a : α), a l (c c' : β), r c c' r (f c a) (g c' a)) :
theorem foldl_rel {l : List α} {f : β α β} {g : γ α γ} {a : β} {b : γ} {r : β γ Prop}
(h : r a b) (h' : (a : α), a l (c : β) (c' : γ), r c c' r (f c a) (g c' a)) :
r (l.foldl (fun acc a => f acc a) a) (l.foldl (fun acc a => g acc a) b) := by
induction l generalizing a b with
| nil => simp_all
@@ -2794,8 +2809,8 @@ We can prove that two folds over the same list are related (by some arbitrary re
if we know that the initial elements are related and the folding function, for each element of the list,
preserves the relation.
-/
theorem foldr_rel {l : List α} {f g : α β β} {a b : β} {r : β β Prop}
(h : r a b) (h' : (a : α), a l (c c' : β), r c c' r (f a c) (g a c')) :
theorem foldr_rel {l : List α} {f : α β β} {g : α γ γ} {a : β} {b : γ} {r : β γ Prop}
(h : r a b) (h' : (a : α), a l (c : β) (c' : γ), r c c' r (f a c) (g a c')) :
r (l.foldr (fun a acc => f a acc) a) (l.foldr (fun a acc => g a acc) b) := by
induction l generalizing a b with
| nil => simp_all
@@ -2902,13 +2917,13 @@ theorem getLast_filterMap_of_eq_some {f : α → Option β} {l : List α} (w : l
rw [head_filterMap_of_eq_some (by simp_all)]
simp_all
theorem getLast?_flatMap {l : List α} {f : α List β} :
@[grind =] theorem getLast?_flatMap {l : List α} {f : α List β} :
(l.flatMap f).getLast? = l.reverse.findSome? fun a => (f a).getLast? := by
simp only [ head?_reverse, reverse_flatMap]
rw [head?_flatMap]
rfl
theorem getLast?_flatten {L : List (List α)} :
@[grind =] theorem getLast?_flatten {L : List (List α)} :
(flatten L).getLast? = L.reverse.findSome? fun l => l.getLast? := by
simp [ flatMap_id, getLast?_flatMap]
@@ -2923,7 +2938,7 @@ theorem getLast?_replicate {a : α} {n : Nat} : (replicate n a).getLast? = if n
/-! ### leftpad -/
-- We unfold `leftpad` and `rightpad` for verification purposes.
attribute [simp] leftpad rightpad
attribute [simp, grind] leftpad rightpad
-- `length_leftpad` and `length_rightpad` are in `Init.Data.List.Nat.Basic`.
@@ -3027,6 +3042,9 @@ we do not separately develop much theory about it.
theorem mem_partition : a l a (partition p l).1 a (partition p l).2 := by
by_cases p a <;> simp_all
grind_pattern mem_partition => a (partition p l).1
grind_pattern mem_partition => a (partition p l).2
/-! ### dropLast
`dropLast` is the specification for `Array.pop`, so theorems about `List.dropLast`
@@ -3098,7 +3116,7 @@ theorem dropLast_concat_getLast : ∀ {l : List α} (h : l ≠ []), dropLast l +
congr
exact dropLast_concat_getLast (cons_ne_nil b l)
@[simp] theorem map_dropLast {f : α β} {l : List α} : l.dropLast.map f = (l.map f).dropLast := by
@[simp, grind _=_] theorem map_dropLast {f : α β} {l : List α} : l.dropLast.map f = (l.map f).dropLast := by
induction l with
| nil => rfl
| cons x xs ih => cases xs <;> simp [ih]
@@ -3110,6 +3128,7 @@ theorem dropLast_concat_getLast : ∀ {l : List α} (h : l ≠ []), dropLast l +
rw [cons_append, dropLast, dropLast_append_of_ne_nil h, cons_append]
simp [h]
@[grind =]
theorem dropLast_append {l₁ l₂ : List α} :
(l₁ ++ l₂).dropLast = if l₂.isEmpty then l₁.dropLast else l₁ ++ l₂.dropLast := by
split <;> simp_all
@@ -3117,9 +3136,9 @@ theorem dropLast_append {l₁ l₂ : List α} :
theorem dropLast_append_cons : dropLast (l₁ ++ b :: l₂) = l₁ ++ dropLast (b :: l₂) := by
simp
@[simp] theorem dropLast_concat : dropLast (l₁ ++ [b]) = l₁ := by simp
@[simp, grind =] theorem dropLast_concat : dropLast (l₁ ++ [b]) = l₁ := by simp
@[simp] theorem dropLast_replicate {n : Nat} {a : α} : dropLast (replicate n a) = replicate (n - 1) a := by
@[simp, grind =] theorem dropLast_replicate {n : Nat} {a : α} : dropLast (replicate n a) = replicate (n - 1) a := by
match n with
| 0 => simp
| 1 => simp [replicate_succ]
@@ -3132,7 +3151,7 @@ theorem dropLast_append_cons : dropLast (l₁ ++ b :: l₂) = l₁ ++ dropLast (
dropLast (a :: replicate n a) = replicate n a := by
rw [ replicate_succ, dropLast_replicate, Nat.add_sub_cancel]
@[simp] theorem tail_reverse {l : List α} : l.reverse.tail = l.dropLast.reverse := by
@[simp, grind _=_] theorem tail_reverse {l : List α} : l.reverse.tail = l.dropLast.reverse := by
apply ext_getElem
· simp
· intro i h₁ h₂
@@ -3372,6 +3391,7 @@ theorem replace_append_right [LawfulBEq α] {l₁ l₂ : List α} (h : ¬ a ∈
(l₁ ++ l₂).replace a b = l₁ ++ l₂.replace a b := by
simp [replace_append, h]
@[grind _=_]
theorem replace_take {l : List α} {i : Nat} :
(l.take i).replace a b = (l.replace a b).take i := by
induction l generalizing i with
@@ -3538,10 +3558,10 @@ end insert
/-! ### `removeAll` -/
@[simp] theorem removeAll_nil [BEq α] {xs : List α} : xs.removeAll [] = xs := by
@[simp, grind =] theorem removeAll_nil [BEq α] {xs : List α} : xs.removeAll [] = xs := by
simp [removeAll]
theorem cons_removeAll [BEq α] {x : α} {xs ys : List α} :
@[grind =] theorem cons_removeAll [BEq α] {x : α} {xs ys : List α} :
(x :: xs).removeAll ys =
if ys.contains x = false then
x :: xs.removeAll ys
@@ -3549,6 +3569,7 @@ theorem cons_removeAll [BEq α] {x : α} {xs ys : List α} :
xs.removeAll ys := by
simp [removeAll, filter_cons]
@[grind =]
theorem removeAll_cons [BEq α] {xs : List α} {y : α} {ys : List α} :
xs.removeAll (y :: ys) = (xs.filter fun x => !x == y).removeAll ys := by
simp [removeAll, Bool.and_comm]
@@ -3568,7 +3589,7 @@ theorem removeAll_cons [BEq α] {xs : List α} {y : α} {ys : List α} :
/-! ### `eraseDupsBy` and `eraseDups` -/
@[simp] theorem eraseDupsBy_nil : ([] : List α).eraseDupsBy r = [] := rfl
@[simp, grind =] theorem eraseDupsBy_nil : ([] : List α).eraseDupsBy r = [] := rfl
private theorem eraseDupsBy_loop_cons {as bs : List α} {r : α α Bool} :
eraseDupsBy.loop r as bs = bs.reverse ++ eraseDupsBy.loop r (as.filter fun a => !bs.any (r a)) [] := by
@@ -3588,17 +3609,19 @@ private theorem eraseDupsBy_loop_cons {as bs : List α} {r : αα → Bool}
simp
termination_by as.length
@[grind =]
theorem eraseDupsBy_cons :
(a :: as).eraseDupsBy r = a :: (as.filter fun b => r b a = false).eraseDupsBy r := by
simp only [eraseDupsBy, eraseDupsBy.loop, any_nil]
rw [eraseDupsBy_loop_cons]
simp
@[simp] theorem eraseDups_nil [BEq α] : ([] : List α).eraseDups = [] := rfl
theorem eraseDups_cons [BEq α] {a : α} {as : List α} :
@[simp, grind =] theorem eraseDups_nil [BEq α] : ([] : List α).eraseDups = [] := rfl
@[grind =] theorem eraseDups_cons [BEq α] {a : α} {as : List α} :
(a :: as).eraseDups = a :: (as.filter fun b => !b == a).eraseDups := by
simp [eraseDups, eraseDupsBy_cons]
@[grind =]
theorem eraseDups_append [BEq α] [LawfulBEq α] {as bs : List α} :
(as ++ bs).eraseDups = as.eraseDups ++ (bs.removeAll as).eraseDups := by
match as with

View File

@@ -91,7 +91,7 @@ is valid.
subst w
rfl
@[simp]
@[simp, grind =]
theorem mapFinIdx_nil {f : (i : Nat) α (h : i < 0) β} : mapFinIdx [] f = [] :=
rfl
@@ -101,7 +101,7 @@ theorem mapFinIdx_nil {f : (i : Nat) → α → (h : i < 0) → β} : mapFinIdx
| nil => simpa using h
| cons _ _ ih => simp [mapFinIdx.go, ih]
@[simp] theorem length_mapFinIdx {as : List α} {f : (i : Nat) α (h : i < as.length) β} :
@[simp, grind =] theorem length_mapFinIdx {as : List α} {f : (i : Nat) α (h : i < as.length) β} :
(as.mapFinIdx f).length = as.length := by
simp [mapFinIdx, length_mapFinIdx_go]
@@ -129,7 +129,7 @@ theorem getElem_mapFinIdx_go {as : List α} {f : (i : Nat) → α → (h : i < a
· have h₃ : i - acc.size = (i - (acc.size + 1)) + 1 := by omega
simp [h₃]
@[simp] theorem getElem_mapFinIdx {as : List α} {f : (i : Nat) α (h : i < as.length) β} {i : Nat} {h} :
@[simp, grind =] theorem getElem_mapFinIdx {as : List α} {f : (i : Nat) α (h : i < as.length) β} {i : Nat} {h} :
(as.mapFinIdx f)[i] = f i (as[i]'(by simp at h; omega)) (by simp at h; omega) := by
simp [mapFinIdx, getElem_mapFinIdx_go]
@@ -137,18 +137,19 @@ theorem mapFinIdx_eq_ofFn {as : List α} {f : (i : Nat) → α → (h : i < as.l
as.mapFinIdx f = List.ofFn fun i : Fin as.length => f i as[i] i.2 := by
apply ext_getElem <;> simp
@[simp] theorem getElem?_mapFinIdx {l : List α} {f : (i : Nat) α (h : i < l.length) β} {i : Nat} :
@[simp, grind =] theorem getElem?_mapFinIdx {l : List α} {f : (i : Nat) α (h : i < l.length) β} {i : Nat} :
(l.mapFinIdx f)[i]? = l[i]?.pbind fun x m => some <| f i x (by simp [getElem?_eq_some_iff] at m; exact m.1) := by
simp only [getElem?_def, length_mapFinIdx, getElem_mapFinIdx]
split <;> simp
@[simp]
@[simp, grind =]
theorem mapFinIdx_cons {l : List α} {a : α} {f : (i : Nat) α (h : i < l.length + 1) β} :
mapFinIdx (a :: l) f = f 0 a (by omega) :: mapFinIdx l (fun i a h => f (i + 1) a (by omega)) := by
apply ext_getElem
· simp
· rintro (_|i) h₁ h₂ <;> simp
@[grind =]
theorem mapFinIdx_append {xs ys : List α} {f : (i : Nat) α (h : i < (xs ++ ys).length) β} :
(xs ++ ys).mapFinIdx f =
xs.mapFinIdx (fun i a h => f i a (by simp; omega)) ++
@@ -165,7 +166,7 @@ theorem mapFinIdx_append {xs ys : List α} {f : (i : Nat) → α → (h : i < (x
congr
omega
@[simp] theorem mapFinIdx_concat {l : List α} {e : α} {f : (i : Nat) α (h : i < (l ++ [e]).length) β}:
@[simp, grind =] theorem mapFinIdx_concat {l : List α} {e : α} {f : (i : Nat) α (h : i < (l ++ [e]).length) β}:
(l ++ [e]).mapFinIdx f = l.mapFinIdx (fun i a h => f i a (by simp; omega)) ++ [f l.length e (by simp)] := by
simp [mapFinIdx_append]
@@ -201,7 +202,7 @@ theorem exists_of_mem_mapFinIdx {b : β} {l : List α} {f : (i : Nat) → α
obtain h', rfl := h
exact i, h', rfl
@[simp] theorem mem_mapFinIdx {b : β} {l : List α} {f : (i : Nat) α (h : i < l.length) β} :
@[simp, grind =] theorem mem_mapFinIdx {b : β} {l : List α} {f : (i : Nat) α (h : i < l.length) β} :
b l.mapFinIdx f (i : Nat) (h : i < l.length), f i l[i] h = b := by
constructor
· intro h
@@ -287,7 +288,7 @@ theorem mapFinIdx_eq_mapFinIdx_iff {l : List α} {f g : (i : Nat) → α → (h
rw [eq_comm, mapFinIdx_eq_iff]
simp [Fin.forall_iff]
@[simp] theorem mapFinIdx_mapFinIdx {l : List α}
@[simp, grind =] theorem mapFinIdx_mapFinIdx {l : List α}
{f : (i : Nat) α (h : i < l.length) β}
{g : (i : Nat) β (h : i < (l.mapFinIdx f).length) γ} :
(l.mapFinIdx f).mapFinIdx g = l.mapFinIdx (fun i a h => g i (f i a h) (by simpa)) := by
@@ -303,7 +304,7 @@ theorem mapFinIdx_eq_replicate_iff {l : List α} {f : (i : Nat) → α → (h :
· rintro w b i h rfl
exact w i h
@[simp] theorem mapFinIdx_reverse {l : List α} {f : (i : Nat) α (h : i < l.reverse.length) β} :
@[simp, grind =] theorem mapFinIdx_reverse {l : List α} {f : (i : Nat) α (h : i < l.reverse.length) β} :
l.reverse.mapFinIdx f =
(l.mapFinIdx (fun i a h => f (l.length - 1 - i) a (by simp; omega))).reverse := by
simp [mapFinIdx_eq_iff]
@@ -313,7 +314,7 @@ theorem mapFinIdx_eq_replicate_iff {l : List α} {f : (i : Nat) → α → (h :
/-! ### mapIdx -/
@[simp]
@[simp, grind =]
theorem mapIdx_nil {f : Nat α β} : mapIdx f [] = [] :=
rfl
@@ -333,7 +334,7 @@ theorem length_mapIdx_go : ∀ {l : List α} {acc : Array β},
simp
omega
@[simp] theorem length_mapIdx {l : List α} : (l.mapIdx f).length = l.length := by
@[simp, grind =] theorem length_mapIdx {l : List α} : (l.mapIdx f).length = l.length := by
simp [mapIdx, length_mapIdx_go]
theorem getElem?_mapIdx_go : {l : List α} {acc : Array β} {i : Nat},
@@ -356,11 +357,11 @@ theorem getElem?_mapIdx_go : ∀ {l : List α} {acc : Array β} {i : Nat},
· have : i - acc.size = i - (acc.size + 1) + 1 := by omega
simp_all
@[simp] theorem getElem?_mapIdx {l : List α} {i : Nat} :
@[simp, grind =] theorem getElem?_mapIdx {l : List α} {i : Nat} :
(l.mapIdx f)[i]? = Option.map (f i) l[i]? := by
simp [mapIdx, getElem?_mapIdx_go]
@[simp] theorem getElem_mapIdx {l : List α} {f : Nat α β} {i : Nat} {h : i < (l.mapIdx f).length} :
@[simp, grind =] theorem getElem_mapIdx {l : List α} {f : Nat α β} {i : Nat} {h : i < (l.mapIdx f).length} :
(l.mapIdx f)[i] = f i (l[i]'(by simpa using h)) := by
apply Option.some_inj.mp
rw [ getElem?_eq_getElem, getElem?_mapIdx, getElem?_eq_getElem (by simpa using h)]
@@ -384,18 +385,19 @@ theorem mapIdx_eq_zipIdx_map {l : List α} {f : Nat → α → β} :
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_enum_map := @mapIdx_eq_zipIdx_map
@[simp]
@[simp, grind =]
theorem mapIdx_cons {l : List α} {a : α} :
mapIdx f (a :: l) = f 0 a :: mapIdx (fun i => f (i + 1)) l := by
simp [mapIdx_eq_zipIdx_map, List.zipIdx_succ]
@[grind =]
theorem mapIdx_append {xs ys : List α} :
(xs ++ ys).mapIdx f = xs.mapIdx f ++ ys.mapIdx fun i => f (i + xs.length) := by
induction xs generalizing f with
| nil => rfl
| cons _ _ ih => simp [ih (f := fun i => f (i + 1)), Nat.add_assoc]
@[simp] theorem mapIdx_concat {l : List α} {e : α} :
@[simp, grind =] theorem mapIdx_concat {l : List α} {e : α} :
mapIdx f (l ++ [e]) = mapIdx f l ++ [f l.length e] := by
simp [mapIdx_append]
@@ -415,7 +417,7 @@ theorem exists_of_mem_mapIdx {b : β} {l : List α}
rw [mapIdx_eq_mapFinIdx] at h
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
@[simp] theorem mem_mapIdx {b : β} {l : List α} :
@[simp, grind =] theorem mem_mapIdx {b : β} {l : List α} :
b mapIdx f l (i : Nat) (h : i < l.length), f i l[i] = b := by
constructor
· intro h
@@ -470,7 +472,7 @@ theorem mapIdx_eq_mapIdx_iff {l : List α} :
· intro i h₁ h₂
simp [w]
@[simp] theorem mapIdx_set {l : List α} {i : Nat} {a : α} :
@[simp, grind =] theorem mapIdx_set {l : List α} {i : Nat} {a : α} :
(l.set i a).mapIdx f = (l.mapIdx f).set i (f i a) := by
simp only [mapIdx_eq_iff, getElem?_set, length_mapIdx, getElem?_mapIdx]
intro i
@@ -478,16 +480,16 @@ theorem mapIdx_eq_mapIdx_iff {l : List α} :
· split <;> simp_all
· rfl
@[simp] theorem head_mapIdx {l : List α} {f : Nat α β} {w : mapIdx f l []} :
@[simp, grind =] theorem head_mapIdx {l : List α} {f : Nat α β} {w : mapIdx f l []} :
(mapIdx f l).head w = f 0 (l.head (by simpa using w)) := by
cases l with
| nil => simp at w
| cons _ _ => simp
@[simp] theorem head?_mapIdx {l : List α} {f : Nat α β} : (mapIdx f l).head? = l.head?.map (f 0) := by
@[simp, grind =] theorem head?_mapIdx {l : List α} {f : Nat α β} : (mapIdx f l).head? = l.head?.map (f 0) := by
cases l <;> simp
@[simp] theorem getLast_mapIdx {l : List α} {f : Nat α β} {h} :
@[simp, grind =] theorem getLast_mapIdx {l : List α} {f : Nat α β} {h} :
(mapIdx f l).getLast h = f (l.length - 1) (l.getLast (by simpa using h)) := by
cases l with
| nil => simp at h
@@ -498,13 +500,13 @@ theorem mapIdx_eq_mapIdx_iff {l : List α} :
simp only [ mapIdx_cons, getElem_mapIdx]
simp
@[simp] theorem getLast?_mapIdx {l : List α} {f : Nat α β} :
@[simp, grind =] theorem getLast?_mapIdx {l : List α} {f : Nat α β} :
(mapIdx f l).getLast? = (getLast? l).map (f (l.length - 1)) := by
cases l
· simp
· rw [getLast?_eq_getLast, getLast?_eq_getLast, getLast_mapIdx] <;> simp
@[simp] theorem mapIdx_mapIdx {l : List α} {f : Nat α β} {g : Nat β γ} :
@[simp, grind =] theorem mapIdx_mapIdx {l : List α} {f : Nat α β} {g : Nat β γ} :
(l.mapIdx f).mapIdx g = l.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff]
@@ -517,7 +519,7 @@ theorem mapIdx_eq_replicate_iff {l : List α} {f : Nat → α → β} {b : β} :
· rintro w _ i h rfl
exact w i h
@[simp] theorem mapIdx_reverse {l : List α} {f : Nat α β} :
@[simp, grind =] theorem mapIdx_reverse {l : List α} {f : Nat α β} :
l.reverse.mapIdx f = (mapIdx (fun i => f (l.length - 1 - i)) l).reverse := by
simp [mapIdx_eq_iff]
intro i

View File

@@ -26,6 +26,7 @@ namespace List
/-! ### dropLast -/
@[grind _=_]
theorem tail_dropLast {l : List α} : tail (dropLast l) = dropLast (tail l) := by
ext1
simp only [getElem?_tail, getElem?_dropLast, length_tail]
@@ -35,7 +36,7 @@ theorem tail_dropLast {l : List α} : tail (dropLast l) = dropLast (tail l) := b
· omega
· rfl
@[simp] theorem dropLast_reverse {l : List α} : l.reverse.dropLast = l.tail.reverse := by
@[simp, grind _=_] theorem dropLast_reverse {l : List α} : l.reverse.dropLast = l.tail.reverse := by
apply ext_getElem
· simp
· intro i h₁ h₂
@@ -114,7 +115,7 @@ section intersperse
variable {l : List α} {sep : α} {i : Nat}
@[simp] theorem length_intersperse : (l.intersperse sep).length = 2 * l.length - 1 := by
@[simp, grind =] theorem length_intersperse : (l.intersperse sep).length = 2 * l.length - 1 := by
fun_induction intersperse <;> simp only [intersperse, length_cons, length_nil] at *
rename_i h _
have := length_pos_iff.mpr h

View File

@@ -16,6 +16,7 @@ namespace List
open Nat
@[grind =]
theorem countP_set {p : α Bool} {l : List α} {i : Nat} {a : α} (h : i < l.length) :
(l.set i a).countP p = l.countP p - (if p l[i] then 1 else 0) + (if p a then 1 else 0) := by
induction l generalizing i with
@@ -29,10 +30,12 @@ theorem countP_set {p : α → Bool} {l : List α} {i : Nat} {a : α} (h : i < l
have : (if p l[i] = true then 1 else 0) l.countP p := boole_getElem_le_countP (p := p) h
omega
@[grind =]
theorem count_set [BEq α] {a b : α} {l : List α} {i : Nat} (h : i < l.length) :
(l.set i a).count b = l.count b - (if l[i] == b then 1 else 0) + (if a == b then 1 else 0) := by
simp [count_eq_countP, countP_set, h]
@[grind =]
theorem countP_replace [BEq α] [LawfulBEq α] {a b : α} {l : List α} {p : α Bool} :
(l.replace a b).countP p =
if l.contains a then l.countP p + (if p b then 1 else 0) - (if p a then 1 else 0) else l.countP p := by
@@ -55,11 +58,31 @@ theorem countP_replace [BEq α] [LawfulBEq α] {a b : α} {l : List α} {p : α
omega
· omega
@[grind =]
theorem count_replace [BEq α] [LawfulBEq α] {a b c : α} {l : List α} :
(l.replace a b).count c =
if l.contains a then l.count c + (if b == c then 1 else 0) - (if a == c then 1 else 0) else l.count c := by
simp [count_eq_countP, countP_replace]
@[grind =] theorem count_insert [BEq α] [LawfulBEq α] {a b : α} {l : List α} :
count a (List.insert b l) = max (count a l) (if b == a then 1 else 0) := by
simp only [List.insert, contains_eq_mem, decide_eq_true_eq, beq_iff_eq]
split <;> rename_i h
· split <;> rename_i h'
· rw [Nat.max_def]
simp only [beq_iff_eq] at h'
split
· have := List.count_pos_iff.mpr (h' h)
omega
· rfl
· simp [h']
· rw [count_cons]
split <;> rename_i h'
· simp only [beq_iff_eq] at h'
rw [count_eq_zero.mpr (h' h)]
simp [h']
· simp
/--
The number of elements satisfying a predicate in a sublist is at least the number of elements satisfying the predicate in the list,
minus the difference in the lengths.
@@ -98,6 +121,8 @@ theorem le_countP_tail {l} : countP p l - 1 ≤ countP p l.tail := by
simp only [length_tail] at this
omega
grind_pattern le_countP_tail => countP p l.tail
variable [BEq α]
theorem Sublist.le_count (s : l₁ <+ l₂) (a : α) : count a l₂ - (l₂.length - l₁.length) count a l₁ :=
@@ -115,4 +140,6 @@ theorem IsInfix.le_count (s : l₁ <:+: l₂) (a : α) : count a l₂ - (l₂.le
theorem le_count_tail {a : α} {l : List α} : count a l - 1 count a l.tail :=
le_countP_tail
grind_pattern le_count_tail => count a l.tail
end List

View File

@@ -14,6 +14,7 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
@[grind =]
theorem getElem?_eraseIdx {l : List α} {i : Nat} {j : Nat} :
(l.eraseIdx i)[j]? = if j < i then l[j]? else l[j + 1]? := by
rw [eraseIdx_eq_take_drop_succ, getElem?_append]
@@ -49,6 +50,7 @@ theorem getElem?_eraseIdx_of_ge {l : List α} {i : Nat} {j : Nat} (h : i ≤ j)
intro h'
omega
@[grind =]
theorem getElem_eraseIdx {l : List α} {i : Nat} {j : Nat} (h : j < (l.eraseIdx i).length) :
(l.eraseIdx i)[j] = if h' : j < i then
l[j]'(by have := length_eraseIdx_le l i; omega)
@@ -123,6 +125,48 @@ theorem eraseIdx_set_gt {l : List α} {i : Nat} {j : Nat} {a : α} (h : i < j) :
· have t : i n := by omega
simp [t]
@[grind =]
theorem eraseIdx_set {xs : List α} {i : Nat} {a : α} {j : Nat} :
(xs.set i a).eraseIdx j =
if j < i then
(xs.eraseIdx j).set (i - 1) a
else if j = i then
xs.eraseIdx i
else
(xs.eraseIdx j).set i a := by
split <;> rename_i h'
· rw [eraseIdx_set_lt]
omega
· split <;> rename_i h''
· subst h''
rw [eraseIdx_set_eq]
· rw [eraseIdx_set_gt]
omega
theorem set_eraseIdx_le {xs : List α} {i : Nat} {j : Nat} {a : α} (h : i j) :
(xs.eraseIdx i).set j a = (xs.set (j + 1) a).eraseIdx i := by
rw [eraseIdx_set_lt]
· simp
· omega
theorem set_eraseIdx_gt {xs : List α} {i : Nat} {j : Nat} {a : α} (h : j < i) :
(xs.eraseIdx i).set j a = (xs.set j a).eraseIdx i := by
rw [eraseIdx_set_gt]
omega
@[grind =]
theorem set_eraseIdx {xs : List α} {i : Nat} {j : Nat} {a : α} :
(xs.eraseIdx i).set j a =
if i j then
(xs.set (j + 1) a).eraseIdx i
else
(xs.set j a).eraseIdx i := by
split <;> rename_i h'
· rw [set_eraseIdx_le]
omega
· rw [set_eraseIdx_gt]
omega
@[simp] theorem set_getElem_succ_eraseIdx_succ
{l : List α} {i : Nat} (h : i + 1 < l.length) :
(l.eraseIdx (i + 1)).set i l[i + 1] = l.eraseIdx i := by
@@ -143,7 +187,7 @@ theorem eraseIdx_set_gt {l : List α} {i : Nat} {j : Nat} {a : α} (h : i < j) :
· have t : ¬ n < i := by omega
simp [t]
@[simp] theorem eraseIdx_length_sub_one {l : List α} :
@[simp, grind =] theorem eraseIdx_length_sub_one {l : List α} :
(l.eraseIdx (l.length - 1)) = l.dropLast := by
apply ext_getElem
· simp [length_eraseIdx]

View File

@@ -30,19 +30,20 @@ section InsertIdx
variable {a : α}
@[simp]
@[simp, grind =]
theorem insertIdx_zero {xs : List α} {x : α} : xs.insertIdx 0 x = x :: xs :=
rfl
@[simp]
@[simp, grind =]
theorem insertIdx_succ_nil {n : Nat} {a : α} : ([] : List α).insertIdx (n + 1) a = [] :=
rfl
@[simp]
@[simp, grind =]
theorem insertIdx_succ_cons {xs : List α} {hd x : α} {i : Nat} :
(hd :: xs).insertIdx (i + 1) x = hd :: xs.insertIdx i x :=
rfl
@[grind =]
theorem length_insertIdx : {i} {as : List α}, (as.insertIdx i a).length = if i as.length then as.length + 1 else as.length
| 0, _ => by simp
| n + 1, [] => by simp
@@ -56,14 +57,9 @@ theorem length_insertIdx_of_le_length (h : i ≤ length as) (a : α) : (as.inser
theorem length_insertIdx_of_length_lt (h : length as < i) (a : α) : (as.insertIdx i a).length = as.length := by
simp [length_insertIdx, h]
@[simp]
theorem eraseIdx_insertIdx {i : Nat} {l : List α} (a : α) : (l.insertIdx i a).eraseIdx i = l := by
rw [eraseIdx_eq_modifyTailIdx, insertIdx, modifyTailIdx_modifyTailIdx_self]
exact modifyTailIdx_id _ _
theorem insertIdx_eraseIdx_of_ge :
{i m as},
i < length as i m (as.eraseIdx i).insertIdx m a = (as.insertIdx (m + 1) a).eraseIdx i
{i j as},
i < length as i j (as.eraseIdx i).insertIdx j a = (as.insertIdx (j + 1) a).eraseIdx i
| 0, 0, [], has, _ => (Nat.lt_irrefl _ has).elim
| 0, 0, _ :: as, _, _ => by simp [eraseIdx, insertIdx]
| 0, _ + 1, _ :: _, _, _ => rfl
@@ -79,6 +75,15 @@ theorem insertIdx_eraseIdx_of_le :
congrArg (cons a) <|
insertIdx_eraseIdx_of_le (Nat.lt_of_succ_lt_succ has) (Nat.le_of_succ_le_succ hmn)
@[grind =]
theorem insertIdx_eraseIdx (h : i < length as) :
(as.eraseIdx i).insertIdx j a =
if i j then (as.insertIdx (j + 1) a).eraseIdx i else (as.insertIdx j a).eraseIdx (i + 1) := by
split <;> rename_i h'
· rw [insertIdx_eraseIdx_of_ge h h']
· rw [insertIdx_eraseIdx_of_le h (by omega)]
@[grind =]
theorem insertIdx_comm (a b : α) :
{i j : Nat} {l : List α} (_ : i j) (_ : j length l),
(l.insertIdx i a).insertIdx (j + 1) b = (l.insertIdx j b).insertIdx i a
@@ -110,6 +115,11 @@ theorem insertIdx_of_length_lt {l : List α} {x : α} {i : Nat} (h : l.length <
· simp only [Nat.succ_lt_succ_iff, length] at h
simpa using ih h
@[simp, grind =]
theorem eraseIdx_insertIdx_self {i : Nat} {l : List α} (a : α) : (l.insertIdx i a).eraseIdx i = l := by
rw [eraseIdx_eq_modifyTailIdx, insertIdx, modifyTailIdx_modifyTailIdx_self]
exact modifyTailIdx_id _ _
@[simp]
theorem insertIdx_length_self {l : List α} {x : α} : l.insertIdx l.length x = l ++ [x] := by
induction l with
@@ -185,6 +195,7 @@ theorem getElem_insertIdx_of_gt {l : List α} {x : α} {i j : Nat} (hn : i < j)
@[deprecated getElem_insertIdx_of_gt (since := "2025-02-04")]
abbrev getElem_insertIdx_of_ge := @getElem_insertIdx_of_gt
@[grind =]
theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertIdx i x).length) :
(l.insertIdx i x)[j] =
if h₁ : j < i then
@@ -201,6 +212,7 @@ theorem getElem_insertIdx {l : List α} {x : α} {i j : Nat} (h : j < (l.insertI
rw [getElem_insertIdx_self h]
· rw [getElem_insertIdx_of_gt (by omega)]
@[grind =]
theorem getElem?_insertIdx {l : List α} {x : α} {i j : Nat} :
(l.insertIdx i x)[j]? =
if j < i then

View File

@@ -17,7 +17,7 @@ namespace List
/-! ### modifyHead -/
@[simp] theorem length_modifyHead {f : α α} {l : List α} : (l.modifyHead f).length = l.length := by
@[simp, grind =] theorem length_modifyHead {f : α α} {l : List α} : (l.modifyHead f).length = l.length := by
cases l <;> simp [modifyHead]
theorem modifyHead_eq_set [Inhabited α] (f : α α) (l : List α) :
@@ -26,9 +26,10 @@ theorem modifyHead_eq_set [Inhabited α] (f : αα) (l : List α) :
@[simp] theorem modifyHead_eq_nil_iff {f : α α} {l : List α} :
l.modifyHead f = [] l = [] := by cases l <;> simp [modifyHead]
@[simp] theorem modifyHead_modifyHead {l : List α} {f g : α α} :
@[simp, grind =] theorem modifyHead_modifyHead {l : List α} {f g : α α} :
(l.modifyHead f).modifyHead g = l.modifyHead (g f) := by cases l <;> simp [modifyHead]
@[grind =]
theorem getElem_modifyHead {l : List α} {f : α α} {i} (h : i < (l.modifyHead f).length) :
(l.modifyHead f)[i] = if h' : i = 0 then f (l[0]'(by simp at h; omega)) else l[i]'(by simpa using h) := by
cases l with
@@ -41,6 +42,7 @@ theorem getElem_modifyHead {l : List α} {f : αα} {i} (h : i < (l.modifyH
@[simp] theorem getElem_modifyHead_succ {l : List α} {f : α α} {n} (h : n + 1 < (l.modifyHead f).length) :
(l.modifyHead f)[n + 1] = l[n + 1]'(by simpa using h) := by simp [getElem_modifyHead]
@[grind =]
theorem getElem?_modifyHead {l : List α} {f : α α} {i} :
(l.modifyHead f)[i]? = if i = 0 then l[i]?.map f else l[i]? := by
cases l with
@@ -53,19 +55,19 @@ theorem getElem?_modifyHead {l : List α} {f : αα} {i} :
@[simp] theorem getElem?_modifyHead_succ {l : List α} {f : α α} {n} :
(l.modifyHead f)[n + 1]? = l[n + 1]? := by simp [getElem?_modifyHead]
@[simp] theorem head_modifyHead (f : α α) (l : List α) (h) :
@[simp, grind =] theorem head_modifyHead (f : α α) (l : List α) (h) :
(l.modifyHead f).head h = f (l.head (by simpa using h)) := by
cases l with
| nil => simp at h
| cons hd tl => simp
@[simp] theorem head?_modifyHead {l : List α} {f : α α} :
@[simp, grind =] theorem head?_modifyHead {l : List α} {f : α α} :
(l.modifyHead f).head? = l.head?.map f := by cases l <;> simp
@[simp] theorem tail_modifyHead {f : α α} {l : List α} :
@[simp, grind =] theorem tail_modifyHead {f : α α} {l : List α} :
(l.modifyHead f).tail = l.tail := by cases l <;> simp
@[simp] theorem take_modifyHead {f : α α} {l : List α} {i} :
@[simp, grind =] theorem take_modifyHead {f : α α} {l : List α} {i} :
(l.modifyHead f).take i = (l.take i).modifyHead f := by
cases l <;> cases i <;> simp
@@ -73,6 +75,7 @@ theorem getElem?_modifyHead {l : List α} {f : αα} {i} :
(l.modifyHead f).drop i = l.drop i := by
cases l <;> cases i <;> simp_all
@[grind =]
theorem eraseIdx_modifyHead_zero {f : α α} {l : List α} :
(l.modifyHead f).eraseIdx 0 = l.eraseIdx 0 := by simp
@@ -81,7 +84,7 @@ theorem eraseIdx_modifyHead_zero {f : αα} {l : List α} :
@[simp] theorem modifyHead_id : modifyHead (id : α α) = id := by funext l; cases l <;> simp
@[simp] theorem modifyHead_dropLast {l : List α} {f : α α} :
@[simp, grind _=_] theorem modifyHead_dropLast {l : List α} {f : α α} :
l.dropLast.modifyHead f = (l.modifyHead f).dropLast := by
rcases l with _|a, l
· simp
@@ -99,7 +102,7 @@ theorem eraseIdx_eq_modifyTailIdx : ∀ i (l : List α), eraseIdx l i = l.modify
| _+1, [] => rfl
| _+1, _ :: _ => congrArg (cons _) (eraseIdx_eq_modifyTailIdx _ _)
@[simp] theorem length_modifyTailIdx (f : List α List α) (H : l, (f l).length = l.length) :
@[simp, grind =] theorem length_modifyTailIdx (f : List α List α) (H : l, (f l).length = l.length) :
(l : List α) i, (l.modifyTailIdx i f).length = l.length
| _, 0 => H _
| [], _+1 => rfl
@@ -142,7 +145,7 @@ theorem modifyTailIdx_modifyTailIdx_self {f g : List α → List α} (i : Nat) (
/-! ### modify -/
@[simp] theorem modify_nil (f : α α) (i) : [].modify i f = [] := by cases i <;> rfl
@[simp, grind =] theorem modify_nil (f : α α) (i) : [].modify i f = [] := by cases i <;> rfl
@[simp] theorem modify_zero_cons (f : α α) (a : α) (l : List α) :
(a :: l).modify 0 f = f a :: l := rfl
@@ -150,6 +153,15 @@ theorem modifyTailIdx_modifyTailIdx_self {f g : List α → List α} (i : Nat) (
@[simp] theorem modify_succ_cons (f : α α) (a : α) (l : List α) (i) :
(a :: l).modify (i + 1) f = a :: l.modify i f := rfl
@[grind =]
theorem modify_cons {f : α α} {a : α} {l : List α} {i : Nat} :
(a :: l).modify i f =
if i = 0 then f a :: l else a :: l.modify (i - 1) f := by
split <;> rename_i h
· subst h
simp
· match i, h with | i + 1, _ => simp
theorem modifyHead_eq_modify_zero (f : α α) (l : List α) :
l.modifyHead f = l.modify 0 f := by cases l <;> simp
@@ -200,6 +212,7 @@ theorem modify_eq_self {f : αα} {i} {l : List α} (h : l.length ≤ i) :
intro h
omega
@[grind =]
theorem modify_modify_eq (f g : α α) (i) (l : List α) :
(l.modify i f).modify i g = l.modify i (g f) := by
apply ext_getElem
@@ -245,7 +258,7 @@ theorem exists_of_modify (f : αα) {i} {l : List α} (h : i < l.length) :
@[simp] theorem modify_id (i) (l : List α) : l.modify i id = l := by
simp [modify]
@[grind =]
@[grind _=_]
theorem take_modify (f : α α) (i j) (l : List α) :
(l.modify i f).take j = (l.take j).modify i f := by
induction j generalizing l i with

View File

@@ -27,11 +27,12 @@ open Nat
/-! ### range' -/
@[simp] theorem mem_range'_1 : m range' s n s m m < s + n := by
@[simp, grind =] theorem mem_range'_1 : m range' s n s m m < s + n := by
simp [mem_range']; exact
fun i, h, e => e Nat.le_add_right .., Nat.add_lt_add_left h _,
fun h₁, h₂ => m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm
@[grind =]
theorem getLast?_range' {n : Nat} : (range' s n).getLast? = if n = 0 then none else some (s + n - 1) := by
induction n generalizing s with
| zero => simp
@@ -43,7 +44,7 @@ theorem getLast?_range' {n : Nat} : (range' s n).getLast? = if n = 0 then none e
· rw [if_neg h]
simp
@[simp] theorem getLast_range' {n : Nat} (h) : (range' s n).getLast h = s + n - 1 := by
@[simp, grind =] theorem getLast_range' {n : Nat} (h) : (range' s n).getLast h = s + n - 1 := by
cases n with
| zero => simp at h
| succ n => simp [getLast?_range', getLast_eq_iff_getLast?_eq_some]
@@ -158,6 +159,26 @@ theorem erase_range' :
simp [p]
omega
@[simp, grind =]
theorem count_range' {a s n step} (h : 0 < step := by simp) :
count a (range' s n step) = if i, i < n a = s + step * i then 1 else 0 := by
rw [(nodup_range' step h).count]
simp only [mem_range']
@[simp, grind =]
theorem count_range_1' {a s n} :
count a (range' s n) = if s a a < s + n then 1 else 0 := by
rw [count_range' (by simp)]
split <;> rename_i h
· obtain i, h, rfl := h
simp [h]
· simp at h
rw [if_neg]
simp only [not_and, Nat.not_lt]
intro w
specialize h (a - s)
omega
/-! ### range -/
theorem reverse_range' : {s n : Nat}, reverse (range' s n) = map (s + n - 1 - ·) (range n)
@@ -167,7 +188,7 @@ theorem reverse_range' : ∀ {s n : Nat}, reverse (range' s n) = map (s + n - 1
show s + (n + 1) - 1 = s + n from rfl, map, map_map]
simp [reverse_range', Nat.sub_right_comm, Nat.sub_sub]
@[simp]
@[simp, grind =]
theorem mem_range {m n : Nat} : m range n m < n := by
simp only [range_eq_range', mem_range'_1, Nat.zero_le, true_and, Nat.zero_add]
@@ -181,7 +202,7 @@ theorem pairwise_lt_range {n : Nat} : Pairwise (· < ·) (range n) := by
theorem pairwise_le_range {n : Nat} : Pairwise (· ·) (range n) :=
Pairwise.imp Nat.le_of_lt pairwise_lt_range
@[simp] theorem take_range {i n : Nat} : take i (range n) = range (min i n) := by
@[simp, grind =] theorem take_range {i n : Nat} : take i (range n) = range (min i n) := by
apply List.ext_getElem
· simp
· simp +contextual [getElem_take, Nat.lt_min]
@@ -189,10 +210,11 @@ 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] theorem find?_range_eq_some {n : Nat} {i : Nat} {p : Nat Bool} :
@[simp, grind] 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
@@ -200,6 +222,12 @@ theorem find?_range_eq_none {n : Nat} {p : Nat → Bool} :
theorem erase_range : (range n).erase i = range (min n i) ++ range' (i + 1) (n - (i + 1)) := by
simp [range_eq_range', erase_range']
@[simp, grind =]
theorem count_range {a n} :
count a (range n) = if a < n then 1 else 0 := by
rw [range_eq_range', count_range_1']
simp
/-! ### iota -/
section
@@ -348,15 +376,15 @@ end
/-! ### zipIdx -/
@[simp]
@[simp, grind =]
theorem zipIdx_singleton {x : α} {k : Nat} : zipIdx [x] k = [(x, k)] :=
rfl
@[simp] theorem head?_zipIdx {l : List α} {k : Nat} :
@[simp, grind =] theorem head?_zipIdx {l : List α} {k : Nat} :
(zipIdx l k).head? = l.head?.map fun a => (a, k) := by
simp [head?_eq_getElem?]
@[simp] theorem getLast?_zipIdx {l : List α} {k : Nat} :
@[simp, grind =] theorem getLast?_zipIdx {l : List α} {k : Nat} :
(zipIdx l k).getLast? = l.getLast?.map fun a => (a, k + l.length - 1) := by
simp [getLast?_eq_getElem?]
cases l <;> simp
@@ -379,6 +407,7 @@ to avoid the inequality and the subtraction. -/
theorem mk_mem_zipIdx_iff_getElem? {i : Nat} {x : α} {l : List α} : (x, i) zipIdx l l[i]? = some x := by
simp [mk_mem_zipIdx_iff_le_and_getElem?_sub]
@[grind =]
theorem mem_zipIdx_iff_le_and_getElem?_sub {x : α × Nat} {l : List α} {k : Nat} :
x zipIdx l k k x.2 l[x.2 - k]? = some x.1 := by
cases x
@@ -441,6 +470,7 @@ theorem zipIdx_map {l : List α} {k : Nat} {f : α → β} :
rw [map_cons, zipIdx_cons', zipIdx_cons', map_cons, map_map, IH, map_map]
rfl
@[grind =]
theorem zipIdx_append {xs ys : List α} {k : Nat} :
zipIdx (xs ++ ys) k = zipIdx xs k ++ zipIdx ys (k + xs.length) := by
induction xs generalizing ys k with

View File

@@ -118,6 +118,7 @@ theorem suffix_iff_eq_append : l₁ <:+ l₂ ↔ take (length l₂ - length l₁
by rintro r, rfl; simp only [length_append, Nat.add_sub_cancel_right, take_left], fun e =>
_, e
@[grind =]
theorem prefix_take_iff {xs ys : List α} {i : Nat} : xs <+: ys.take i xs <+: ys xs.length i := by
constructor
· intro h

View File

@@ -99,6 +99,7 @@ theorem getLast_take {l : List α} (h : l.take i ≠ []) :
· rw [getElem?_eq_none (by omega), getLast_eq_getElem]
simp
@[grind =]
theorem take_take : {i j} {l : List α}, take i (take j l) = take (min i j) l
| n, 0, l => by rw [Nat.min_zero, take_zero, take_nil]
| 0, m, l => by rw [Nat.zero_min, take_zero, take_zero]
@@ -117,19 +118,19 @@ theorem take_set_of_le {a : α} {i j : Nat} {l : List α} (h : j ≤ i) :
@[deprecated take_set_of_le (since := "2025-02-04")]
abbrev take_set_of_lt := @take_set_of_le
@[simp] theorem take_replicate {a : α} : {i n : Nat}, take i (replicate n a) = replicate (min i n) a
@[simp, grind =] theorem take_replicate {a : α} : {i n : Nat}, take i (replicate n a) = replicate (min i n) a
| n, 0 => by simp [Nat.min_zero]
| 0, m => by simp [Nat.zero_min]
| succ n, succ m => by simp [replicate_succ, succ_min_succ, take_replicate]
@[simp] theorem drop_replicate {a : α} : {i n : Nat}, drop i (replicate n a) = replicate (n - i) a
@[simp, grind =] theorem drop_replicate {a : α} : {i n : Nat}, drop i (replicate n a) = replicate (n - i) a
| n, 0 => by simp
| 0, m => by simp
| succ n, succ m => by simp [replicate_succ, succ_sub_succ, drop_replicate]
/-- Taking the first `i` elements in `l₁ ++ l₂` is the same as appending the first `i` elements
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
theorem take_append_eq_append_take {l₁ l₂ : List α} {i : Nat} :
theorem take_append {l₁ l₂ : List α} {i : Nat} :
take i (l₁ ++ l₂) = take i l₁ ++ take (i - l₁.length) l₂ := by
induction l₁ generalizing i
· simp
@@ -140,15 +141,18 @@ theorem take_append_eq_append_take {l₁ l₂ : List α} {i : Nat} :
congr 1
omega
@[deprecated take_append (since := "2025-06-16")]
abbrev take_append_eq_append_take := @take_append
theorem take_append_of_le_length {l₁ l₂ : List α} {i : Nat} (h : i l₁.length) :
(l₁ ++ l₂).take i = l₁.take i := by
simp [take_append_eq_append_take, Nat.sub_eq_zero_of_le h]
simp [take_append, Nat.sub_eq_zero_of_le h]
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
`i` elements of `l₂` to `l₁`. -/
theorem take_append {l₁ l₂ : List α} (i : Nat) :
theorem take_length_add_append {l₁ l₂ : List α} (i : Nat) :
take (l₁.length + i) (l₁ ++ l₂) = l₁ ++ take i l₂ := by
rw [take_append_eq_append_take, take_of_length_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
rw [take_append, take_of_length_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
@[simp]
theorem take_eq_take_iff :
@@ -162,11 +166,12 @@ theorem take_eq_take_iff :
@[deprecated take_eq_take_iff (since := "2025-02-16")]
abbrev take_eq_take := @take_eq_take_iff
@[grind =]
theorem take_add {l : List α} {i j : Nat} : l.take (i + j) = l.take i ++ (l.drop i).take j := by
suffices take (i + j) (take i l ++ drop i l) = take i l ++ take j (drop i l) by
rw [take_append_drop] at this
assumption
rw [take_append_eq_append_take, take_of_length_le, append_right_inj]
rw [take_append, take_of_length_le, append_right_inj]
· simp only [take_eq_take_iff, length_take, length_drop]
omega
apply Nat.le_trans (m := i)
@@ -236,7 +241,7 @@ dropping the first `i` elements. Version designed to rewrite from the small list
exact Nat.add_lt_of_lt_sub (length_drop h)) := by
rw [getElem_drop']
@[simp]
@[simp, grind =]
theorem getElem?_drop {xs : List α} {i j : Nat} : (xs.drop i)[j]? = xs[i + j]? := by
ext
simp only [getElem?_eq_some_iff, getElem_drop]
@@ -285,7 +290,7 @@ theorem getLast?_drop {l : List α} : (l.drop i).getLast? = if l.length ≤ i th
congr
omega
@[simp] theorem getLast_drop {l : List α} (h : l.drop i []) :
@[simp, grind =] theorem getLast_drop {l : List α} (h : l.drop i []) :
(l.drop i).getLast h = l.getLast (ne_nil_of_length_pos (by simp at h; omega)) := by
simp only [ne_eq, drop_eq_nil_iff] at h
apply Option.some_inj.1
@@ -306,7 +311,8 @@ theorem drop_length_cons {l : List α} (h : l ≠ []) (a : α) :
/-- Dropping the elements up to `i` in `l₁ ++ l₂` is the same as dropping the elements up to `i`
in `l₁`, dropping the elements up to `i - l₁.length` in `l₂`, and appending them. -/
theorem drop_append_eq_append_drop {l₁ l₂ : List α} {i : Nat} :
@[grind =]
theorem drop_append {l₁ l₂ : List α} {i : Nat} :
drop i (l₁ ++ l₂) = drop i l₁ ++ drop (i - l₁.length) l₂ := by
induction l₁ generalizing i
· simp
@@ -316,15 +322,18 @@ theorem drop_append_eq_append_drop {l₁ l₂ : List α} {i : Nat} :
congr 1
omega
@[deprecated drop_append (since := "2025-06-16")]
abbrev drop_append_eq_append_drop := @drop_append
theorem drop_append_of_le_length {l₁ l₂ : List α} {i : Nat} (h : i l₁.length) :
(l₁ ++ l₂).drop i = l₁.drop i ++ l₂ := by
simp [drop_append_eq_append_drop, Nat.sub_eq_zero_of_le h]
simp [drop_append, Nat.sub_eq_zero_of_le h]
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
up to `i` in `l₂`. -/
@[simp]
theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
theorem drop_length_add_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
rw [drop_append, drop_eq_nil_of_le] <;>
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
theorem set_eq_take_append_cons_drop {l : List α} {i : Nat} {a : α} :
@@ -458,7 +467,7 @@ theorem false_of_mem_take_findIdx {xs : List α} {p : α → Bool} (h : x ∈ xs
obtain i, h, rfl := h
exact not_of_lt_findIdx (by omega)
@[simp] theorem findIdx_take {xs : List α} {i : Nat} {p : α Bool} :
@[simp, grind =] theorem findIdx_take {xs : List α} {i : Nat} {p : α Bool} :
(xs.take i).findIdx p = min i (xs.findIdx p) := by
induction xs generalizing i with
| nil => simp
@@ -470,7 +479,7 @@ theorem false_of_mem_take_findIdx {xs : List α} {p : α → Bool} (h : x ∈ xs
· simp
· rw [Nat.add_min_add_right]
@[simp] theorem min_findIdx_findIdx {xs : List α} {p q : α Bool} :
@[simp, grind =] theorem min_findIdx_findIdx {xs : List α} {p q : α Bool} :
min (xs.findIdx p) (xs.findIdx q) = xs.findIdx (fun a => p a || q a) := by
induction xs with
| nil => simp
@@ -512,7 +521,7 @@ theorem dropWhile_eq_drop_findIdx_not {xs : List α} {p : α → Bool} :
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_replicate {n} {a : α} : rotateLeft (replicate m a) n = replicate m a := by
@[simp, grind =] theorem rotateLeft_replicate {n} {a : α} : rotateLeft (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
@@ -525,7 +534,7 @@ theorem dropWhile_eq_drop_findIdx_not {xs : List α} {p : α → Bool} :
/-! ### rotateRight -/
@[simp] theorem rotateRight_replicate {n} {a : α} : rotateRight (replicate m a) n = replicate m a := by
@[simp, grind =] theorem rotateRight_replicate {n} {a : α} : rotateRight (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
@@ -538,7 +547,7 @@ theorem dropWhile_eq_drop_findIdx_not {xs : List α} {p : α → Bool} :
/-! ### zipWith -/
@[simp] theorem length_zipWith {f : α β γ} {l₁ : List α} {l₂ : List β} :
@[simp, grind =] theorem length_zipWith {f : α β γ} {l₁ : List α} {l₂ : List β} :
length (zipWith f l₁ l₂) = min (length l₁) (length l₂) := by
induction l₁ generalizing l₂ <;> cases l₂ <;>
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
@@ -549,7 +558,7 @@ theorem lt_length_left_of_zipWith {f : α → β → γ} {i : Nat} {l : List α}
theorem lt_length_right_of_zipWith {f : α β γ} {i : Nat} {l : List α} {l' : List β}
(h : i < (zipWith f l l').length) : i < l'.length := by rw [length_zipWith] at h; omega
@[simp]
@[simp, grind =]
theorem getElem_zipWith {f : α β γ} {l : List α} {l' : List β}
{i : Nat} {h : i < (zipWith f l l').length} :
(zipWith f l l')[i] =
@@ -566,6 +575,7 @@ theorem zipWith_eq_zipWith_take_min : ∀ {l₁ : List α} {l₂ : List β},
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zipWith_eq_zipWith_take_min (l₁ := l₁) (l₂ := l₂)]
@[grind =]
theorem reverse_zipWith (h : l.length = l'.length) :
(zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
induction l generalizing l' with
@@ -578,14 +588,14 @@ theorem reverse_zipWith (h : l.length = l'.length) :
have : tl.reverse.length = tl'.reverse.length := by simp [h]
simp [hl h, zipWith_append this]
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
@[simp, grind =] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
rw [zipWith_eq_zipWith_take_min]
simp
/-! ### zip -/
@[simp] theorem length_zip {l₁ : List α} {l₂ : List β} :
@[simp, grind =] theorem length_zip {l₁ : List α} {l₂ : List β} :
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
simp [zip]
@@ -597,7 +607,7 @@ theorem lt_length_right_of_zip {i : Nat} {l : List α} {l' : List β} (h : i < (
i < l'.length :=
lt_length_right_of_zipWith h
@[simp]
@[simp, grind =]
theorem getElem_zip {l : List α} {l' : List β} {i : Nat} {h : i < (zip l l').length} :
(zip l l')[i] =
(l[i]'(lt_length_left_of_zip h), l'[i]'(lt_length_right_of_zip h)) :=
@@ -609,7 +619,7 @@ theorem zip_eq_zip_take_min : ∀ {l₁ : List α} {l₂ : List β},
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zip_eq_zip_take_min (l₁ := l₁) (l₂ := l₂)]
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
@[simp, grind =] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
rw [zip_eq_zip_take_min]
simp

View File

@@ -6,7 +6,7 @@ Author: Leonardo de Moura
module
prelude
import Init.Data.Nat.Div.Basic
meta import Init.Data.Nat.Div.Basic
/-!
# Notation for `List` literals.

View File

@@ -34,14 +34,14 @@ to each potential index in order, starting at `0`.
def ofFnM {n} [Monad m] (f : Fin n m α) : m (List α) :=
List.reverse <$> Fin.foldlM n (fun xs i => (· :: xs) <$> f i) []
@[simp]
@[simp, grind =]
theorem length_ofFn {f : Fin n α} : (ofFn f).length = n := by
simp only [ofFn]
induction n with
| zero => simp
| succ n ih => simp [Fin.foldr_succ, ih]
@[simp]
@[simp, grind =]
protected theorem getElem_ofFn {f : Fin n α} (h : i < (ofFn f).length) :
(ofFn f)[i] = f i, by simp_all := by
simp only [ofFn]
@@ -55,7 +55,7 @@ protected theorem getElem_ofFn {f : Fin n → α} (h : i < (ofFn f).length) :
apply ih
simp_all
@[simp]
@[simp, grind =]
protected theorem getElem?_ofFn {f : Fin n α} :
(ofFn f)[i]? = if h : i < n then some (f i, h) else none :=
if h : i < (ofFn f).length
@@ -67,7 +67,7 @@ protected theorem getElem?_ofFn {f : Fin n → α} :
simpa using h
/-- `ofFn` on an empty domain is the empty list. -/
@[simp]
@[simp, grind =]
theorem ofFn_zero {f : Fin 0 α} : ofFn f = [] := by
rw [ofFn, Fin.foldr_zero]
@@ -98,7 +98,7 @@ theorem ofFn_add {n m} {f : Fin (n + m) → α} :
theorem ofFn_eq_nil_iff {f : Fin n α} : ofFn f = [] n = 0 := by
cases n <;> simp only [ofFn_zero, ofFn_succ, eq_self_iff_true, Nat.succ_ne_zero, reduceCtorEq]
@[simp 500]
@[simp 500, grind =]
theorem mem_ofFn {n} {f : Fin n α} {a : α} : a ofFn f i, f i = a := by
constructor
· intro w
@@ -107,17 +107,17 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
· rintro i, rfl
apply mem_of_getElem (i := i) <;> simp
theorem head_ofFn {n} {f : Fin n α} (h : ofFn f []) :
@[grind =] theorem head_ofFn {n} {f : Fin n α} (h : ofFn f []) :
(ofFn f).head h = f 0, Nat.pos_of_ne_zero (mt ofFn_eq_nil_iff.2 h) := by
rw [ getElem_zero (length_ofFn Nat.pos_of_ne_zero (mt ofFn_eq_nil_iff.2 h)),
List.getElem_ofFn]
theorem getLast_ofFn {n} {f : Fin n α} (h : ofFn f []) :
@[grind =]theorem getLast_ofFn {n} {f : Fin n α} (h : ofFn f []) :
(ofFn f).getLast h = f n - 1, Nat.sub_one_lt (mt ofFn_eq_nil_iff.2 h) := by
simp [getLast_eq_getElem, length_ofFn, List.getElem_ofFn]
/-- `ofFnM` on an empty domain is the empty list. -/
@[simp]
@[simp, grind =]
theorem ofFnM_zero [Monad m] [LawfulMonad m] {f : Fin 0 m α} : ofFnM f = pure [] := by
simp [ofFnM]

View File

@@ -159,7 +159,7 @@ theorem pairwise_append_comm {R : αα → Prop} (s : ∀ {x y}, R x y →
@[grind =] theorem pairwise_middle {R : α α Prop} (s : {x y}, R x y R y x) {a : α} {l₁ l₂ : List α} :
Pairwise R (l₁ ++ a :: l₂) Pairwise R (a :: (l₁ ++ l₂)) := by
show Pairwise R (l₁ ++ ([a] ++ l₂)) Pairwise R ([a] ++ l₁ ++ l₂)
change Pairwise R (l₁ ++ ([a] ++ l₂)) Pairwise R ([a] ++ l₁ ++ l₂)
rw [ append_assoc, pairwise_append, @pairwise_append _ _ ([a] ++ l₁), pairwise_append_comm s]
simp only [mem_append, or_comm]
@@ -279,7 +279,11 @@ theorem nodup_nil : @Nodup α [] :=
theorem nodup_cons {a : α} {l : List α} : Nodup (a :: l) a l Nodup l := by
simp only [Nodup, pairwise_cons, forall_mem_ne]
@[grind ] theorem Nodup.sublist : l₁ <+ l₂ Nodup l₂ Nodup l₁ :=
@[grind =] theorem nodup_append {l₁ l₂ : List α} :
(l₁ ++ l₂).Nodup l₁.Nodup l₂.Nodup a l₁, b l₂, a b :=
pairwise_append
theorem Nodup.sublist : l₁ <+ l₂ Nodup l₂ Nodup l₁ :=
Pairwise.sublist
grind_pattern Nodup.sublist => l₁ <+ l₂, Nodup l₁
@@ -312,4 +316,48 @@ theorem getElem?_inj {xs : List α}
@[simp, grind =] theorem nodup_replicate {n : Nat} {a : α} :
(replicate n a).Nodup n 1 := by simp [Nodup]
theorem Nodup.count [BEq α] [LawfulBEq α] {a : α} {l : List α} (h : Nodup l) : count a l = if a l then 1 else 0 := by
split <;> rename_i h'
· obtain s, t, rfl := List.append_of_mem h'
rw [nodup_append] at h
simp_all
rw [count_eq_zero.mpr ?_, count_eq_zero.mpr ?_]
· exact h.2.1.1
· intro w
simpa using h.2.2 _ w
· rw [count_eq_zero_of_not_mem h']
grind_pattern Nodup.count => count a l, Nodup l
@[grind =]
theorem nodup_iff_count [BEq α] [LawfulBEq α] {l : List α} : l.Nodup a, count a l 1 := by
induction l with
| nil => simp
| cons x l ih =>
constructor
· intro h a
simp at h
rw [count_cons]
split <;> rename_i h'
· simp at h'
rw [count_eq_zero.mpr ?_]
· exact Nat.le_refl _
· exact h' h.1
· simp at h'
refine ih.mp h.2 a
· intro h
simp only [count_cons] at h
simp only [nodup_cons]
constructor
· intro w
specialize h x
simp at h
have := count_pos_iff.mpr w
replace h := le_of_lt_succ h
apply Nat.lt_irrefl _ (Nat.lt_of_lt_of_le this h)
· rw [ih]
intro a
specialize h a
exact le_of_add_right_le h
end List

View File

@@ -23,8 +23,7 @@ The notation `~` is used for permutation equivalence.
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
-- TODO: restore after an update-stage0
-- set_option linter.indexVariables true -- Enforce naming conventions for index variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
open Nat
@@ -90,6 +89,9 @@ theorem Perm.mem_iff {a : α} {l₁ l₂ : List α} (p : l₁ ~ l₂) : a ∈ l
| swap => simp only [mem_cons, or_left_comm]
| trans _ _ ih₁ ih₂ => simp only [ih₁, ih₂]
grind_pattern Perm.mem_iff => l₁ ~ l₂, a l₁
grind_pattern Perm.mem_iff => l₁ ~ l₂, a l₂
theorem Perm.subset {l₁ l₂ : List α} (p : l₁ ~ l₂) : l₁ l₂ := fun _ => p.mem_iff.mp
theorem Perm.append_right {l₁ l₂ : List α} (t₁ : List α) (p : l₁ ~ l₂) : l₁ ++ t₁ ~ l₂ ++ t₁ := by
@@ -106,9 +108,15 @@ theorem Perm.append_left {t₁ t₂ : List α} : ∀ l : List α, t₁ ~ t₂
theorem Perm.append {l₁ l₂ t₁ t₂ : List α} (p₁ : l₁ ~ l₂) (p₂ : t₁ ~ t₂) : l₁ ++ t₁ ~ l₂ ++ t₂ :=
(p₁.append_right t₁).trans (p₂.append_left l₂)
grind_pattern Perm.append => l₁ ~ l₂, t₁ ~ t₂, l₁ ++ t₁
grind_pattern Perm.append => l₁ ~ l₂, t₁ ~ t₂, l₂ ++ t₂
theorem Perm.append_cons (a : α) {l₁ l₂ r₁ r₂ : List α} (p₁ : l₁ ~ l₂) (p₂ : r₁ ~ r₂) :
l₁ ++ a :: r₁ ~ l₂ ++ a :: r₂ := p₁.append (p₂.cons a)
grind_pattern Perm.append_cons => l₁ ~ l₂, r₁ ~ r₂, l₁ ++ a :: r₁
grind_pattern Perm.append_cons => l₁ ~ l₂, r₁ ~ r₂, l₂ ++ a :: r₂
@[simp] theorem perm_middle {a : α} : {l₁ l₂ : List α}, l₁ ++ a :: l₂ ~ a :: (l₁ ++ l₂)
| [], _ => .refl _
| b :: _, _ => (Perm.cons _ perm_middle).trans (swap a b _)
@@ -194,9 +202,15 @@ theorem Perm.filterMap (f : α → Option β) {l₁ l₂ : List α} (p : l₁ ~
| swap x y l₂ => cases hx : f x <;> cases hy : f y <;> simp [hx, hy, filterMap_cons, swap]
| trans _p₁ _p₂ IH₁ IH₂ => exact IH₁.trans IH₂
grind_pattern Perm.filterMap => l₁ ~ l₂, filterMap f l₁
grind_pattern Perm.filterMap => l₁ ~ l₂, filterMap f l₂
theorem Perm.map (f : α β) {l₁ l₂ : List α} (p : l₁ ~ l₂) : map f l₁ ~ map f l₂ :=
filterMap_eq_map p.filterMap _
grind_pattern Perm.map => l₁ ~ l₂, map f l₁
grind_pattern Perm.map => l₁ ~ l₂, map f l₂
theorem Perm.pmap {p : α Prop} (f : a, p a β) {l₁ l₂ : List α} (p : l₁ ~ l₂) {H₁ H₂} :
pmap f l₁ H₁ ~ pmap f l₂ H₂ := by
induction p with
@@ -205,12 +219,18 @@ theorem Perm.pmap {p : α → Prop} (f : ∀ a, p a → β) {l₁ l₂ : List α
| swap x y => simp [swap]
| trans _p₁ p₂ IH₁ IH₂ => exact IH₁.trans (IH₂ (H₁ := fun a m => H₂ a (p₂.subset m)))
grind_pattern Perm.pmap => l₁ ~ l₂, pmap f l₁ H₁
grind_pattern Perm.pmap => l₁ ~ l₂, pmap f l₂ H₂
theorem Perm.unattach {α : Type u} {p : α Prop} {l₁ l₂ : List { x // p x }} (h : l₁ ~ l₂) :
l₁.unattach.Perm l₂.unattach := h.map _
theorem Perm.filter (p : α Bool) {l₁ l₂ : List α} (s : l₁ ~ l₂) :
filter p l₁ ~ filter p l₂ := by rw [ filterMap_eq_filter]; apply s.filterMap
grind_pattern Perm.filter => l₁ ~ l₂, filter p l₁
grind_pattern Perm.filter => l₁ ~ l₂, filter p l₂
theorem filter_append_perm (p : α Bool) (l : List α) :
filter p l ++ filter (fun x => !p x) l ~ l := by
induction l with
@@ -388,12 +408,16 @@ theorem Perm.erase (a : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) : l₁.erase
have h₂ : a l₂ := mt p.mem_iff.2 h₁
rw [erase_of_not_mem h₁, erase_of_not_mem h₂]; exact p
grind_pattern Perm.erase => l₁ ~ l₂, l₁.erase a
grind_pattern Perm.erase => l₁ ~ l₂, l₂.erase a
theorem cons_perm_iff_perm_erase {a : α} {l₁ l₂ : List α} :
a :: l₁ ~ l₂ a l₂ l₁ ~ l₂.erase a := by
refine fun h => ?_, fun m, h => (h.cons a).trans (perm_cons_erase m).symm
have : a l₂ := h.subset mem_cons_self
exact this, (h.trans <| perm_cons_erase this).cons_inv
@[grind =]
theorem perm_iff_count {l₁ l₂ : List α} : l₁ ~ l₂ a, count a l₁ = count a l₂ := by
refine Perm.count_eq, fun H => ?_
induction l₁ generalizing l₂ with
@@ -410,6 +434,12 @@ theorem perm_iff_count {l₁ l₂ : List α} : l₁ ~ l₂ ↔ ∀ a, count a l
rw [(perm_cons_erase this).count_eq] at H
by_cases h : b = a <;> simpa [h, count_cons, Nat.succ_inj] using H
theorem Perm.count (h : l₁ ~ l₂) (a : α) : count a l₁ = count a l₂ := by
rw [perm_iff_count.mp h]
grind_pattern Perm.count => l₁ ~ l₂, count a l₁
grind_pattern Perm.count => l₁ ~ l₂, count a l₂
theorem isPerm_iff : {l₁ l₂ : List α}, l₁.isPerm l₂ l₁ ~ l₂
| [], [] => by simp [isPerm, isEmpty]
| [], _ :: _ => by simp [isPerm, isEmpty, Perm.nil_eq]
@@ -425,6 +455,9 @@ protected theorem Perm.insert (a : α) {l₁ l₂ : List α} (p : l₁ ~ l₂) :
have := p.cons a
simpa [h, mt p.mem_iff.2 h] using this
grind_pattern Perm.insert => l₁ ~ l₂, l₁.insert a
grind_pattern Perm.insert => l₁ ~ l₂, l₂.insert a
theorem perm_insert_swap (x y : α) (l : List α) :
List.insert x (List.insert y l) ~ List.insert y (List.insert x l) := by
by_cases xl : x l <;> by_cases yl : y l <;> simp [xl, yl]
@@ -491,6 +524,9 @@ theorem Perm.nodup {l l' : List α} (hl : l ~ l') (hR : l.Nodup) : l'.Nodup := h
theorem Perm.nodup_iff {l₁ l₂ : List α} : l₁ ~ l₂ (Nodup l₁ Nodup l₂) :=
Perm.pairwise_iff <| @Ne.symm α
grind_pattern Perm.nodup_iff => l₁ ~ l₂, Nodup l₁
grind_pattern Perm.nodup_iff => l₁ ~ l₂, Nodup l₂
theorem Perm.flatten {l₁ l₂ : List (List α)} (h : l₁ ~ l₂) : l₁.flatten ~ l₂.flatten := by
induction h with
| nil => rfl
@@ -541,20 +577,30 @@ theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i ≤ l.length) :
namespace Perm
theorem take {l₁ l₂ : List α} (h : l₁ ~ l₂) {n : Nat} (w : l₁.drop n ~ l₂.drop n) :
l₁.take n ~ l₂.take n := by
theorem take {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} (w : l₁.drop i ~ l₂.drop i) :
l₁.take i ~ l₂.take i := by
classical
rw [perm_iff_count] at h w
rw [ take_append_drop n l₁, take_append_drop n l₂] at h
rw [ take_append_drop i l₁, take_append_drop i l₂] at h
simpa only [count_append, w, Nat.add_right_cancel_iff] using h
theorem drop {l₁ l₂ : List α} (h : l₁ ~ l₂) {n : Nat} (w : l₁.take n ~ l₂.take n) :
l₁.drop n ~ l₂.drop n := by
theorem drop {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} (w : l₁.take i ~ l₂.take i) :
l₁.drop i ~ l₂.drop i := by
classical
rw [perm_iff_count] at h w
rw [ take_append_drop n l₁, take_append_drop n l₂] at h
rw [ take_append_drop i l₁, take_append_drop i l₂] at h
simpa only [count_append, w, Nat.add_left_cancel_iff] using h
theorem sum_nat {l₁ l₂ : List Nat} (h : l₁ ~ l₂) : l₁.sum = l₂.sum := by
induction h with
| nil => simp
| cons _ _ ih => simp [ih]
| swap => simpa [List.sum_cons] using Nat.add_left_comm ..
| trans _ _ ih₁ ih₂ => simp [ih₁, ih₂]
grind_pattern Perm.sum_nat => l₁ ~ l₂, l₁.sum
grind_pattern Perm.sum_nat => l₁ ~ l₂, l₂.sum
end Perm
end List

View File

@@ -225,7 +225,7 @@ theorem zipIdx_eq_nil_iff {l : List α} {i : Nat} : List.zipIdx l i = [] ↔ l =
| [], _ => rfl
| _ :: _, _ => congrArg Nat.succ length_zipIdx
@[simp]
@[simp, grind =]
theorem getElem?_zipIdx :
{l : List α} {i j}, (zipIdx l i)[j]? = l[j]?.map fun a => (a, i + j)
| [], _, _ => rfl
@@ -234,7 +234,7 @@ theorem getElem?_zipIdx :
simp only [zipIdx_cons, getElem?_cons_succ]
exact getElem?_zipIdx.trans <| by rw [Nat.add_right_comm]; rfl
@[simp]
@[simp, grind =]
theorem getElem_zipIdx {l : List α} (h : i < (l.zipIdx j).length) :
(l.zipIdx j)[i] = (l[i]'(by simpa [length_zipIdx] using h), j + i) := by
simp only [length_zipIdx] at h
@@ -242,7 +242,7 @@ theorem getElem_zipIdx {l : List α} (h : i < (l.zipIdx j).length) :
simp only [getElem?_zipIdx, getElem?_eq_getElem h]
simp
@[simp]
@[simp, grind =]
theorem tail_zipIdx {l : List α} {i : Nat} : (zipIdx l i).tail = zipIdx l.tail (i + 1) := by
induction l generalizing i with
| nil => simp

View File

@@ -467,7 +467,7 @@ theorem replace_takeWhile [BEq α] [LawfulBEq α] {l : List α} {p : α → Bool
/-! ### splitAt -/
@[simp] theorem splitAt_eq {i : Nat} {l : List α} : splitAt i l = (l.take i, l.drop i) := by
@[simp, grind =] theorem splitAt_eq {i : Nat} {l : List α} : splitAt i l = (l.take i, l.drop i) := by
rw [splitAt, splitAt_go, reverse_nil, nil_append]
split <;> simp_all [take_of_length_le, drop_of_length_le]

View File

@@ -685,7 +685,7 @@ theorem replace_toArray [BEq α] [LawfulBEq α] (l : List α) (a b : α) :
· rw [if_pos (by omega), if_pos, if_neg]
· simp only [mem_take_iff_getElem, not_exists]
intro k hk
simpa using h.2 k, by omega (by show k < i.1; omega)
simpa using h.2 k, by omega (by change k < i.1; omega)
· subst h₃
simpa using h.1
· rw [if_neg (by omega)]

View File

@@ -46,6 +46,7 @@ theorem zipWith_self {f : αα → δ} : ∀ {l : List α}, zipWith f l l =
See also `getElem?_zipWith'` for a variant
using `Option.map` and `Option.bind` rather than a `match`.
-/
@[grind =]
theorem getElem?_zipWith {f : α β γ} {i : Nat} :
(zipWith f as bs)[i]? = match as[i]?, bs[i]? with
| some a, some b => some (f a b) | _, _ => none := by
@@ -83,33 +84,39 @@ theorem getElem?_zip_eq_some {l₁ : List α} {l₂ : List β} {z : α × β} {i
· rintro h₀, h₁
exact _, _, h₀, h₁, rfl
@[grind =]
theorem head?_zipWith {f : α β γ} :
(List.zipWith f as bs).head? = match as.head?, bs.head? with
| some a, some b => some (f a b) | _, _ => none := by
simp [head?_eq_getElem?, getElem?_zipWith]
@[grind =]
theorem head_zipWith {f : α β γ} (h):
(List.zipWith f as bs).head h = f (as.head (by rintro rfl; simp_all)) (bs.head (by rintro rfl; simp_all)) := by
apply Option.some.inj
rw [ head?_eq_head, head?_zipWith, head?_eq_head, head?_eq_head]
@[simp]
@[simp, grind =]
theorem zipWith_map {μ} {f : γ δ μ} {g : α γ} {h : β δ} {l₁ : List α} {l₂ : List β} :
zipWith f (l₁.map g) (l₂.map h) = zipWith (fun a b => f (g a) (h b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
@[grind =]
theorem zipWith_map_left {l₁ : List α} {l₂ : List β} {f : α α'} {g : α' β γ} :
zipWith g (l₁.map f) l₂ = zipWith (fun a b => g (f a) b) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
@[grind =]
theorem zipWith_map_right {l₁ : List α} {l₂ : List β} {f : β β'} {g : α β' γ} :
zipWith g l₁ (l₂.map f) = zipWith (fun a b => g a (f b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
@[grind =]
theorem zipWith_foldr_eq_zip_foldr {f : α β γ} {i : δ} {g : γ δ δ} :
(zipWith f l₁ l₂).foldr g i = (zip l₁ l₂).foldr (fun p r => g (f p.1 p.2) r) i := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
@[grind =]
theorem zipWith_foldl_eq_zip_foldl {f : α β γ} {i : δ} {g : δ γ δ} :
(zipWith f l₁ l₂).foldl g i = (zip l₁ l₂).foldl (fun r p => g r (f p.1 p.2)) i := by
induction l₁ generalizing i l₂ <;> cases l₂ <;> simp_all
@@ -118,6 +125,7 @@ theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} {i : δ} {g : δ →
theorem zipWith_eq_nil_iff {f : α β γ} {l l'} : zipWith f l l' = [] l = [] l' = [] := by
cases l <;> cases l' <;> simp
@[grind =]
theorem map_zipWith {δ : Type _} {f : α β} {g : γ δ α} {l : List γ} {l' : List δ} :
map f (zipWith g l l') = zipWith (fun x y => f (g x y)) l l' := by
induction l generalizing l' with
@@ -127,6 +135,7 @@ theorem map_zipWith {δ : Type _} {f : α → β} {g : γ → δ → α} {l : Li
· simp
· simp [hl]
@[grind =]
theorem take_zipWith : (zipWith f l l').take i = zipWith f (l.take i) (l'.take i) := by
induction l generalizing l' i with
| nil => simp
@@ -137,6 +146,7 @@ theorem take_zipWith : (zipWith f l l').take i = zipWith f (l.take i) (l'.take i
· simp
· simp [hl]
@[grind =]
theorem drop_zipWith : (zipWith f l l').drop i = zipWith f (l.drop i) (l'.drop i) := by
induction l generalizing l' i with
| nil => simp
@@ -147,10 +157,11 @@ theorem drop_zipWith : (zipWith f l l').drop i = zipWith f (l.drop i) (l'.drop i
· simp
· simp [hl]
@[simp]
@[simp, grind =]
theorem tail_zipWith : (zipWith f l l').tail = zipWith f l.tail l'.tail := by
rw [ drop_one]; simp [drop_zipWith]
@[grind =]
theorem zipWith_append {f : α β γ} {l₁ l₁' : List α} {l₂ l₂' : List β}
(h : l₁.length = l₂.length) :
zipWith f (l₁ ++ l₁') (l₂ ++ l₂') = zipWith f l₁ l₂ ++ zipWith f l₁' l₂' := by
@@ -254,22 +265,26 @@ theorem zip_eq_zipWith : ∀ {l₁ : List α} {l₂ : List β}, zip l₁ l₂ =
| _, [] => rfl
| a :: l₁, b :: l₂ => by simp [zip_cons_cons, zip_eq_zipWith (l₁ := l₁)]
@[grind _=_]
theorem zip_map {f : α γ} {g : β δ} :
{l₁ : List α} {l₂ : List β}, zip (l₁.map f) (l₂.map g) = (zip l₁ l₂).map (Prod.map f g)
| [], _ => rfl
| _, [] => by simp only [map, zip_nil_right]
| _ :: _, _ :: _ => by simp only [map, zip_cons_cons, zip_map, Prod.map]
@[grind _=_]
theorem zip_map_left {f : α γ} {l₁ : List α} {l₂ : List β} :
zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id) := by rw [ zip_map, map_id]
@[grind _=_]
theorem zip_map_right {f : β γ} {l₁ : List α} {l₂ : List β} :
zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f) := by rw [ zip_map, map_id]
@[simp] theorem tail_zip {l₁ : List α} {l₂ : List β} :
@[simp, grind =] theorem tail_zip {l₁ : List α} {l₂ : List β} :
(zip l₁ l₂).tail = zip l₁.tail l₂.tail := by
cases l₁ <;> cases l₂ <;> simp
@[grind =]
theorem zip_append :
{l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
@@ -278,6 +293,7 @@ theorem zip_append :
| _ :: _, _, _ :: _, _, h => by
simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
@[grind =]
theorem zip_map' {f : α β} {g : α γ} :
{l : List α}, zip (l.map f) (l.map g) = l.map fun a => (f a, g a)
| [] => rfl
@@ -296,7 +312,7 @@ theorem map_fst_zip :
| [], _, _ => rfl
| _ :: as, _ :: bs, h => by
simp [Nat.succ_le_succ_iff] at h
show _ :: map Prod.fst (zip as bs) = _ :: as
change _ :: map Prod.fst (zip as bs) = _ :: as
rw [map_fst_zip (l₁ := as) h]
| _ :: _, [], h => by simp at h
@@ -308,7 +324,7 @@ theorem map_snd_zip :
| [], b :: bs, h => by simp at h
| a :: as, b :: bs, h => by
simp [Nat.succ_le_succ_iff] at h
show _ :: map Prod.snd (zip as bs) = _ :: bs
change _ :: map Prod.snd (zip as bs) = _ :: bs
rw [map_snd_zip (l₂ := bs) h]
theorem map_prod_left_eq_zip {l : List α} {f : α β} :
@@ -353,6 +369,7 @@ theorem zip_eq_append_iff {l₁ : List α} {l₂ : List β} :
/-! ### zipWithAll -/
@[grind =]
theorem getElem?_zipWithAll {f : Option α Option β γ} {i : Nat} :
(zipWithAll f as bs)[i]? = match as[i]?, bs[i]? with
| none, none => .none | a?, b? => some (f a? b?) := by
@@ -366,33 +383,38 @@ theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
cases i <;> simp_all
| cons b bs => cases i <;> simp_all
@[grind =]
theorem head?_zipWithAll {f : Option α Option β γ} :
(zipWithAll f as bs).head? = match as.head?, bs.head? with
| none, none => .none | a?, b? => some (f a? b?) := by
simp [head?_eq_getElem?, getElem?_zipWithAll]
@[simp] theorem head_zipWithAll {f : Option α Option β γ} (h) :
@[simp, grind =] theorem head_zipWithAll {f : Option α Option β γ} (h) :
(zipWithAll f as bs).head h = f as.head? bs.head? := by
apply Option.some.inj
rw [ head?_eq_head, head?_zipWithAll]
split <;> simp_all
@[simp] theorem tail_zipWithAll {f : Option α Option β γ} :
@[simp, grind =] theorem tail_zipWithAll {f : Option α Option β γ} :
(zipWithAll f as bs).tail = zipWithAll f as.tail bs.tail := by
cases as <;> cases bs <;> simp
@[grind =]
theorem zipWithAll_map {μ} {f : Option γ Option δ μ} {g : α γ} {h : β δ} {l₁ : List α} {l₂ : List β} :
zipWithAll f (l₁.map g) (l₂.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
@[grind =]
theorem zipWithAll_map_left {l₁ : List α} {l₂ : List β} {f : α α'} {g : Option α' Option β γ} :
zipWithAll g (l₁.map f) l₂ = zipWithAll (fun a b => g (f <$> a) b) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
@[grind =]
theorem zipWithAll_map_right {l₁ : List α} {l₂ : List β} {f : β β'} {g : Option α Option β' γ} :
zipWithAll g l₁ (l₂.map f) = zipWithAll (fun a b => g a (f <$> b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
@[grind =]
theorem map_zipWithAll {δ : Type _} {f : α β} {g : Option γ Option δ α} {l : List γ} {l' : List δ} :
map f (zipWithAll g l l') = zipWithAll (fun x y => f (g x y)) l l' := by
induction l generalizing l' with
@@ -400,7 +422,7 @@ theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option
| cons hd tl hl =>
cases l' <;> simp_all
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
@[simp, grind =] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (replicate n a) (replicate n b) = replicate n (f (some a) (some b)) := by
induction n with
| zero => rfl
@@ -408,12 +430,13 @@ theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option
/-! ### unzip -/
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
@[simp, grind =] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
induction l <;> simp_all
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
@[simp, grind =] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
induction l <;> simp_all
@[grind =]
theorem unzip_eq_map : {l : List (α × β)}, unzip l = (l.map Prod.fst, l.map Prod.snd)
| [] => rfl
| (a, b) :: l => by simp only [unzip_cons, map_cons, unzip_eq_map (l := l)]
@@ -453,6 +476,6 @@ theorem tail_zip_fst {l : List (α × β)} : l.unzip.1.tail = l.tail.unzip.1 :=
theorem tail_zip_snd {l : List (α × β)} : l.unzip.2.tail = l.tail.unzip.2 := by
simp
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
@[simp, grind =] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
ext1 <;> simp

View File

@@ -406,6 +406,12 @@ theorem le_of_add_right_le {n m k : Nat} (h : n + k ≤ m) : n ≤ m :=
theorem le_add_right_of_le {n m k : Nat} (h : n m) : n m + k :=
Nat.le_trans h (le_add_right m k)
theorem le_of_add_left_le {n m k : Nat} (h : k + n m) : n m :=
Nat.le_trans (le_add_left n k) h
theorem le_add_left_of_le {n m k : Nat} (h : n m) : n k + m :=
Nat.le_trans h (le_add_left m k)
theorem lt_of_add_one_le {n m : Nat} (h : n + 1 m) : n < m := h
theorem add_one_le_of_lt {n m : Nat} (h : n < m) : n + 1 m := h
@@ -1069,7 +1075,7 @@ protected theorem sub_lt_sub_right : ∀ {a b c : Nat}, c ≤ a → a < b → a
exact Nat.sub_lt_sub_right (le_of_succ_le_succ hle) (lt_of_succ_lt_succ h)
protected theorem sub_self_add (n m : Nat) : n - (n + m) = 0 := by
show (n + 0) - (n + m) = 0
change (n + 0) - (n + m) = 0
rw [Nat.add_sub_add_left, Nat.zero_sub]
@[simp] protected theorem sub_eq_zero_of_le {n m : Nat} (h : n m) : n - m = 0 := by

View File

@@ -51,24 +51,24 @@ noncomputable def div2Induction {motive : Nat → Sort u}
apply hyp
exact Nat.div_lt_self n_pos (Nat.le_refl _)
@[simp] theorem zero_and (x : Nat) : 0 &&& x = 0 := by
@[simp, grind =] theorem zero_and (x : Nat) : 0 &&& x = 0 := by
simp only [HAnd.hAnd, AndOp.and, land]
unfold bitwise
simp
@[simp] theorem and_zero (x : Nat) : x &&& 0 = 0 := by
@[simp, grind =] theorem and_zero (x : Nat) : x &&& 0 = 0 := by
simp only [HAnd.hAnd, AndOp.and, land]
unfold bitwise
simp
@[simp] theorem one_and_eq_mod_two (n : Nat) : 1 &&& n = n % 2 := by
@[simp, grind =] theorem one_and_eq_mod_two (n : Nat) : 1 &&& n = n % 2 := by
if n0 : n = 0 then
subst n0; decide
else
simp only [HAnd.hAnd, AndOp.and, land]
cases mod_two_eq_zero_or_one n with | _ h => simp [bitwise, n0, h]
@[simp] theorem and_one_is_mod (x : Nat) : x &&& 1 = x % 2 := by
@[simp, grind =] theorem and_one_is_mod (x : Nat) : x &&& 1 = x % 2 := by
if xz : x = 0 then
simp [xz, zero_and]
else
@@ -102,11 +102,12 @@ Depending on use cases either `testBit_add_one` or `testBit_div_two`
may be more useful as a `simp` lemma, so neither is a global `simp` lemma.
-/
-- We turn `testBit_add_one` on as a `local simp` for this file.
@[local simp]
@[local simp, grind _=_]
theorem testBit_add_one (x i : Nat) : testBit x (i + 1) = testBit (x/2) i := by
unfold testBit
simp [shiftRight_succ_inside]
@[grind _=_]
theorem testBit_add (x i n : Nat) : testBit x (i + n) = testBit (x / 2 ^ n) i := by
revert x
induction n with
@@ -122,6 +123,7 @@ theorem testBit_div_two (x i : Nat) : testBit (x / 2) i = testBit x (i + 1) := b
theorem testBit_div_two_pow (x i : Nat) : testBit (x / 2 ^ n) i = testBit x (i + n) :=
testBit_add .. |>.symm
@[grind =]
theorem testBit_eq_decide_div_mod_eq {x : Nat} : testBit x i = decide (x / 2^i % 2 = 1) := by
induction i generalizing x with
| zero =>
@@ -290,7 +292,7 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
| d+1 =>
simp [Nat.pow_succ, Nat.mul_comm _ 2, Nat.mul_add_mod]
@[simp] theorem testBit_mod_two_pow (x j i : Nat) :
@[simp, grind =] theorem testBit_mod_two_pow (x j i : Nat) :
testBit (x % 2^j) i = (decide (i < j) && testBit x i) := by
induction x using Nat.strongRecOn generalizing j i with
| ind x hyp =>
@@ -322,6 +324,7 @@ theorem not_decide_mod_two_eq_one (x : Nat)
: (!decide (x % 2 = 1)) = decide (x % 2 = 0) := by
cases Nat.mod_two_eq_zero_or_one x <;> (rename_i p; simp [p])
@[grind =]
theorem testBit_two_pow_sub_succ (h₂ : x < 2 ^ n) (i : Nat) :
testBit (2^n - (x + 1)) i = (decide (i < n) && ! testBit x i) := by
induction i generalizing n x with
@@ -357,6 +360,7 @@ theorem testBit_one_eq_true_iff_self_eq_zero {i : Nat} :
Nat.testBit 1 i = true i = 0 := by
cases i <;> simp
@[grind =]
theorem testBit_two_pow {n m : Nat} : testBit (2 ^ n) m = decide (n = m) := by
rw [testBit, shiftRight_eq_div_pow]
by_cases h : n = m
@@ -482,18 +486,20 @@ theorem bitwise_mod_two_pow (of_false_false : f false false = false := by rfl) :
/-! ### and -/
@[simp] theorem testBit_and (x y i : Nat) : (x &&& y).testBit i = (x.testBit i && y.testBit i) := by
@[simp, grind =] theorem testBit_and (x y i : Nat) : (x &&& y).testBit i = (x.testBit i && y.testBit i) := by
simp [HAnd.hAnd, AndOp.and, land, testBit_bitwise ]
@[simp] protected theorem and_self (x : Nat) : x &&& x = x := by
@[simp, grind =] protected theorem and_self (x : Nat) : x &&& x = x := by
apply Nat.eq_of_testBit_eq
simp
@[grind =]
protected theorem and_comm (x y : Nat) : x &&& y = y &&& x := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_comm]
@[grind _=_]
protected theorem and_assoc (x y z : Nat) : (x &&& y) &&& z = x &&& (y &&& z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_assoc]
@@ -537,54 +543,63 @@ abbrev and_pow_two_sub_one_of_lt_two_pow := @and_two_pow_sub_one_of_lt_two_pow
rw [testBit_and]
simp
@[grind _=_]
theorem and_div_two_pow : (a &&& b) / 2 ^ n = a / 2 ^ n &&& b / 2 ^ n :=
bitwise_div_two_pow
@[grind _=_]
theorem and_div_two : (a &&& b) / 2 = a / 2 &&& b / 2 :=
and_div_two_pow (n := 1)
@[grind _=_]
theorem and_mod_two_pow : (a &&& b) % 2 ^ n = (a % 2 ^ n) &&& (b % 2 ^ n) :=
bitwise_mod_two_pow
/-! ### lor -/
@[simp] theorem zero_or (x : Nat) : 0 ||| x = x := by
@[simp, grind =] theorem zero_or (x : Nat) : 0 ||| x = x := by
simp only [HOr.hOr, OrOp.or, lor]
unfold bitwise
simp [@eq_comm _ 0]
@[simp] theorem or_zero (x : Nat) : x ||| 0 = x := by
@[simp, grind =] theorem or_zero (x : Nat) : x ||| 0 = x := by
simp only [HOr.hOr, OrOp.or, lor]
unfold bitwise
simp [@eq_comm _ 0]
@[simp] theorem testBit_or (x y i : Nat) : (x ||| y).testBit i = (x.testBit i || y.testBit i) := by
@[simp, grind =] theorem testBit_or (x y i : Nat) : (x ||| y).testBit i = (x.testBit i || y.testBit i) := by
simp [HOr.hOr, OrOp.or, lor, testBit_bitwise ]
@[simp] protected theorem or_self (x : Nat) : x ||| x = x := by
@[simp, grind =] protected theorem or_self (x : Nat) : x ||| x = x := by
apply Nat.eq_of_testBit_eq
simp
@[grind =]
protected theorem or_comm (x y : Nat) : x ||| y = y ||| x := by
apply Nat.eq_of_testBit_eq
simp [Bool.or_comm]
@[grind _=_]
protected theorem or_assoc (x y z : Nat) : (x ||| y) ||| z = x ||| (y ||| z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.or_assoc]
@[grind _=_]
theorem and_or_distrib_left (x y z : Nat) : x &&& (y ||| z) = (x &&& y) ||| (x &&& z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_or_distrib_left]
@[grind _=_]
theorem and_distrib_right (x y z : Nat) : (x ||| y) &&& z = (x &&& z) ||| (y &&& z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_or_distrib_right]
@[grind _=_]
theorem or_and_distrib_left (x y z : Nat) : x ||| (y &&& z) = (x ||| y) &&& (x ||| z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.or_and_distrib_left]
@[grind _=_]
theorem or_and_distrib_right (x y z : Nat) : (x &&& y) ||| z = (x ||| z) &&& (y ||| z) := by
apply Nat.eq_of_testBit_eq
simp [Bool.or_and_distrib_right]
@@ -610,37 +625,42 @@ theorem or_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ||| y
rw [testBit_or]
simp
@[grind _=_]
theorem or_div_two_pow : (a ||| b) / 2 ^ n = a / 2 ^ n ||| b / 2 ^ n :=
bitwise_div_two_pow
@[grind _=_]
theorem or_div_two : (a ||| b) / 2 = a / 2 ||| b / 2 :=
or_div_two_pow (n := 1)
@[grind _=_]
theorem or_mod_two_pow : (a ||| b) % 2 ^ n = a % 2 ^ n ||| b % 2 ^ n :=
bitwise_mod_two_pow
/-! ### xor -/
@[simp] theorem testBit_xor (x y i : Nat) :
@[simp, grind =] theorem testBit_xor (x y i : Nat) :
(x ^^^ y).testBit i = ((x.testBit i) ^^ (y.testBit i)) := by
simp [HXor.hXor, Xor.xor, xor, testBit_bitwise ]
@[simp] theorem zero_xor (x : Nat) : 0 ^^^ x = x := by
@[simp, grind =] theorem zero_xor (x : Nat) : 0 ^^^ x = x := by
apply Nat.eq_of_testBit_eq
simp
@[simp] theorem xor_zero (x : Nat) : x ^^^ 0 = x := by
@[simp, grind =] theorem xor_zero (x : Nat) : x ^^^ 0 = x := by
apply Nat.eq_of_testBit_eq
simp
@[simp] protected theorem xor_self (x : Nat) : x ^^^ x = 0 := by
@[simp, grind =] protected theorem xor_self (x : Nat) : x ^^^ x = 0 := by
apply Nat.eq_of_testBit_eq
simp
@[grind =]
protected theorem xor_comm (x y : Nat) : x ^^^ y = y ^^^ x := by
apply Nat.eq_of_testBit_eq
simp [Bool.xor_comm]
@[grind _=_]
protected theorem xor_assoc (x y z : Nat) : (x ^^^ y) ^^^ z = x ^^^ (y ^^^ z) := by
apply Nat.eq_of_testBit_eq
simp
@@ -658,10 +678,12 @@ instance : Std.LawfulCommIdentity (α := Nat) (· ^^^ ·) 0 where
theorem xor_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ^^^ y < 2^n :=
bitwise_lt_two_pow left right
@[grind _=_]
theorem and_xor_distrib_right {a b c : Nat} : (a ^^^ b) &&& c = (a &&& c) ^^^ (b &&& c) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_xor_distrib_right]
@[grind _=_]
theorem and_xor_distrib_left {a b c : Nat} : a &&& (b ^^^ c) = (a &&& b) ^^^ (a &&& c) := by
apply Nat.eq_of_testBit_eq
simp [Bool.and_xor_distrib_left]
@@ -671,12 +693,15 @@ theorem and_xor_distrib_left {a b c : Nat} : a &&& (b ^^^ c) = (a &&& b) ^^^ (a
rw [testBit_xor]
simp
@[grind _=_]
theorem xor_div_two_pow : (a ^^^ b) / 2 ^ n = a / 2 ^ n ^^^ b / 2 ^ n :=
bitwise_div_two_pow
@[grind _=_]
theorem xor_div_two : (a ^^^ b) / 2 = a / 2 ^^^ b / 2 :=
xor_div_two_pow (n := 1)
@[grind _=_]
theorem xor_mod_two_pow : (a ^^^ b) % 2 ^ n = a % 2 ^ n ^^^ b % 2 ^ n :=
bitwise_mod_two_pow
@@ -713,6 +738,7 @@ theorem testBit_two_pow_mul_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat)
@[deprecated testBit_two_pow_mul_add (since := "2025-03-18")]
abbrev testBit_mul_pow_two_add := @testBit_two_pow_mul_add
@[grind =]
theorem testBit_two_pow_mul :
testBit (2 ^ i * a) j = (decide (j i) && testBit a (j-i)) := by
have gen := testBit_two_pow_mul_add a (Nat.two_pow_pos i) j
@@ -721,6 +747,11 @@ theorem testBit_two_pow_mul :
cases Nat.lt_or_ge j i with
| _ p => simp [p, Nat.not_le_of_lt, Nat.not_lt_of_le]
@[grind =] -- Ideally `grind` could do this just with `testBit_two_pow_mul`.
theorem testBit_mul_two_pow (x j i : Nat) :
(x * 2 ^ i).testBit j = (decide (i j) && x.testBit (j - i)) := by
rw [Nat.mul_comm, testBit_two_pow_mul]
@[deprecated testBit_two_pow_mul (since := "2025-03-18")]
abbrev testBit_mul_pow_two := @testBit_two_pow_mul
@@ -744,21 +775,17 @@ abbrev mul_add_lt_is_or := @two_pow_add_eq_or_of_lt
/-! ### shiftLeft and shiftRight -/
@[simp] theorem testBit_shiftLeft (x : Nat) : testBit (x <<< i) j =
@[simp, grind =] theorem testBit_shiftLeft (x : Nat) : testBit (x <<< i) j =
(decide (j i) && testBit x (j-i)) := by
simp [shiftLeft_eq, Nat.mul_comm _ (2^_), testBit_two_pow_mul]
@[simp] theorem testBit_shiftRight (x : Nat) : testBit (x >>> i) j = testBit x (i+j) := by
@[simp, grind =] theorem testBit_shiftRight (x : Nat) : testBit (x >>> i) j = testBit x (i+j) := by
simp [testBit, shiftRight_add]
@[simp] theorem shiftLeft_mod_two_eq_one : x <<< i % 2 = 1 i = 0 x % 2 = 1 := by
rw [mod_two_eq_one_iff_testBit_zero, testBit_shiftLeft]
simp
theorem testBit_mul_two_pow (x i n : Nat) :
(x * 2 ^ n).testBit i = (decide (n i) && x.testBit (i - n)) := by
rw [ testBit_shiftLeft, shiftLeft_eq]
theorem bitwise_mul_two_pow (of_false_false : f false false = false := by rfl) :
(bitwise f x y) * 2 ^ n = bitwise f (x * 2 ^ n) (y * 2 ^ n) := by
apply Nat.eq_of_testBit_eq
@@ -768,16 +795,20 @@ theorem bitwise_mul_two_pow (of_false_false : f false false = false := by rfl) :
· simp [hn]
· simp [hn, of_false_false]
@[grind _=_]
theorem shiftLeft_bitwise_distrib {a b : Nat} (of_false_false : f false false = false := by rfl) :
(bitwise f a b) <<< i = bitwise f (a <<< i) (b <<< i) := by
simp [shiftLeft_eq, bitwise_mul_two_pow of_false_false]
@[grind _=_]
theorem shiftLeft_and_distrib {a b : Nat} : (a &&& b) <<< i = a <<< i &&& b <<< i :=
shiftLeft_bitwise_distrib
@[grind _=_]
theorem shiftLeft_or_distrib {a b : Nat} : (a ||| b) <<< i = a <<< i ||| b <<< i :=
shiftLeft_bitwise_distrib
@[grind _=_]
theorem shiftLeft_xor_distrib {a b : Nat} : (a ^^^ b) <<< i = a <<< i ^^^ b <<< i :=
shiftLeft_bitwise_distrib
@@ -786,16 +817,20 @@ theorem shiftLeft_xor_distrib {a b : Nat} : (a ^^^ b) <<< i = a <<< i ^^^ b <<<
simp only [testBit, one_and_eq_mod_two, mod_two_bne_zero]
exact (Bool.beq_eq_decide_eq _ _).symm
@[grind _=_]
theorem shiftRight_bitwise_distrib {a b : Nat} (of_false_false : f false false = false := by rfl) :
(bitwise f a b) >>> i = bitwise f (a >>> i) (b >>> i) := by
simp [shiftRight_eq_div_pow, bitwise_div_two_pow of_false_false]
@[grind _=_]
theorem shiftRight_and_distrib {a b : Nat} : (a &&& b) >>> i = a >>> i &&& b >>> i :=
shiftRight_bitwise_distrib
@[grind _=_]
theorem shiftRight_or_distrib {a b : Nat} : (a ||| b) >>> i = a >>> i ||| b >>> i :=
shiftRight_bitwise_distrib
@[grind _=_]
theorem shiftRight_xor_distrib {a b : Nat} : (a ^^^ b) >>> i = a >>> i ^^^ b >>> i :=
shiftRight_bitwise_distrib

View File

@@ -9,6 +9,7 @@ prelude
import Init.WF
import Init.WFTactics
import Init.Data.Nat.Basic
meta import Init.MetaTypes
@[expose] section
@@ -75,7 +76,7 @@ private theorem div.go.fuel_congr (x y fuel1 fuel2 : Nat) (hy : 0 < y) (h1 : x <
termination_by structural fuel1
theorem div_eq (x y : Nat) : x / y = if 0 < y y x then (x - y) / y + 1 else 0 := by
show Nat.div _ _ = ite _ (Nat.div _ _ + 1) _
change Nat.div _ _ = ite _ (Nat.div _ _ + 1) _
unfold Nat.div
split
next =>
@@ -257,7 +258,7 @@ protected def mod : @& Nat → @& Nat → Nat
instance instMod : Mod Nat := Nat.mod
protected theorem modCore_eq_mod (n m : Nat) : Nat.modCore n m = n % m := by
show Nat.modCore n m = Nat.mod n m
change Nat.modCore n m = Nat.mod n m
match n, m with
| 0, _ =>
rw [Nat.modCore_eq]
@@ -521,7 +522,7 @@ theorem mul_sub_div (x n p : Nat) (h₁ : x < n*p) : (n * p - (x + 1)) / n = p -
rw [Nat.mul_sub_right_distrib, Nat.mul_comm]
exact Nat.sub_le_sub_left ((div_lt_iff_lt_mul npos).1 (lt_succ_self _)) _
focus
show succ (pred (n * p - x)) (succ (pred (p - x / n))) * n
change succ (pred (n * p - x)) (succ (pred (p - x / n))) * n
rw [succ_pred_eq_of_pos (Nat.sub_pos_of_lt h₁),
fun h => succ_pred_eq_of_pos (Nat.sub_pos_of_lt h)] -- TODO: why is the function needed?
focus

View File

@@ -210,4 +210,19 @@ theorem mod_mod_eq_mod_mod_mod_of_dvd {a b c : Nat} (hb : b c) :
have : b < c := Nat.lt_of_le_of_ne (Nat.le_of_dvd hc hb) hb'
rw [Nat.mod_mod_of_dvd' hb, Nat.mod_eq_of_lt this, Nat.mod_mod_of_dvd _ hb]
theorem mod_eq_mod_iff {x y z : Nat} :
x % z = y % z k₁ k₂, x + k₁ * z = y + k₂ * z := by
constructor
· rw [Nat.mod_def, Nat.mod_def]
rw [Nat.sub_eq_iff_eq_add, Nat.add_comm, Nat.add_sub_assoc, eq_comm, Nat.sub_eq_iff_eq_add, eq_comm]
· intro h
refine (y / z), (x / z), ?_
rwa [Nat.mul_comm z, Nat.add_comm _ y, Nat.mul_comm z] at h
· exact le_add_left_of_le (mul_div_le y z)
· exact mul_div_le y z
· exact mul_div_le x z
· rintro k₁, k₂, h
replace h := congrArg (· % z) h
simpa using h
end Nat

View File

@@ -1772,6 +1772,12 @@ instance decidableExistsLE' {p : (m : Nat) → m ≤ k → Prop} [I : ∀ m h, D
intro
exact fun h, w => le_of_lt_succ h, w, fun h, w => lt_add_one_of_le h, w
instance decidableExistsFin (P : Fin n Prop) [DecidablePred P] : Decidable ( i, P i) :=
decidable_of_iff ( k, k < n ((h: k < n) P k, h))
fun k, a => Exists.intro k, a.left (a.right a.left),
fun i, e => Exists.intro i.val i.isLt, fun _ => e
/-! ### Results about `List.sum` specialized to `Nat` -/
protected theorem sum_pos_iff_exists_pos {l : List Nat} : 0 < l.sum x l, 0 < x := by

View File

@@ -149,7 +149,7 @@ instance : LawfulBEq PolyCnstr where
rw [h₁, h₂, h₃]
rfl {a} := by
cases a; rename_i eq lhs rhs
show (eq == eq && (lhs == lhs && rhs == rhs)) = true
change (eq == eq && (lhs == lhs && rhs == rhs)) = true
simp
structure ExprCnstr where

View File

@@ -85,4 +85,4 @@ theorem Membership.get_elem_helper {i n : Nat} {r : Std.Range} (h₁ : i ∈ r)
i < n := h₂ h₁.2.1
macro_rules
| `(tactic| get_elem_tactic_trivial) => `(tactic| apply Membership.get_elem_helper; assumption; rfl)
| `(tactic| get_elem_tactic_extensible) => `(tactic| apply Membership.get_elem_helper; assumption; rfl)

View File

@@ -342,11 +342,12 @@ instance : Repr Int where
def hexDigitRepr (n : Nat) : String :=
String.singleton <| Nat.digitChar n
def Char.quoteCore (c : Char) : String :=
def Char.quoteCore (c : Char) (inString : Bool := false) : String :=
if c = '\n' then "\\n"
else if c = '\t' then "\\t"
else if c = '\\' then "\\\\"
else if c = '\"' then "\\\""
else if !inString && c = '\'' then "\\\'"
else if c.toNat <= 31 c = '\x7f' then "\\x" ++ smallCharToHex c
else String.singleton c
where
@@ -383,7 +384,7 @@ Examples:
-/
def String.quote (s : String) : String :=
if s.isEmpty then "\"\""
else s.foldl (fun s c => s ++ c.quoteCore) "\"" ++ "\""
else s.foldl (fun s c => s ++ c.quoteCore (inString := true)) "\"" ++ "\""
instance : Repr String where
reprPrec s _ := s.quote

View File

@@ -9,8 +9,8 @@ prelude
import all Init.Data.Nat.Bitwise.Basic
import all Init.Data.SInt.Basic
import all Init.Data.BitVec.Basic
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Bitblast
import all Init.Data.BitVec.Lemmas
import Init.Data.Int.LemmasAux
import all Init.Data.UInt.Basic
import Init.Data.UInt.Lemmas

View File

@@ -586,7 +586,7 @@ decreasing_by
focus
rename_i i₀ j₀ _ eq h'
rw [show (s.next i₀ - sep.next j₀).1 = (i₀ - j₀).1 by
show (_ + Char.utf8Size _) - (_ + Char.utf8Size _) = _
change (_ + Char.utf8Size _) - (_ + Char.utf8Size _) = _
rw [(beq_iff_eq ..).1 eq, Nat.add_sub_add_right]; rfl]
right; exact Nat.sub_lt_sub_left
(Nat.lt_of_le_of_lt (Nat.le_add_right ..) (Nat.gt_of_not_le (mt decide_eq_true h')))

View File

@@ -295,11 +295,11 @@ where
termination_by text.utf8ByteSize - pos.byteIdx
decreasing_by
decreasing_with
show text.utf8ByteSize - (text.next (text.next pos)).byteIdx < text.utf8ByteSize - pos.byteIdx
change text.utf8ByteSize - (text.next (text.next pos)).byteIdx < text.utf8ByteSize - pos.byteIdx
have k := Nat.gt_of_not_le <| mt decide_eq_true h
exact Nat.sub_lt_sub_left k (Nat.lt_trans (String.lt_next text pos) (String.lt_next _ _))
decreasing_with
show text.utf8ByteSize - (text.next pos).byteIdx < text.utf8ByteSize - pos.byteIdx
change text.utf8ByteSize - (text.next pos).byteIdx < text.utf8ByteSize - pos.byteIdx
have k := Nat.gt_of_not_le <| mt decide_eq_true h
exact Nat.sub_lt_sub_left k (String.lt_next _ _)

View File

@@ -6,8 +6,7 @@ Author: Leonardo de Moura
module
prelude
import Init.Meta
import Init.Data.ToString.Basic
meta import Init.Meta
syntax:max "s!" interpolatedStr(term) : term

View File

@@ -10,10 +10,9 @@ import all Init.Data.UInt.Basic
import all Init.Data.UInt.BasicAux
import Init.Data.Fin.Lemmas
import all Init.Data.Fin.Bitwise
import all Init.Data.BitVec.Basic
import all Init.Data.BitVec.BasicAux
import all Init.Data.BitVec.Basic
import Init.Data.BitVec.Lemmas
import Init.Data.BitVec.Bitblast
import Init.Data.Nat.Div.Lemmas
import Init.System.Platform

View File

@@ -7,6 +7,7 @@ Authors: Shreyas Srinivas, François G. Dorais, Kim Morrison
module
prelude
meta import Init.Coe
import Init.Data.Array.Lemmas
import Init.Data.Array.MapIdx
import Init.Data.Array.InsertIdx

View File

@@ -40,6 +40,7 @@ theorem countP_push {a : α} {xs : Vector α n} : countP p (xs.push a) = countP
rcases xs with xs, rfl
simp [Array.countP_push]
@[grind =]
theorem countP_singleton {a : α} : countP p #v[a] = if p a then 1 else 0 := by
simp
@@ -51,7 +52,7 @@ theorem countP_le_size {xs : Vector α n} : countP p xs ≤ n := by
rcases xs with xs, rfl
simp [Array.countP_le_size (p := p)]
@[simp] theorem countP_append {xs : Vector α n} {ys : Vector α m} : countP p (xs ++ ys) = countP p xs + countP p ys := by
@[simp, grind =] theorem countP_append {xs : Vector α n} {ys : Vector α m} : countP p (xs ++ ys) = countP p xs + countP p ys := by
cases xs
cases ys
simp
@@ -116,7 +117,7 @@ theorem countP_flatMap {p : β → Bool} {xs : Vector α n} {f : α → Vector
rcases xs with xs, rfl
simp [Array.countP_flatMap, Function.comp_def]
@[simp] theorem countP_reverse {xs : Vector α n} : countP p xs.reverse = countP p xs := by
@[simp, grind =] theorem countP_reverse {xs : Vector α n} : countP p xs.reverse = countP p xs := by
rcases xs with xs, rfl
simp
@@ -136,7 +137,7 @@ section count
variable [BEq α]
@[simp] theorem count_empty {a : α} : count a #v[] = 0 := rfl
@[simp, grind =] theorem count_empty {a : α} : count a #v[] = 0 := rfl
theorem count_push {a b : α} {xs : Vector α n} :
count a (xs.push b) = count a xs + if b == a then 1 else 0 := by
@@ -151,23 +152,25 @@ theorem count_eq_countP' {a : α} : count (n := n) a = countP (· == a) := by
theorem count_le_size {a : α} {xs : Vector α n} : count a xs n := countP_le_size
grind_pattern count_le_size => count a xs
theorem count_le_count_push {a b : α} {xs : Vector α n} : count a xs count a (xs.push b) := by
rcases xs with xs, rfl
simp [Array.count_push]
@[simp] theorem count_singleton {a b : α} : count a #v[b] = if b == a then 1 else 0 := by
@[simp, grind =] theorem count_singleton {a b : α} : count a #v[b] = if b == a then 1 else 0 := by
simp [count_eq_countP]
@[simp] theorem count_append {a : α} {xs : Vector α n} {ys : Vector α m} :
@[simp, grind =] theorem count_append {a : α} {xs : Vector α n} {ys : Vector α m} :
count a (xs ++ ys) = count a xs + count a ys :=
countP_append ..
@[simp] theorem count_flatten {a : α} {xss : Vector (Vector α m) n} :
@[simp, grind =] theorem count_flatten {a : α} {xss : Vector (Vector α m) n} :
count a xss.flatten = (xss.map (count a)).sum := by
rcases xss with xss, rfl
simp [Array.count_flatten, Function.comp_def]
@[simp] theorem count_reverse {a : α} {xs : Vector α n} : count a xs.reverse = count a xs := by
@[simp, grind =] theorem count_reverse {a : α} {xs : Vector α n} : count a xs.reverse = count a xs := by
rcases xs with xs, rfl
simp

View File

@@ -22,11 +22,13 @@ open Nat
/-! ### eraseIdx -/
@[grind =]
theorem eraseIdx_eq_take_drop_succ {xs : Vector α n} {i : Nat} (h) :
xs.eraseIdx i = (xs.take i ++ xs.drop (i + 1)).cast (by omega) := by
rcases xs with xs, rfl
simp [Array.eraseIdx_eq_take_drop_succ, *]
@[grind =]
theorem getElem?_eraseIdx {xs : Vector α n} {i : Nat} (h : i < n) {j : Nat} :
(xs.eraseIdx i)[j]? = if j < i then xs[j]? else xs[j + 1]? := by
rcases xs with xs, rfl
@@ -44,12 +46,14 @@ theorem getElem?_eraseIdx_of_ge {xs : Vector α n} {i : Nat} (h : i < n) {j : Na
intro h'
omega
@[grind =]
theorem getElem_eraseIdx {xs : Vector α n} {i : Nat} (h : i < n) {j : Nat} (h' : j < n - 1) :
(xs.eraseIdx i)[j] = if h'' : j < i then xs[j] else xs[j + 1] := by
apply Option.some.inj
rw [ getElem?_eq_getElem, getElem?_eraseIdx]
split <;> simp
@[grind ]
theorem mem_of_mem_eraseIdx {xs : Vector α n} {i : Nat} {h} {a : α} (h : a xs.eraseIdx i) : a xs := by
rcases xs with xs, rfl
simpa using Array.mem_of_mem_eraseIdx (by simpa using h)
@@ -64,13 +68,23 @@ theorem eraseIdx_append_of_length_le {xs : Vector α n} {k : Nat} (hk : n ≤ k)
eraseIdx (xs ++ xs') k = (xs ++ eraseIdx xs' (k - n)).cast (by omega) := by
rcases xs with xs
rcases xs' with xs'
simp [Array.eraseIdx_append_of_length_le, *]
simp [Array.eraseIdx_append_of_size_le, *]
@[grind =]
theorem eraseIdx_append {xs : Vector α n} {ys : Vector α m} {k : Nat} (h : k < n + m) :
eraseIdx (xs ++ ys) k = if h' : k < n then (eraseIdx xs k ++ ys).cast (by omega) else (xs ++ eraseIdx ys (k - n) (by omega)).cast (by omega) := by
rcases xs with xs, rfl
rcases ys with ys, rfl
simp only [mk_append_mk, eraseIdx_mk, Array.eraseIdx_append]
split <;> simp
@[grind = ]
theorem eraseIdx_cast {xs : Vector α n} {k : Nat} (h : k < m) :
eraseIdx (xs.cast w) k h = (eraseIdx xs k).cast (by omega) := by
rcases xs with xs
simp
@[grind =]
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by
rw [replicate_eq_mk_replicate, eraseIdx_mk]
@@ -112,6 +126,45 @@ theorem eraseIdx_set_gt {xs : Vector α n} {i : Nat} {j : Nat} {a : α} (h : i <
rcases xs with xs
simp [Array.eraseIdx_set_gt, *]
@[grind =]
theorem eraseIdx_set {xs : Vector α n} {i : Nat} {a : α} {hi : i < n} {j : Nat} {hj : j < n} :
(xs.set i a).eraseIdx j =
if h' : j < i then
(xs.eraseIdx j).set (i - 1) a
else if h'' : j = i then
xs.eraseIdx i
else
(xs.eraseIdx j).set i a := by
rcases xs with xs
simp only [set_mk, eraseIdx_mk, Array.eraseIdx_set]
split
· simp
· split <;> simp
theorem set_eraseIdx_le {xs : Vector α n} {i : Nat} {w : i < n} {j : Nat} {a : α} (h : i j) (hj : j < n - 1) :
(xs.eraseIdx i).set j a = (xs.set (j + 1) a).eraseIdx i := by
rw [eraseIdx_set_lt]
· simp
· omega
theorem set_eraseIdx_gt {xs : Vector α n} {i : Nat} {w : i < n} {j : Nat} {a : α} (h : j < i) (hj : j < n - 1) :
(xs.eraseIdx i).set j a = (xs.set j a).eraseIdx i := by
rw [eraseIdx_set_gt]
omega
@[grind =]
theorem set_eraseIdx {xs : Vector α n} {i : Nat} {w : i < n} {j : Nat} {a : α} (hj : j < n - 1) :
(xs.eraseIdx i).set j a =
if h' : i j then
(xs.set (j + 1) a).eraseIdx i
else
(xs.set j a).eraseIdx i := by
split <;> rename_i h'
· rw [set_eraseIdx_le]
omega
· rw [set_eraseIdx_gt]
omega
@[simp] theorem set_getElem_succ_eraseIdx_succ
{xs : Vector α n} {i : Nat} (h : i + 1 < n) :
(xs.eraseIdx (i + 1)).set i xs[i + 1] = xs.eraseIdx i := by

View File

@@ -28,19 +28,19 @@ set_option linter.indexVariables false
rcases xs with as, rfl
simp [h]
@[simp]
@[simp, grind =]
theorem extract_push {xs : Vector α n} {b : α} {start stop : Nat} (h : stop n) :
(xs.push b).extract start stop = (xs.extract start stop).cast (by omega) := by
rcases xs with xs, rfl
simp [h]
@[simp]
@[simp, grind =]
theorem extract_eq_pop {xs : Vector α n} {stop : Nat} (h : stop = n - 1) :
xs.extract 0 stop = xs.pop.cast (by omega) := by
rcases xs with xs, rfl
simp [h]
@[simp]
@[simp, grind _=_]
theorem extract_append_extract {xs : Vector α n} {i j k : Nat} :
xs.extract i j ++ xs.extract j k =
(xs.extract (min i j) (max j k)).cast (by omega) := by
@@ -79,11 +79,12 @@ theorem getElem?_extract_of_succ {xs : Vector α n} {j : Nat} :
· rw [if_neg (by omega)]
simp_all
@[simp] theorem extract_extract {xs : Vector α n} {i j k l : Nat} :
@[simp, grind =] theorem extract_extract {xs : Vector α n} {i j k l : Nat} :
(xs.extract i j).extract k l = (xs.extract (i + k) (min (i + l) j)).cast (by omega) := by
rcases xs with xs, rfl
simp
@[grind =]
theorem extract_set {xs : Vector α n} {i j k : Nat} (h : k < n) {a : α} :
(xs.set k a).extract i j =
if _ : k < i then
@@ -97,12 +98,13 @@ theorem extract_set {xs : Vector α n} {i j k : Nat} (h : k < n) {a : α} :
· simp
· split <;> simp
@[grind =]
theorem set_extract {xs : Vector α n} {i j k : Nat} (h : k < min j n - i) {a : α} :
(xs.extract i j).set k a = (xs.set (i + k) a).extract i j := by
rcases xs with xs, rfl
simp [Array.set_extract]
@[simp]
@[simp, grind =]
theorem extract_append {xs : Vector α n} {ys : Vector α m} {i j : Nat} :
(xs ++ ys).extract i j =
(xs.extract i j ++ ys.extract (i - n) (j - n)).cast (by omega) := by
@@ -128,12 +130,12 @@ theorem extract_append_left {xs : Vector α n} {ys : Vector α m} :
congr 1
omega
@[simp] theorem map_extract {xs : Vector α n} {i j : Nat} :
@[simp, grind =] theorem map_extract {xs : Vector α n} {i j : Nat} :
(xs.extract i j).map f = (xs.map f).extract i j := by
rcases xs with xs, rfl
simp
@[simp] theorem extract_replicate {a : α} {n i j : Nat} :
@[simp, grind =] theorem extract_replicate {a : α} {n i j : Nat} :
(replicate n a).extract i j = replicate (min j n - i) a := by
ext i h
simp
@@ -161,6 +163,7 @@ theorem set_eq_push_extract_append_extract {xs : Vector α n} {i : Nat} (h : i <
rcases xs with as, rfl
simp [Array.set_eq_push_extract_append_extract, h]
@[grind =]
theorem extract_reverse {xs : Vector α n} {i j : Nat} :
xs.reverse.extract i j = (xs.extract (n - j) (n - i)).reverse.cast (by omega) := by
ext i h
@@ -168,6 +171,7 @@ theorem extract_reverse {xs : Vector α n} {i j : Nat} :
congr 1
omega
@[grind =]
theorem reverse_extract {xs : Vector α n} {i j : Nat} :
(xs.extract i j).reverse = (xs.reverse.extract (n - j) (n - i)).cast (by omega) := by
rcases xs with xs, rfl

View File

@@ -17,7 +17,7 @@ namespace Vector
/-- `finRange n` is the vector of all elements of `Fin n` in order. -/
protected def finRange (n : Nat) : Vector (Fin n) n := ofFn fun i => i
@[simp] theorem getElem_finRange {i : Nat} (h : i < n) :
@[simp, grind =] theorem getElem_finRange {i : Nat} (h : i < n) :
(Vector.finRange n)[i] = i, h := by
simp [Vector.finRange]
@@ -39,6 +39,7 @@ theorem finRange_succ_last {n} :
· simp_all
omega
@[grind _=_]
theorem finRange_reverse {n} : (Vector.finRange n).reverse = (Vector.finRange n).map Fin.rev := by
ext i h
simp

View File

@@ -44,12 +44,23 @@ theorem findSome?_singleton {a : α} {f : α → Option β} : #v[a].findSome? f
rcases xs with xs, rfl
simp only [push_mk, findSomeRev?_mk, Array.findSomeRev?_push_of_isNone, h]
@[grind =]
theorem findSomeRev?_push {xs : Vector α n} {a : α} {f : α Option β} :
(xs.push a).findSomeRev? f = (f a).or (xs.findSomeRev? f) := by
match h : f a with
| some b =>
rw [findSomeRev?_push_of_isSome]
all_goals simp_all
| none =>
rw [findSomeRev?_push_of_isNone]
all_goals simp_all
theorem exists_of_findSome?_eq_some {f : α Option β} {xs : Vector α n} (w : xs.findSome? f = some b) :
a, a xs f a = some b := by
rcases xs with xs, rfl
simpa using Array.exists_of_findSome?_eq_some (by simpa using w)
@[simp] theorem findSome?_eq_none_iff {f : α Option β} {xs : Vector α n} :
@[simp, grind =] theorem findSome?_eq_none_iff {f : α Option β} {xs : Vector α n} :
xs.findSome? f = none x xs, f x = none := by
rcases xs with xs, rfl
simp
@@ -72,24 +83,27 @@ theorem findSome?_eq_some_iff {f : α → Option β} {xs : Vector α n} {b : β}
· rintro k₁, k₂, h, ys, a, zs, w, h₁, h₂
exact ys.toArray, a, zs.toArray, by simp [w], h₁, by simpa using h₂
@[simp] theorem findSome?_guard {xs : Vector α n} : findSome? (Option.guard fun x => p x) xs = find? p xs := by
@[simp, grind =] theorem findSome?_guard {xs : Vector α n} : findSome? (Option.guard p) xs = find? p xs := by
rcases xs with xs, rfl
simp
theorem find?_eq_findSome?_guard {xs : Vector α n} : find? p xs = findSome? (Option.guard fun x => p x) xs :=
theorem find?_eq_findSome?_guard {xs : Vector α n} : find? p xs = findSome? (Option.guard p) xs :=
findSome?_guard.symm
@[simp] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Vector α n} :
@[simp, grind =] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Vector α n} :
(xs.findSome? f).map g = xs.findSome? (Option.map g f) := by
cases xs; simp
@[grind _=_]
theorem findSome?_map {f : β γ} {xs : Vector β n} : findSome? p (xs.map f) = xs.findSome? (p f) := by
rcases xs with xs, rfl
simp [Array.findSome?_map]
@[grind =]
theorem findSome?_append {xs : Vector α n₁} {ys : Vector α n₂} : (xs ++ ys).findSome? f = (xs.findSome? f).or (ys.findSome? f) := by
cases xs; cases ys; simp [Array.findSome?_append]
@[grind =]
theorem getElem?_zero_flatten {xss : Vector (Vector α m) n} :
(flatten xss)[0]? = xss.findSome? fun xs => xs[0]? := by
cases xss using vector₂_induction
@@ -106,12 +120,14 @@ theorem getElem_zero_flatten.proof {xss : Vector (Vector α m) n} (h : 0 < n * m
Option.isSome_some, and_true]
exact xss[0], h₂ _ (by simp), by simp
@[grind =]
theorem getElem_zero_flatten {xss : Vector (Vector α m) n} (h : 0 < n * m) :
(flatten xss)[0] = (xss.findSome? fun xs => xs[0]?).get (getElem_zero_flatten.proof h) := by
have t := getElem?_zero_flatten (xss := xss)
simp [getElem?_eq_getElem, h] at t
simp [ t]
@[grind =]
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
rw [replicate_eq_mk_replicate, findSome?_mk, Array.findSome?_replicate]
@@ -142,9 +158,9 @@ abbrev findSome?_mkVector_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@[simp] theorem find?_empty : find? p #v[] = none := rfl
@[simp, grind =] theorem find?_empty : find? p #v[] = none := rfl
theorem find?_singleton {a : α} {p : α Bool} :
@[grind =]theorem find?_singleton {a : α} {p : α Bool} :
#v[a].find? p = if p a then some a else none := by
simp
@@ -152,11 +168,23 @@ theorem find?_singleton {a : α} {p : α → Bool} :
findRev? p (xs.push a) = some a := by
cases xs; simp [h]
@[simp] theorem findRev?_cons_of_neg {xs : Vector α n} (h : ¬p a) :
@[simp] theorem findRev?_push_of_neg {xs : Vector α n} (h : ¬p a) :
findRev? p (xs.push a) = findRev? p xs := by
cases xs; simp [h]
@[simp] theorem find?_eq_none : find? p l = none x l, ¬ p x := by
@[deprecated findRev?_push_of_neg (since := "2025-06-12")]
abbrev findRev?_cons_of_neg := @findRev?_push_of_neg
@[grind =]
theorem finRev?_push {xs : Vector α n} :
findRev? p (xs.push a) = (Option.guard p a).or (xs.findRev? p) := by
cases h : p a
· rw [findRev?_push_of_neg, Option.guard_eq_none_iff.mpr h]
all_goals simp [h]
· rw [findRev?_push_of_pos, Option.guard_eq_some_iff.mpr rfl, h]
all_goals simp [h]
@[simp, grind =] theorem find?_eq_none : find? p l = none x l, ¬ p x := by
cases l; simp
theorem find?_eq_some_iff_append {xs : Vector α n} :
@@ -181,35 +209,38 @@ theorem find?_push_eq_some {xs : Vector α n} :
(xs.push a).find? p = some b xs.find? p = some b (xs.find? p = none (p a a = b)) := by
cases xs; simp
@[simp] theorem find?_isSome {xs : Vector α n} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
@[simp, grind =] theorem find?_isSome {xs : Vector α n} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
cases xs; simp
@[grind ]
theorem find?_some {xs : Vector α n} (h : find? p xs = some a) : p a := by
rcases xs with xs, rfl
simp at h
exact Array.find?_some h
@[grind ]
theorem mem_of_find?_eq_some {xs : Vector α n} (h : find? p xs = some a) : a xs := by
cases xs
simp at h
simpa using Array.mem_of_find?_eq_some h
@[grind]
theorem get_find?_mem {xs : Vector α n} (h) : (xs.find? p).get h xs := by
cases xs
simp [Array.get_find?_mem]
@[simp] theorem find?_map {f : β α} {xs : Vector β n} :
@[simp, grind =] theorem find?_map {f : β α} {xs : Vector β n} :
find? p (xs.map f) = (xs.find? (p f)).map f := by
cases xs; simp
@[simp] theorem find?_append {xs : Vector α n₁} {ys : Vector α n₂} :
@[simp, grind =] theorem find?_append {xs : Vector α n₁} {ys : Vector α n₂} :
(xs ++ ys).find? p = (xs.find? p).or (ys.find? p) := by
cases xs
cases ys
simp
@[simp] theorem find?_flatten {xs : Vector (Vector α m) n} {p : α Bool} :
xs.flatten.find? p = xs.findSome? (·.find? p) := by
@[simp, grind =] theorem find?_flatten {xs : Vector (Vector α m) n} {p : α Bool} :
xs.flatten.find? p = xs.findSome? (find? p) := by
cases xs using vector₂_induction
simp [Array.findSome?_map, Function.comp_def]
@@ -217,7 +248,7 @@ theorem find?_flatten_eq_none_iff {xs : Vector (Vector α m) n} {p : α → Bool
xs.flatten.find? p = none ys xs, x ys, !p x := by
simp
@[simp] theorem find?_flatMap {xs : Vector α n} {f : α Vector β m} {p : β Bool} :
@[simp, grind =] theorem find?_flatMap {xs : Vector α n} {f : α Vector β m} {p : β Bool} :
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
cases xs
simp [Array.find?_flatMap, Array.flatMap_toArray]
@@ -227,6 +258,7 @@ theorem find?_flatMap_eq_none_iff {xs : Vector α n} {f : α → Vector β m} {p
(xs.flatMap f).find? p = none x xs, y f x, !p y := by
simp
@[grind =]
theorem find?_replicate :
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
rw [replicate_eq_mk_replicate, find?_mk, Array.find?_replicate]
@@ -278,6 +310,7 @@ abbrev find?_mkVector_eq_some_iff := @find?_replicate_eq_some_iff
@[deprecated get_find?_replicate (since := "2025-03-18")]
abbrev get_find?_mkVector := @get_find?_replicate
@[grind =]
theorem find?_pmap {P : α Prop} {f : (a : α) P a β} {xs : Vector α n}
(H : (a : α), a xs P a) {p : β Bool} :
(xs.pmap f H).find? p = (xs.attach.find? (fun a, m => p (f a (H a m)))).map fun a, m => f a (H a m) := by
@@ -291,8 +324,10 @@ theorem find?_eq_some_iff_getElem {xs : Vector α n} {p : α → Bool} {b : α}
/-! ### findFinIdx? -/
@[grind =]
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p (#v[] : Vector α 0) = none := by simp
@[grind =]
theorem findFinIdx?_singleton {a : α} {p : α Bool} :
#[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp
@@ -302,6 +337,12 @@ theorem findFinIdx?_singleton {a : α} {p : α → Bool} :
subst w
simp
@[simp, grind =] theorem findFinIdx?_eq_none_iff {xs : Vector α n} {p : α Bool} :
xs.findFinIdx? p = none x, x xs ¬ p x := by
rcases xs with xs, rfl
simp [Array.findFinIdx?_eq_none_iff]
@[grind =]
theorem findFinIdx?_push {xs : Vector α n} {a : α} {p : α Bool} :
(xs.push a).findFinIdx? p =
((xs.findFinIdx? p).map Fin.castSucc).or (if p a then some n, by simp else none) := by
@@ -309,6 +350,7 @@ theorem findFinIdx?_push {xs : Vector α n} {a : α} {p : α → Bool} :
simp [Array.findFinIdx?_push, Option.map_or, Function.comp_def]
congr
@[grind =]
theorem findFinIdx?_append {xs : Vector α n₁} {ys : Vector α n₂} {p : α Bool} :
(xs ++ ys).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or
@@ -317,13 +359,13 @@ theorem findFinIdx?_append {xs : Vector α n₁} {ys : Vector α n₂} {p : α
rcases ys with ys, rfl
simp [Array.findFinIdx?_append, Option.map_or, Function.comp_def]
@[simp]
@[simp, grind =]
theorem isSome_findFinIdx? {xs : Vector α n} {p : α Bool} :
(xs.findFinIdx? p).isSome = xs.any p := by
rcases xs with xs, rfl
simp
@[simp]
@[simp, grind =]
theorem isNone_findFinIdx? {xs : Vector α n} {p : α Bool} :
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
rcases xs with xs, rfl

View File

@@ -30,15 +30,20 @@ section InsertIdx
variable {a : α}
@[simp]
@[simp, grind =]
theorem insertIdx_zero {xs : Vector α n} {x : α} : xs.insertIdx 0 x = (#v[x] ++ xs).cast (by omega) := by
cases xs
simp
theorem eraseIdx_insertIdx {i : Nat} {xs : Vector α n} {h : i n} :
theorem eraseIdx_insertIdx_self {i : Nat} {xs : Vector α n} {h : i n} :
(xs.insertIdx i a).eraseIdx i = xs := by
rcases xs with xs, rfl
simp_all [Array.eraseIdx_insertIdx]
simp_all [Array.eraseIdx_insertIdx_self]
@[deprecated eraseIdx_insertIdx_self (since := "2025-06-15")]
theorem eraseIdx_insertIdx {i : Nat} {xs : Vector α n} {h : i n} :
(xs.insertIdx i a).eraseIdx i = xs := by
simp [eraseIdx_insertIdx_self]
theorem insertIdx_eraseIdx_of_ge {xs : Vector α n}
(w₁ : i < n) (w₂ : j n - 1) (h : i j) :
@@ -54,6 +59,18 @@ theorem insertIdx_eraseIdx_of_le {xs : Vector α n}
rcases xs with as, rfl
simpa using Array.insertIdx_eraseIdx_of_le (by simpa) (by simpa) (by simpa)
@[grind =]
theorem insertIdx_eraseIdx {as : Vector α n} (h₁ : i < n) (h₂ : j n - 1) :
(as.eraseIdx i).insertIdx j a =
if h : i j then
((as.insertIdx (j + 1) a).eraseIdx i).cast (by omega)
else
((as.insertIdx j a).eraseIdx (i + 1) (by simp_all)).cast (by omega) := by
split <;> rename_i h'
· rw [insertIdx_eraseIdx_of_ge] <;> omega
· rw [insertIdx_eraseIdx_of_le] <;> omega
@[grind =]
theorem insertIdx_comm (a b : α) {i j : Nat} {xs : Vector α n} (_ : i j) (_ : j n) :
(xs.insertIdx i a).insertIdx (j + 1) b =
(xs.insertIdx j b).insertIdx i a := by
@@ -70,6 +87,7 @@ theorem insertIdx_size_self {xs : Vector α n} {x : α} : xs.insertIdx n x = xs.
rcases xs with as, rfl
simp
@[grind =]
theorem getElem_insertIdx {xs : Vector α n} {x : α} {i k : Nat} (w : i n) (h : k < n + 1) :
(xs.insertIdx i x)[k] =
if h₁ : k < i then
@@ -98,6 +116,7 @@ theorem getElem_insertIdx_of_gt {xs : Vector α n} {x : α} {i k : Nat} (w : k
simp [Array.getElem_insertIdx, w, h]
rw [dif_neg (by omega), dif_neg (by omega)]
@[grind =]
theorem getElem?_insertIdx {xs : Vector α n} {x : α} {i k : Nat} (h : i n) :
(xs.insertIdx i x)[k]? =
if k < i then

View File

@@ -2538,23 +2538,23 @@ theorem foldr_hom (f : β₁ → β₂) {g₁ : α → β₁ → β₁} {g₂ :
rw [Array.foldr_hom _ H]
/--
We can prove that two folds over the same array are related (by some arbitrary relation)
if we know that the initial elements are related and the folding function, for each element of the array,
preserves the relation.
We can prove that two folds over the same vector are related (by some arbitrary relation)
if we know that the initial elements are related and the folding function, for each element of the
vector, preserves the relation.
-/
theorem foldl_rel {xs : Vector α n} {f g : β α β} {a b : β} {r : β β Prop}
(h : r a b) (h' : (a : α), a xs (c c' : β), r c c' r (f c a) (g c' a)) :
theorem foldl_rel {xs : Vector α n} {f : β α β} {g : γ α γ} {a : β} {b : γ} {r : β γ Prop}
(h : r a b) (h' : (a : α), a xs (c : β) (c' : γ), r c c' r (f c a) (g c' a)) :
r (xs.foldl (fun acc a => f acc a) a) (xs.foldl (fun acc a => g acc a) b) := by
rcases xs with xs, rfl
simpa using Array.foldl_rel h (by simpa using h')
/--
We can prove that two folds over the same array are related (by some arbitrary relation)
if we know that the initial elements are related and the folding function, for each element of the array,
preserves the relation.
We can prove that two folds over the same vector are related (by some arbitrary relation)
if we know that the initial elements are related and the folding function, for each element of the
vector, preserves the relation.
-/
theorem foldr_rel {xs : Vector α n} {f g : α β β} {a b : β} {r : β β Prop}
(h : r a b) (h' : (a : α), a xs (c c' : β), r c c' r (f a c) (g a c')) :
theorem foldr_rel {xs : Vector α n} {f : α β β} {g : α γ γ} {a : β} {b : γ} {r : β γ Prop}
(h : r a b) (h' : (a : α), a xs (c : β) (c' : γ), r c c' r (f a c) (g a c')) :
r (xs.foldr (fun a acc => f a acc) a) (xs.foldr (fun a acc => g a acc) b) := by
rcases xs with xs, rfl
simpa using Array.foldr_rel h (by simpa using h')
@@ -3076,7 +3076,7 @@ theorem getElem_push_last {xs : Vector α n} {x : α} : (xs.push x)[n] = x := by
/-! ### zipWith -/
@[simp] theorem getElem_zipWith {f : α β γ} {as : Vector α n} {bs : Vector β n} {i : Nat}
@[simp, grind =] theorem getElem_zipWith {f : α β γ} {as : Vector α n} {bs : Vector β n} {i : Nat}
(hi : i < n) : (zipWith f as bs)[i] = f as[i] bs[i] := by
cases as
cases bs

View File

@@ -19,13 +19,13 @@ namespace Vector
/-! ### mapFinIdx -/
@[simp] theorem getElem_mapFinIdx {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} {i : Nat}
@[simp, grind =] theorem getElem_mapFinIdx {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} {i : Nat}
(h : i < n) :
(xs.mapFinIdx f)[i] = f i xs[i] h := by
rcases xs with xs, rfl
simp
@[simp] theorem getElem?_mapFinIdx {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} {i : Nat} :
@[simp, grind =] theorem getElem?_mapFinIdx {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} {i : Nat} :
(xs.mapFinIdx f)[i]? =
xs[i]?.pbind fun b h => some <| f i b (getElem?_eq_some_iff.1 h).1 := by
simp only [getElem?_def, getElem_mapFinIdx]
@@ -33,12 +33,12 @@ namespace Vector
/-! ### mapIdx -/
@[simp] theorem getElem_mapIdx {f : Nat α β} {xs : Vector α n} {i : Nat} (h : i < n) :
@[simp, grind =] theorem getElem_mapIdx {f : Nat α β} {xs : Vector α n} {i : Nat} (h : i < n) :
(xs.mapIdx f)[i] = f i (xs[i]'(by simp_all)) := by
rcases xs with xs, rfl
simp
@[simp] theorem getElem?_mapIdx {f : Nat α β} {xs : Vector α n} {i : Nat} :
@[simp, grind =] theorem getElem?_mapIdx {f : Nat α β} {xs : Vector α n} {i : Nat} :
(xs.mapIdx f)[i]? = xs[i]?.map (f i) := by
rcases xs with xs, rfl
simp
@@ -47,11 +47,11 @@ end Vector
namespace Array
@[simp] theorem mapFinIdx_toVector {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] theorem mapFinIdx_toVector {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
xs.toVector.mapFinIdx f = (xs.mapFinIdx f).toVector.cast (by simp) := by
ext <;> simp
@[simp] theorem mapIdx_toVector {f : Nat α β} {xs : Array α} :
@[simp, grind =] theorem mapIdx_toVector {f : Nat α β} {xs : Array α} :
xs.toVector.mapIdx f = (xs.mapIdx f).toVector.cast (by simp) := by
ext <;> simp
@@ -61,12 +61,12 @@ namespace Vector
/-! ### zipIdx -/
@[simp] theorem toList_zipIdx {xs : Vector α n} (k : Nat := 0) :
@[simp, grind =] theorem toList_zipIdx {xs : Vector α n} (k : Nat := 0) :
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
rcases xs with xs, rfl
simp
@[simp] theorem getElem_zipIdx {xs : Vector α n} {i : Nat} {h : i < n} :
@[simp, grind =] theorem getElem_zipIdx {xs : Vector α n} {i : Nat} {h : i < n} :
(xs.zipIdx k)[i] = (xs[i]'(by simp_all), k + i) := by
rcases xs with xs, rfl
simp
@@ -116,7 +116,7 @@ abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
subst w
rfl
@[simp]
@[simp, grind =]
theorem mapFinIdx_empty {f : (i : Nat) α (h : i < 0) β} : mapFinIdx #v[] f = #v[] :=
rfl
@@ -125,6 +125,7 @@ theorem mapFinIdx_eq_ofFn {as : Vector α n} {f : (i : Nat) → α → (h : i <
rcases as with as, rfl
simp [Array.mapFinIdx_eq_ofFn]
@[grind =]
theorem mapFinIdx_append {xs : Vector α n} {ys : Vector α m} {f : (i : Nat) α (h : i < n + m) β} :
(xs ++ ys).mapFinIdx f =
xs.mapFinIdx (fun i a h => f i a (by omega)) ++
@@ -133,7 +134,7 @@ theorem mapFinIdx_append {xs : Vector α n} {ys : Vector α m} {f : (i : Nat)
rcases ys with ys, rfl
simp [Array.mapFinIdx_append]
@[simp]
@[simp, grind =]
theorem mapFinIdx_push {xs : Vector α n} {a : α} {f : (i : Nat) α (h : i < n + 1) β} :
mapFinIdx (xs.push a) f =
(mapFinIdx xs (fun i a h => f i a (by omega))).push (f n a (by simp)) := by
@@ -154,7 +155,7 @@ theorem exists_of_mem_mapFinIdx {b : β} {xs : Vector α n} {f : (i : Nat) →
rcases xs with xs, rfl
exact List.exists_of_mem_mapFinIdx (by simpa using h)
@[simp] theorem mem_mapFinIdx {b : β} {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} :
@[simp, grind =] theorem mem_mapFinIdx {b : β} {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} :
b xs.mapFinIdx f (i : Nat) (h : i < n), f i xs[i] h = b := by
rcases xs with xs, rfl
simp
@@ -215,7 +216,7 @@ theorem mapFinIdx_eq_mapFinIdx_iff {xs : Vector α n} {f g : (i : Nat) → α
rw [eq_comm, mapFinIdx_eq_iff]
simp
@[simp] theorem mapFinIdx_mapFinIdx {xs : Vector α n}
@[simp, grind =] theorem mapFinIdx_mapFinIdx {xs : Vector α n}
{f : (i : Nat) α (h : i < n) β}
{g : (i : Nat) β (h : i < n) γ} :
(xs.mapFinIdx f).mapFinIdx g = xs.mapFinIdx (fun i a h => g i (f i a h) h) := by
@@ -229,14 +230,14 @@ theorem mapFinIdx_eq_replicate_iff {xs : Vector α n} {f : (i : Nat) → α
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapFinIdx_eq_mkVector_iff := @mapFinIdx_eq_replicate_iff
@[simp] theorem mapFinIdx_reverse {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} :
@[simp, grind =] theorem mapFinIdx_reverse {xs : Vector α n} {f : (i : Nat) α (h : i < n) β} :
xs.reverse.mapFinIdx f = (xs.mapFinIdx (fun i a h => f (n - 1 - i) a (by omega))).reverse := by
rcases xs with xs, rfl
simp
/-! ### mapIdx -/
@[simp]
@[simp, grind =]
theorem mapIdx_empty {f : Nat α β} : mapIdx f #v[] = #v[] :=
rfl
@@ -256,13 +257,14 @@ theorem mapIdx_eq_zipIdx_map {xs : Vector α n} {f : Nat → α → β} :
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-27")]
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
@[grind =]
theorem mapIdx_append {xs : Vector α n} {ys : Vector α m} :
(xs ++ ys).mapIdx f = xs.mapIdx f ++ ys.mapIdx fun i => f (i + n) := by
rcases xs with xs, rfl
rcases ys with ys, rfl
simp [Array.mapIdx_append]
@[simp]
@[simp, grind =]
theorem mapIdx_push {xs : Vector α n} {a : α} :
mapIdx f (xs.push a) = (mapIdx f xs).push (f n a) := by
simp [ append_singleton, mapIdx_append]
@@ -275,7 +277,7 @@ theorem exists_of_mem_mapIdx {b : β} {xs : Vector α n}
rw [mapIdx_eq_mapFinIdx] at h
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
@[simp] theorem mem_mapIdx {b : β} {xs : Vector α n} :
@[simp, grind =] theorem mem_mapIdx {b : β} {xs : Vector α n} :
b xs.mapIdx f (i : Nat) (h : i < n), f i xs[i] = b := by
constructor
· intro h
@@ -332,7 +334,7 @@ theorem mapIdx_eq_mapIdx_iff {xs : Vector α n} :
rcases xs with xs, rfl
simp [Array.mapIdx_eq_mapIdx_iff]
@[simp] theorem mapIdx_set {xs : Vector α n} {i : Nat} {h : i < n} {a : α} :
@[simp, grind =] theorem mapIdx_set {xs : Vector α n} {i : Nat} {h : i < n} {a : α} :
(xs.set i a).mapIdx f = (xs.mapIdx f).set i (f i a) (by simpa) := by
rcases xs with xs, rfl
simp
@@ -342,17 +344,17 @@ theorem mapIdx_eq_mapIdx_iff {xs : Vector α n} :
rcases xs with xs, rfl
simp
@[simp] theorem back?_mapIdx {xs : Vector α n} {f : Nat α β} :
@[simp, grind =] theorem back?_mapIdx {xs : Vector α n} {f : Nat α β} :
(mapIdx f xs).back? = (xs.back?).map (f (n - 1)) := by
rcases xs with xs, rfl
simp
@[simp] theorem back_mapIdx [NeZero n] {xs : Vector α n} {f : Nat α β} :
@[simp, grind =] theorem back_mapIdx [NeZero n] {xs : Vector α n} {f : Nat α β} :
(mapIdx f xs).back = f (n - 1) (xs.back) := by
rcases xs with xs, rfl
simp
@[simp] theorem mapIdx_mapIdx {xs : Vector α n} {f : Nat α β} {g : Nat β γ} :
@[simp, grind =] theorem mapIdx_mapIdx {xs : Vector α n} {f : Nat α β} {g : Nat β γ} :
(xs.mapIdx f).mapIdx g = xs.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff]
@@ -364,7 +366,7 @@ theorem mapIdx_eq_replicate_iff {xs : Vector α n} {f : Nat → α → β} {b :
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapIdx_eq_mkVector_iff := @mapIdx_eq_replicate_iff
@[simp] theorem mapIdx_reverse {xs : Vector α n} {f : Nat α β} :
@[simp, grind =] theorem mapIdx_reverse {xs : Vector α n} {f : Nat α β} :
xs.reverse.mapIdx f = (mapIdx (fun i => f (n - 1 - i)) xs).reverse := by
rcases xs with xs, rfl
simp [Array.mapIdx_reverse]

View File

@@ -20,15 +20,15 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Vector
@[simp] theorem getElem_ofFn {α n} {f : Fin n α} (h : i < n) :
@[simp, grind =] theorem getElem_ofFn {α n} {f : Fin n α} (h : i < n) :
(Vector.ofFn f)[i] = f i, by simpa using h := by
simp [ofFn]
theorem getElem?_ofFn {α n} {f : Fin n α} :
@[simp, grind =] theorem getElem?_ofFn {α n} {f : Fin n α} :
(ofFn f)[i]? = if h : i < n then some (f i, h) else none := by
simp [getElem?_def]
@[simp 500]
@[simp 500, grind =]
theorem mem_ofFn {n} {f : Fin n α} {a : α} : a ofFn f i, f i = a := by
constructor
· intro w
@@ -37,7 +37,7 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
· rintro i, rfl
apply mem_of_getElem (i := i) <;> simp
theorem back_ofFn {n} [NeZero n] {f : Fin n α} :
@[grind =] theorem back_ofFn {n} [NeZero n] {f : Fin n α} :
(ofFn f).back = f n - 1, by have := NeZero.ne n; omega := by
simp [back]
@@ -71,7 +71,7 @@ def ofFnM {n} [Monad m] (f : Fin n → m α) : m (Vector α n) :=
else
pure acc, by omega
@[simp]
@[simp, grind =]
theorem ofFnM_zero [Monad m] {f : Fin 0 m α} : Vector.ofFnM f = pure #v[] := by
simp [ofFnM, ofFnM.go]
@@ -114,13 +114,13 @@ theorem ofFnM_add {n m} [Monad m] [LawfulMonad m] {f : Fin (n + k) → m α} :
| zero => simp
| succ k ih => simp [ofFnM_succ, ih, push_append]
@[simp, grind] theorem toArray_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
@[simp, grind =] theorem toArray_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
toArray <$> ofFnM f = Array.ofFnM f := by
induction n with
| zero => simp
| succ n ih => simp [ofFnM_succ, Array.ofFnM_succ, ih]
@[simp, grind] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
@[simp, grind =] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n m α} :
toList <$> Vector.ofFnM f = List.ofFnM f := by
unfold toList
suffices Array.toList <$> (toArray <$> ofFnM f) = List.ofFnM f by

View File

@@ -112,14 +112,25 @@ theorem range'_eq_append_iff : range' s (n + m) = xs ++ ys ↔ xs = range' s n
· rintro h₁, h₂
exact n, by omega, by simp_all
@[simp] theorem find?_range'_eq_some {s n : Nat} {i : Nat} {p : Nat Bool} :
@[simp, grind =] 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
simp [range'_eq_mk_range']
@[simp] theorem find?_range'_eq_none {s n : Nat} {p : Nat Bool} :
@[simp, grind =] theorem find?_range'_eq_none {s n : Nat} {p : Nat Bool} :
(range' s n).find? p = none i, s i i < s + n !p i := by
simp [range'_eq_mk_range']
@[simp, grind =]
theorem count_range' {a s n step} (h : 0 < step := by simp) :
count a (range' s n step) = if i, i < n a = s + step * i then 1 else 0 := by
rw [range'_eq_mk_range', count_mk, Array.count_range' h]
@[simp, grind =]
theorem count_range_1' {a s n} :
count a (range' s n) = if s a a < s + n then 1 else 0 := by
rw [range'_eq_mk_range', count_mk, Array.count_range_1']
/-! ### range -/
@[simp, grind =] theorem getElem_range {i : Nat} (hi : i < n) : (Vector.range n)[i] = i := by
@@ -171,9 +182,15 @@ theorem self_mem_range_succ {n : Nat} : n ∈ range (n + 1) := by simp
(range n).find? p = none i, i < n !p i := by
simp [range_eq_range']
@[simp, grind =]
theorem count_range {a n} :
count a (range n) = if a < n then 1 else 0 := by
rw [range_eq_range', count_range_1']
simp
/-! ### zipIdx -/
@[simp]
@[simp, grind =]
theorem getElem?_zipIdx {xs : Vector α n} {i j} : (zipIdx xs i)[j]? = xs[j]?.map fun a => (a, i + j) := by
simp [getElem?_def]
@@ -216,7 +233,7 @@ theorem zipIdx_eq_map_add {xs : Vector α n} {i : Nat} :
simp only [zipIdx_mk, map_mk, eq_mk]
rw [Array.zipIdx_eq_map_add]
@[simp]
@[simp, grind =]
theorem zipIdx_singleton {x : α} {k : Nat} : zipIdx (#v[x]) k = #v[(x, k)] :=
rfl
@@ -265,6 +282,7 @@ theorem zipIdx_map {f : α → β} {xs : Vector α n} {k : Nat} :
rcases xs with xs, rfl
simp [Array.zipIdx_map]
@[grind =]
theorem zipIdx_append {xs : Vector α n} {ys : Vector α m} {k : Nat} :
zipIdx (xs ++ ys) k = zipIdx xs k ++ zipIdx ys (k + n) := by
rcases xs with xs, rfl

View File

@@ -46,6 +46,7 @@ theorem zipWith_self {f : αα → δ} {xs : Vector α n} : zipWith f xs xs
See also `getElem?_zipWith'` for a variant
using `Option.map` and `Option.bind` rather than a `match`.
-/
@[grind =]
theorem getElem?_zipWith {f : α β γ} {i : Nat} :
(zipWith f as bs)[i]? = match as[i]?, bs[i]? with
| some a, some b => some (f a b) | _, _ => none := by
@@ -74,53 +75,61 @@ theorem getElem?_zip_eq_some {as : Vector α n} {bs : Vector β n} {z : α × β
rcases bs with bs, h
simp [Array.getElem?_zip_eq_some]
@[simp]
@[simp, grind =]
theorem zipWith_map {μ} {f : γ δ μ} {g : α γ} {h : β δ} {as : Vector α n} {bs : Vector β n} :
zipWith f (as.map g) (bs.map h) = zipWith (fun a b => f (g a) (h b)) as bs := by
rcases as with as, rfl
rcases bs with bs, h
simp [Array.zipWith_map]
@[grind =]
theorem zipWith_map_left {as : Vector α n} {bs : Vector β n} {f : α α'} {g : α' β γ} :
zipWith g (as.map f) bs = zipWith (fun a b => g (f a) b) as bs := by
rcases as with as, rfl
rcases bs with bs, h
simp [Array.zipWith_map_left]
@[grind =]
theorem zipWith_map_right {as : Vector α n} {bs : Vector β n} {f : β β'} {g : α β' γ} :
zipWith g as (bs.map f) = zipWith (fun a b => g a (f b)) as bs := by
rcases as with as, rfl
rcases bs with bs, h
simp [Array.zipWith_map_right]
@[grind =]
theorem zipWith_foldr_eq_zip_foldr {f : α β γ} {i : δ} :
(zipWith f as bs).foldr g i = (zip as bs).foldr (fun p r => g (f p.1 p.2) r) i := by
rcases as with as, rfl
rcases bs with bs, h
simpa using Array.zipWith_foldr_eq_zip_foldr
@[grind =]
theorem zipWith_foldl_eq_zip_foldl {f : α β γ} {i : δ} :
(zipWith f as bs).foldl g i = (zip as bs).foldl (fun r p => g r (f p.1 p.2)) i := by
rcases as with as, rfl
rcases bs with bs, h
simpa using Array.zipWith_foldl_eq_zip_foldl
@[grind =]
theorem map_zipWith {δ : Type _} {f : α β} {g : γ δ α} {as : Vector γ n} {bs : Vector δ n} :
map f (zipWith g as bs) = zipWith (fun x y => f (g x y)) as bs := by
rcases as with as, rfl
rcases bs with bs, h
simp [Array.map_zipWith]
@[grind =]
theorem take_zipWith : (zipWith f as bs).take i = zipWith f (as.take i) (bs.take i) := by
rcases as with as, rfl
rcases bs with bs, h
simp [Array.take_zipWith]
@[grind =]
theorem extract_zipWith : (zipWith f as bs).extract i j = zipWith f (as.extract i j) (bs.extract i j) := by
rcases as with as, rfl
rcases bs with bs, h
simp [Array.extract_zipWith]
@[grind =]
theorem zipWith_append {f : α β γ}
{as : Vector α n} {as' : Vector α m} {bs : Vector β n} {bs' : Vector β m} :
zipWith f (as ++ as') (bs ++ bs') = zipWith f as bs ++ zipWith f as' bs' := by
@@ -147,7 +156,8 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Vector α (n + m)} {b
simp only at w₁ w₂
exact as₁, as₂, bs₁, bs₂, by simpa [hw, hy] using w₁, w₂
@[simp] theorem zipWith_replicate {a : α} {b : β} {n : Nat} :
@[simp, grind =]
theorem zipWith_replicate {a : α} {b : β} {n : Nat} :
zipWith f (replicate n a) (replicate n b) = replicate n (f a b) := by
ext
simp
@@ -167,6 +177,7 @@ theorem map_zip_eq_zipWith {f : α × β → γ} {as : Vector α n} {bs : Vector
rcases bs with bs, h
simp [Array.map_zip_eq_zipWith]
@[grind =]
theorem reverse_zipWith {f : α β γ} {as : Vector α n} {bs : Vector β n} :
(zipWith f as bs).reverse = zipWith f as.reverse bs.reverse := by
rcases as with as, rfl
@@ -175,7 +186,7 @@ theorem reverse_zipWith {f : α → β → γ} {as : Vector α n} {bs : Vector
/-! ### zip -/
@[simp]
@[simp, grind =]
theorem getElem_zip {as : Vector α n} {bs : Vector β n} {i : Nat} {h : i < n} :
(zip as bs)[i] = (as[i], bs[i]) :=
getElem_zipWith ..
@@ -185,18 +196,22 @@ theorem zip_eq_zipWith {as : Vector α n} {bs : Vector β n} : zip as bs = zipWi
rcases bs with bs, h
simp [Array.zip_eq_zipWith, h]
@[grind _=_]
theorem zip_map {f : α γ} {g : β δ} {as : Vector α n} {bs : Vector β n} :
zip (as.map f) (bs.map g) = (zip as bs).map (Prod.map f g) := by
rcases as with as, rfl
rcases bs with bs, h
simp [Array.zip_map, h]
@[grind _=_]
theorem zip_map_left {f : α γ} {as : Vector α n} {bs : Vector β n} :
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 : Vector α n} {bs : Vector β n} :
zip as (bs.map f) = (zip as bs).map (Prod.map id f) := by rw [ zip_map, map_id]
@[grind =]
theorem zip_append {as : Vector α n} {bs : Vector β n} {as' : Vector α m} {bs' : Vector β m} :
zip (as ++ as') (bs ++ bs') = zip as bs ++ zip as' bs' := by
rcases as with as, rfl
@@ -205,6 +220,7 @@ theorem zip_append {as : Vector α n} {bs : Vector β n} {as' : Vector α m} {bs
rcases bs' with bs', h'
simp [Array.zip_append, h, h']
@[grind =]
theorem zip_map' {f : α β} {g : α γ} {xs : Vector α n} :
zip (xs.map f) (xs.map g) = xs.map fun a => (f a, g a) := by
rcases xs with xs, rfl
@@ -248,7 +264,8 @@ theorem zip_eq_append_iff {as : Vector α (n + m)} {bs : Vector β (n + m)} {xs
as₁ as₂ bs₁ bs₂, as = as₁ ++ as₂ bs = bs₁ ++ bs₂ xs = zip as₁ bs₁ ys = zip as₂ bs₂ := by
simp [zip_eq_zipWith, zipWith_eq_append_iff]
@[simp] theorem zip_replicate {a : α} {b : β} {n : Nat} :
@[simp, grind =]
theorem zip_replicate {a : α} {b : β} {n : Nat} :
zip (replicate n a) (replicate n b) = replicate n (a, b) := by
ext <;> simp
@@ -257,14 +274,17 @@ abbrev zip_mkVector := @zip_replicate
/-! ### unzip -/
@[simp] theorem unzip_fst : (unzip xs).fst = xs.map Prod.fst := by
@[simp, grind =]
theorem unzip_fst : (unzip xs).fst = xs.map Prod.fst := by
cases xs
simp_all
@[simp] theorem unzip_snd : (unzip xs).snd = xs.map Prod.snd := by
@[simp, grind =]
theorem unzip_snd : (unzip xs).snd = xs.map Prod.snd := by
cases xs
simp_all
@[grind =]
theorem unzip_eq_map {xs : Vector (α × β) n} : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
cases xs
simp [List.unzip_eq_map]
@@ -296,7 +316,8 @@ theorem zip_of_prod {as : Vector α n} {bs : Vector β n} {xs : Vector (α × β
(hr : xs.map Prod.snd = bs) : xs = as.zip bs := by
rw [ hl, hr, zip_unzip xs, unzip_fst, unzip_snd, zip_unzip, zip_unzip]
@[simp] theorem unzip_replicate {a : α} {b : β} {n : Nat} :
@[simp, grind =]
theorem unzip_replicate {a : α} {b : β} {n : Nat} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
ext1 <;> simp

View File

@@ -47,7 +47,7 @@ proof in the context using `have`, because `get_elem_tactic` tries
The proof side-condition `valid xs i` is automatically dispatched by the
`get_elem_tactic` tactic; this tactic can be extended by adding more clauses to
`get_elem_tactic_trivial` using `macro_rules`.
`get_elem_tactic_extensible` using `macro_rules`.
`xs[i]?` and `xs[i]!` do not impose a proof obligation; the former returns
an `Option elem`, with `none` signalling that the value isn't present, and
@@ -281,7 +281,7 @@ instance [GetElem? cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
@[simp, grind =] theorem getElem!_fin [GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) [Inhabited Elem] : a[i]! = a[i.1]! := rfl
macro_rules
| `(tactic| get_elem_tactic_trivial) => `(tactic| (with_reducible apply Fin.val_lt_of_le); get_elem_tactic_trivial; done)
| `(tactic| get_elem_tactic_extensible) => `(tactic| (with_reducible apply Fin.val_lt_of_le); get_elem_tactic_extensible; done)
end Fin

View File

@@ -14,7 +14,7 @@ import Init.Grind.Propagator
import Init.Grind.Util
import Init.Grind.Offset
import Init.Grind.PP
import Init.Grind.CommRing
import Init.Grind.Ring
import Init.Grind.Module
import Init.Grind.Ordered
import Init.Grind.Ext

View File

@@ -1,16 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Grind.CommRing.Basic
import Init.Grind.CommRing.Int
import Init.Grind.CommRing.UInt
import Init.Grind.CommRing.SInt
import Init.Grind.CommRing.Fin
import Init.Grind.CommRing.BitVec
import Init.Grind.CommRing.Poly
import Init.Grind.CommRing.Field

View File

@@ -7,12 +7,15 @@ module
prelude
import Init.Data.Int.Order
import Init.Grind.ToInt
namespace Lean.Grind
class AddRightCancel (M : Type u) [Add M] where
add_right_cancel : a b c : M, a + c = b + c a = b
class NatModule (M : Type u) extends Zero M, Add M, HMul Nat M M where
add_zero : a : M, a + 0 = a
zero_add : a : M, 0 + a = a
add_comm : a b : M, a + b = b + a
add_assoc : a b c : M, a + b + c = a + (b + c)
zero_hmul : a : M, 0 * a = 0
@@ -26,7 +29,6 @@ attribute [instance 100] NatModule.toZero NatModule.toAdd NatModule.toHMul
class IntModule (M : Type u) extends Zero M, Add M, Neg M, Sub M, HMul Int M M where
add_zero : a : M, a + 0 = a
zero_add : a : M, 0 + a = a
add_comm : a b : M, a + b = b + a
add_assoc : a b c : M, a + b + c = a + (b + c)
zero_hmul : a : M, (0 : Int) * a = 0
@@ -38,6 +40,15 @@ class IntModule (M : Type u) extends Zero M, Add M, Neg M, Sub M, HMul Int M M w
neg_add_cancel : a : M, -a + a = 0
sub_eq_add_neg : a b : M, a - b = a + -b
namespace NatModule
variable {M : Type u} [NatModule M]
theorem zero_add (a : M) : 0 + a = a := by
rw [add_comm, add_zero]
end NatModule
namespace IntModule
attribute [instance 100] IntModule.toZero IntModule.toAdd IntModule.toNeg IntModule.toSub IntModule.toHMul
@@ -52,9 +63,21 @@ instance toNatModule (M : Type u) [i : IntModule M] : NatModule M :=
variable {M : Type u} [IntModule M]
instance (priority := 100) (M : Type u) [IntModule M] : SMul Nat M where
smul a x := (a : Int) * x
instance (priority := 100) (M : Type u) [IntModule M] : SMul Int M where
smul a x := a * x
theorem zero_add (a : M) : 0 + a = a := by
rw [add_comm, add_zero]
theorem add_neg_cancel (a : M) : a + -a = 0 := by
rw [add_comm, neg_add_cancel]
theorem add_left_comm (a b c : M) : a + (b + c) = b + (a + c) := by
rw [ add_assoc, add_assoc, add_comm a]
theorem add_left_inj {a b : M} (c : M) : a + c = b + c a = b :=
fun h => by simpa [add_assoc, add_neg_cancel, add_zero] using (congrArg (· + -c) h),
fun g => congrArg (· + c) g
@@ -93,6 +116,12 @@ theorem sub_eq_iff {a b c : M} : a - b = c ↔ a = c + b := by
theorem sub_eq_zero_iff {a b : M} : a - b = 0 a = b := by
simp [sub_eq_iff, zero_add]
theorem add_sub_cancel {a b : M} : a + b - b = a := by
rw [sub_eq_add_neg, add_assoc, add_neg_cancel, add_zero]
theorem sub_add_cancel {a b : M} : a - b + b = a := by
rw [sub_eq_add_neg, add_assoc, neg_add_cancel, add_zero]
theorem neg_hmul (n : Int) (a : M) : (-n) * a = - (n * a) := by
apply (add_left_inj (n * a)).mp
rw [ add_hmul, Int.add_left_neg, zero_hmul, neg_add_cancel]
@@ -101,6 +130,12 @@ theorem hmul_neg (n : Int) (a : M) : n * (-a) = - (n * a) := by
apply (add_left_inj (n * a)).mp
rw [ hmul_add, neg_add_cancel, neg_add_cancel, hmul_zero]
theorem hmul_sub (k : Int) (a b : M) : k * (a - b) = k * a - k * b := by
rw [sub_eq_add_neg, hmul_add, hmul_neg, sub_eq_add_neg]
theorem sub_hmul (k₁ k₂ : Int) (a : M) : (k₁ - k₂) * a = k₁ * a - k₂ * a := by
rw [Int.sub_eq_add_neg, add_hmul, neg_hmul, sub_eq_add_neg]
end IntModule
/--
@@ -111,4 +146,17 @@ class NoNatZeroDivisors (α : Type u) [Zero α] [HMul Nat α α] where
export NoNatZeroDivisors (no_nat_zero_divisors)
instance [ToInt α (some lo) (some hi)] [IntModule α] [ToInt.Zero α (some lo) (some hi)] [ToInt.Add α (some lo) (some hi)] : ToInt.Neg α (some lo) (some hi) where
toInt_neg x := by
have := (ToInt.Add.toInt_add (-x) x).symm
rw [IntModule.neg_add_cancel, ToInt.Zero.toInt_zero] at this
rw [ToInt.wrap_eq_wrap_iff] at this
simp at this
rw [ ToInt.wrap_toInt]
rw [ToInt.wrap_eq_wrap_iff]
simpa
instance [ToInt α (some lo) (some hi)] [IntModule α] [ToInt.Add α (some lo) (some hi)] [ToInt.Neg α (some lo) (some hi)] : ToInt.Sub α (some lo) (some hi) :=
ToInt.Sub.of_sub_eq_add_neg IntModule.sub_eq_add_neg
end Lean.Grind

View File

@@ -12,6 +12,7 @@ import Init.Classical
import Init.ByCases
import Init.Data.Int.Linear
import Init.Data.Int.Pow
import Init.Grind.Ring.Field
namespace Lean.Grind
/-!
@@ -177,15 +178,16 @@ init_grind_norm
Nat.add_eq Nat.sub_eq Nat.mul_eq Nat.zero_eq Nat.le_eq
Nat.div_zero Nat.mod_zero Nat.div_one Nat.mod_one
Nat.sub_sub Nat.pow_zero Nat.pow_one Nat.sub_self
Nat.one_pow
-- Int
Int.lt_eq
Int.emod_neg Int.ediv_neg
Int.ediv_zero Int.emod_zero
Int.ediv_one Int.emod_one
Int.negSucc_eq
natCast_eq natCast_div natCast_mod
natCast_add natCast_mul
Int.one_pow
Int.pow_zero Int.pow_one
-- GT GE
ge_eq gt_eq
@@ -197,5 +199,7 @@ init_grind_norm
-- Function composition
Function.const_apply Function.comp_apply Function.const_comp
Function.comp_const Function.true_comp Function.false_comp
-- Field
Field.div_eq_mul_inv Field.inv_zero Field.inv_inv Field.inv_one Field.inv_neg
end Lean.Grind

View File

@@ -11,3 +11,4 @@ import Init.Grind.Ordered.Module
import Init.Grind.Ordered.Ring
import Init.Grind.Ordered.Field
import Init.Grind.Ordered.Int
import Init.Grind.Ordered.Linarith

View File

@@ -6,7 +6,7 @@ Authors: Kim Morrison
module
prelude
import Init.Grind.CommRing.Field
import Init.Grind.Ring.Field
import Init.Grind.Ordered.Ring
namespace Lean.Grind
@@ -185,4 +185,54 @@ theorem mul_le_mul_iff_of_neg_left {a b c : R} (h : c < 0) : c * a ≤ c * b ↔
end Field.IsOrdered
theorem Field.zero_lt_one [Field α] [LinearOrder α] [Ring.IsOrdered α] : (0 : α) < 1 := by
cases LinearOrder.trichotomy (0:α) 1
next => assumption
next h =>
cases h
next => have := Field.zero_ne_one (α := α); contradiction
next h =>
have := Ring.IsOrdered.mul_pos_of_neg_of_neg h h
simp [Semiring.one_mul] at this
assumption
theorem Field.minus_one_lt_zero [Field α] [LinearOrder α] [Ring.IsOrdered α] : (-1 : α) < 0 := by
have h := Field.zero_lt_one (α := α)
have := IntModule.IsOrdered.add_lt_left h (-1)
rw [Semiring.zero_add, Ring.add_neg_cancel] at this
assumption
theorem ofNat_nonneg [Field α] [LinearOrder α] [Ring.IsOrdered α] (x : Nat) : (OfNat.ofNat x : α) 0 := by
induction x
next => simp [OfNat.ofNat, Zero.zero]; apply Preorder.le_refl
next n ih =>
have := Field.zero_lt_one (α := α)
rw [Semiring.ofNat_succ]
replace ih := IntModule.IsOrdered.add_le_left ih 1
rw [Semiring.zero_add] at ih
have := Preorder.lt_of_lt_of_le this ih
exact Preorder.le_of_lt this
instance [Field α] [LinearOrder α] [Ring.IsOrdered α] : IsCharP α 0 where
ofNat_eq_zero_iff := by
intro x
simp only [Nat.mod_zero]; constructor
next =>
intro h
cases x
next => rfl
next x =>
rw [Semiring.ofNat_succ] at h
replace h := congrArg (· - 1) h; simp at h
rw [Ring.sub_eq_add_neg, Semiring.add_assoc, Ring.add_neg_cancel,
Ring.sub_eq_add_neg, Semiring.zero_add, Semiring.add_zero] at h
have h₁ : (OfNat.ofNat x : α) < 0 := by
have := Field.minus_one_lt_zero (α := α)
rw [h]; assumption
have h₂ := ofNat_nonneg (α := α) x
have : (0 : α) < 0 := Preorder.lt_of_le_of_lt h₂ h₁
simp
exact (Preorder.lt_irrefl 0) this
next => intro h; rw [OfNat.ofNat, h]; rfl
end Lean.Grind

View File

@@ -7,7 +7,7 @@ module
prelude
import Init.Grind.Ordered.Ring
import Init.Grind.CommRing.Int
import Init.GrindInstances.Ring.Int
import Init.Omega
/-!
@@ -24,7 +24,7 @@ instance : Preorder Int where
instance : IntModule.IsOrdered Int where
neg_le_iff := by omega
add_le_left := by omega
hmul_pos k a ha := fun hk => Int.mul_pos hk ha, fun h => Int.pos_of_mul_pos_left h ha
hmul_pos_iff k a ha := fun h => Int.pos_of_mul_pos_left h ha, fun hk => Int.mul_pos hk ha
hmul_nonneg hk ha := Int.mul_nonneg hk ha
instance : Ring.IsOrdered Int where

View File

@@ -0,0 +1,564 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Grind.Ordered.Module
import Init.Grind.Ordered.Ring
import Init.Grind.Ring.Field
import all Init.Data.Ord
import all Init.Data.AC
import Init.Data.RArray
/-!
Support for the linear arithmetic module for `IntModule` in `grind`
-/
namespace Lean.Grind.Linarith
abbrev Var := Nat
open IntModule
attribute [local simp] add_zero zero_add zero_hmul hmul_zero one_hmul
inductive Expr where
| zero
| var (i : Var)
| add (a b : Expr)
| sub (a b : Expr)
| neg (a : Expr)
| mul (k : Int) (a : Expr)
deriving Inhabited, BEq
abbrev Context (α : Type u) := RArray α
def Var.denote {α} (ctx : Context α) (v : Var) : α :=
ctx.get v
def Expr.denote {α} [IntModule α] (ctx : Context α) : Expr α
| zero => 0
| .var v => v.denote ctx
| .add a b => denote ctx a + denote ctx b
| .sub a b => denote ctx a - denote ctx b
| .mul k a => k * denote ctx a
| .neg a => -denote ctx a
inductive Poly where
| nil
| add (k : Int) (v : Var) (p : Poly)
deriving BEq
def Poly.denote {α} [IntModule α] (ctx : Context α) (p : Poly) : α :=
match p with
| .nil => 0
| .add k v p => k * v.denote ctx + denote ctx p
/--
Similar to `Poly.denote`, but produces a denotation better for normalization.
-/
def Poly.denote' {α} [IntModule α] (ctx : Context α) (p : Poly) : α :=
match p with
| .nil => 0
| .add 1 v p => go (v.denote ctx) p
| .add k v p => go (k * v.denote ctx) p
where
go (r : α) (p : Poly) : α :=
match p with
| .nil => r
| .add 1 v p => go (r + v.denote ctx) p
| .add k v p => go (r + k * v.denote ctx) p
-- Helper instance for `ac_rfl`
local instance {α} [IntModule α] : Std.Associative (· + · : α α α) where
assoc := IntModule.add_assoc
-- Helper instance for `ac_rfl`
local instance {α} [IntModule α] : Std.Commutative (· + · : α α α) where
comm := IntModule.add_comm
theorem Poly.denote'_go_eq_denote {α} [IntModule α] (ctx : Context α) (p : Poly) (r : α) : denote'.go ctx r p = p.denote ctx + r := by
induction r, p using denote'.go.induct ctx <;> simp [denote'.go, denote]
next ih => rw [ih]; ac_rfl
next ih => rw [ih]; ac_rfl
theorem Poly.denote'_eq_denote {α} [IntModule α] (ctx : Context α) (p : Poly) : p.denote' ctx = p.denote ctx := by
unfold denote' <;> split <;> simp [denote, denote'_go_eq_denote] <;> ac_rfl
def Poly.coeff (p : Poly) (x : Var) : Int :=
match p with
| .add a y p => bif x == y then a else coeff p x
| .nil => 0
def Poly.insert (k : Int) (v : Var) (p : Poly) : Poly :=
match p with
| .nil => .add k v .nil
| .add k' v' p =>
bif Nat.blt v' v then
.add k v <| .add k' v' p
else bif Nat.beq v v' then
if Int.add k k' == 0 then
p
else
.add (Int.add k k') v' p
else
.add k' v' (insert k v p)
/-- Normalizes the given polynomial by fusing monomial and constants. -/
def Poly.norm (p : Poly) : Poly :=
match p with
| .nil => .nil
| .add k v p => (norm p).insert k v
def Poly.append (p₁ p₂ : Poly) : Poly :=
match p₁ with
| .nil => p₂
| .add k x p₁ => .add k x (append p₁ p₂)
def Poly.combine' (fuel : Nat) (p₁ p₂ : Poly) : Poly :=
match fuel with
| 0 => p₁.append p₂
| fuel + 1 => match p₁, p₂ with
| .nil, p₂ => p₂
| p₁, .nil => p₁
| .add a₁ x₁ p₁, .add a₂ x₂ p₂ =>
bif Nat.beq x₁ x₂ then
let a := a₁ + a₂
bif a == 0 then
combine' fuel p₁ p₂
else
.add a x₁ (combine' fuel p₁ p₂)
else bif Nat.blt x₂ x₁ then
.add a₁ x₁ (combine' fuel p₁ (.add a₂ x₂ p₂))
else
.add a₂ x₂ (combine' fuel (.add a₁ x₁ p₁) p₂)
def Poly.combine (p₁ p₂ : Poly) : Poly :=
combine' 100000000 p₁ p₂
/-- Converts the given expression into a polynomial. -/
def Expr.toPoly' (e : Expr) : Poly :=
go 1 e .nil
where
go (coeff : Int) : Expr (Poly Poly)
| .zero => id
| .var v => (.add coeff v ·)
| .add a b => go coeff a go coeff b
| .sub a b => go coeff a go (-coeff) b
| .mul k a => bif k == 0 then id else go (Int.mul coeff k) a
| .neg a => go (-coeff) a
/-- Converts the given expression into a polynomial, and then normalizes it. -/
def Expr.norm (e : Expr) : Poly :=
e.toPoly'.norm
/--
`p.mul k` multiplies all coefficients and constant of the polynomial `p` by `k`.
-/
def Poly.mul' (p : Poly) (k : Int) : Poly :=
match p with
| .nil => .nil
| .add k' v p => .add (k*k') v (mul' p k)
def Poly.mul (p : Poly) (k : Int) : Poly :=
if k == 0 then
.nil
else
p.mul' k
@[simp] theorem Poly.denote_mul {α} [IntModule α] (ctx : Context α) (p : Poly) (k : Int) : (p.mul k).denote ctx = k * p.denote ctx := by
simp [mul]
split
next => simp [*, denote]
next =>
induction p <;> simp [mul', denote, *]
rw [mul_hmul, hmul_add]
theorem Poly.denote_insert {α} [IntModule α] (ctx : Context α) (k : Int) (v : Var) (p : Poly) :
(p.insert k v).denote ctx = p.denote ctx + k * v.denote ctx := by
fun_induction p.insert k v <;> simp [denote]
next => ac_rfl
next h₁ h₂ h₃ =>
simp at h₃; simp at h₂; subst h₂
rw [add_comm, add_assoc, add_hmul, h₃, zero_hmul, zero_add]
next h _ => simp at h; subst h; rw [add_hmul]; ac_rfl
next ih => rw [ih]; ac_rfl
attribute [local simp] Poly.denote_insert
theorem Poly.denote_norm {α} [IntModule α] (ctx : Context α) (p : Poly) : p.norm.denote ctx = p.denote ctx := by
induction p <;> simp [denote, norm, add_comm, *]
attribute [local simp] Poly.denote_norm
theorem Poly.denote_append {α} [IntModule α] (ctx : Context α) (p₁ p₂ : Poly) : (p₁.append p₂).denote ctx = p₁.denote ctx + p₂.denote ctx := by
induction p₁ <;> simp [append, denote, *]; ac_rfl
attribute [local simp] Poly.denote_append
theorem Poly.denote_combine' {α} [IntModule α] (ctx : Context α) (fuel : Nat) (p₁ p₂ : Poly) : (p₁.combine' fuel p₂).denote ctx = p₁.denote ctx + p₂.denote ctx := by
fun_induction p₁.combine' fuel p₂ <;>
simp_all +zetaDelta [denote, Int.add_mul]
next h _ =>
rw [Int.add_comm] at h
rw [add_left_comm, add_assoc, add_assoc, add_hmul, h, zero_hmul, zero_add]
next => rw [add_hmul]; ac_rfl
all_goals ac_rfl
theorem Poly.denote_combine {α} [IntModule α] (ctx : Context α) (p₁ p₂ : Poly) : (p₁.combine p₂).denote ctx = p₁.denote ctx + p₂.denote ctx := by
simp [combine, denote_combine']
attribute [local simp] Poly.denote_combine
theorem Expr.denote_toPoly'_go {α} [IntModule α] {k p} (ctx : Context α) (e : Expr)
: (toPoly'.go k e p).denote ctx = k * e.denote ctx + p.denote ctx := by
induction k, e using Expr.toPoly'.go.induct generalizing p <;> simp [toPoly'.go, denote, Poly.denote, *, hmul_add]
next => ac_rfl
next => rw [sub_eq_add_neg, neg_hmul, hmul_add, hmul_neg]; ac_rfl
next h => simp at h; subst h; simp
next ih => simp at ih; rw [ih, mul_hmul]
next => rw [hmul_neg, neg_hmul]
theorem Expr.denote_norm {α} [IntModule α] (ctx : Context α) (e : Expr) : e.norm.denote ctx = e.denote ctx := by
simp [norm, toPoly', Expr.denote_toPoly'_go, Poly.denote]
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
def Poly.leadCoeff (p : Poly) : Int :=
match p with
| .add a _ _ => a
| _ => 1
open IntModule.IsOrdered
/-!
Helper theorems for conflict resolution during model construction.
-/
private theorem le_add_le {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] {a b : α}
(h₁ : a 0) (h₂ : b 0) : a + b 0 := by
replace h₁ := add_le_left h₁ b; simp at h₁
exact Preorder.le_trans h₁ h₂
private theorem le_add_lt {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] {a b : α}
(h₁ : a 0) (h₂ : b < 0) : a + b < 0 := by
replace h₁ := add_le_left h₁ b; simp at h₁
exact Preorder.lt_of_le_of_lt h₁ h₂
private theorem lt_add_lt {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] {a b : α}
(h₁ : a < 0) (h₂ : b < 0) : a + b < 0 := by
replace h₁ := add_lt_left h₁ b; simp at h₁
exact Preorder.lt_trans h₁ h₂
private theorem coe_natAbs_nonneg (a : Int) : (a.natAbs : Int) 0 := by
exact Int.natCast_nonneg a.natAbs
def le_le_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
theorem le_le_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
: le_le_combine_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [le_le_combine_cert]; intro _ h₁ h₂; subst p₃; simp
replace h₁ := hmul_nonpos (coe_natAbs_nonneg p₂.leadCoeff) h₁
replace h₂ := hmul_nonpos (coe_natAbs_nonneg p₁.leadCoeff) h₂
exact le_add_le h₁ h₂
def le_lt_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
a₁ > (0 : Int) && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
theorem le_lt_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
: le_lt_combine_cert p₁ p₂ p₃ p₁.denote' ctx 0 p₂.denote' ctx < 0 p₃.denote' ctx < 0 := by
simp [-Int.natAbs_pos, -Int.ofNat_pos, le_lt_combine_cert]; intro hp _ h₁ h₂; subst p₃; simp
replace h₁ := hmul_nonpos (coe_natAbs_nonneg p₂.leadCoeff) h₁
replace h₂ := hmul_neg_iff (p₁.leadCoeff.natAbs) h₂ |>.mpr hp
exact le_add_lt h₁ h₂
def lt_lt_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
a₂ > (0 : Int) && a₁ > (0 : Int) && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
theorem lt_lt_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
: lt_lt_combine_cert p₁ p₂ p₃ p₁.denote' ctx < 0 p₂.denote' ctx < 0 p₃.denote' ctx < 0 := by
simp [-Int.natAbs_pos, -Int.ofNat_pos, lt_lt_combine_cert]; intro hp₁ hp₂ _ h₁ h₂; subst p₃; simp
replace h₁ := hmul_neg_iff (p₂.leadCoeff.natAbs) h₁ |>.mpr hp₁
replace h₂ := hmul_neg_iff (p₁.leadCoeff.natAbs) h₂ |>.mpr hp₂
exact lt_add_lt h₁ h₂
def diseq_split_cert (p₁ p₂ : Poly) : Bool :=
p₂ == p₁.mul (-1)
-- We need `LinearOrder` to use `trichotomy`
theorem diseq_split {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ : Poly)
: diseq_split_cert p₁ p₂ p₁.denote' ctx 0 p₁.denote' ctx < 0 p₂.denote' ctx < 0 := by
simp [diseq_split_cert]; intro _ h₁; subst p₂; simp
cases LinearOrder.trichotomy (p₁.denote ctx) 0
next h => exact Or.inl h
next h =>
apply Or.inr
simp [h₁] at h
rw [ neg_pos_iff, neg_hmul, neg_neg, one_hmul]; assumption
theorem diseq_split_resolve {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ : Poly)
: diseq_split_cert p₁ p₂ p₁.denote' ctx 0 ¬p₁.denote' ctx < 0 p₂.denote' ctx < 0 := by
intro h₁ h₂ h₃
exact (diseq_split ctx p₁ p₂ h₁ h₂).resolve_left h₃
/-!
Helper theorems for internalizing facts into the linear arithmetic procedure
-/
def norm_cert (lhs rhs : Expr) (p : Poly) :=
p == (lhs.sub rhs).norm
theorem eq_norm {α} [IntModule α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote' ctx = 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
theorem le_of_eq {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
apply Preorder.le_refl
theorem diseq_norm {α} [IntModule α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
intro h
replace h := congrArg (rhs.denote ctx + ·) h; simp [sub_eq_add_neg] at h
rw [add_left_comm, sub_eq_add_neg, sub_self, add_zero] at h
contradiction
theorem le_norm {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx rhs.denote ctx p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
replace h₁ := add_le_left h₁ (-rhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem lt_norm {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p lhs.denote ctx < rhs.denote ctx p.denote' ctx < 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
replace h₁ := add_lt_left h₁ (-rhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_le_norm {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert rhs lhs p ¬ lhs.denote ctx rhs.denote ctx p.denote' ctx < 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
replace h₁ := LinearOrder.lt_of_not_le h₁
replace h₁ := add_lt_left h₁ (-lhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
theorem not_lt_norm {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert rhs lhs p ¬ lhs.denote ctx < rhs.denote ctx p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
replace h₁ := LinearOrder.le_of_not_lt h₁
replace h₁ := add_le_left h₁ (-lhs.denote ctx)
simp [ sub_eq_add_neg, sub_self] at h₁
assumption
-- If the module does not have a linear order, we can still put the expressions in polynomial forms
theorem not_le_norm' {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p ¬ lhs.denote ctx rhs.denote ctx ¬ p.denote' ctx 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]; intro h
replace h := add_le_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp at h
contradiction
theorem not_lt_norm' {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: norm_cert lhs rhs p ¬ lhs.denote ctx < rhs.denote ctx ¬ p.denote' ctx < 0 := by
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]; intro h
replace h := add_lt_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp at h
contradiction
/-!
Equality detection
-/
def eq_of_le_ge_cert (p₁ p₂ : Poly) : Bool :=
p₂ == p₁.mul (-1)
theorem eq_of_le_ge {α} [IntModule α] [PartialOrder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ : Poly) (p₂ : Poly)
: eq_of_le_ge_cert p₁ p₂ p₁.denote' ctx 0 p₂.denote' ctx 0 p₁.denote' ctx = 0 := by
simp [eq_of_le_ge_cert]
intro; subst p₂; simp
intro h₁ h₂
replace h₂ := add_le_left h₂ (p₁.denote ctx)
rw [add_comm, neg_hmul, one_hmul, sub_eq_add_neg, sub_self, zero_add] at h₂
exact PartialOrder.le_antisymm h₁ h₂
/-!
Helper theorems for closing the goal
-/
theorem diseq_unsat {α} [IntModule α] (ctx : Context α) : (Poly.nil).denote ctx 0 False := by
simp [Poly.denote]
theorem lt_unsat {α} [IntModule α] [Preorder α] (ctx : Context α) : (Poly.nil).denote ctx < 0 False := by
simp [Poly.denote]; intro h
have := Preorder.lt_iff_le_not_le.mp h
simp at this
def zero_lt_one_cert (p : Poly) : Bool :=
p == .add (-1) 0 .nil
theorem zero_lt_one {α} [Ring α] [Preorder α] [Ring.IsOrdered α] (ctx : Context α) (p : Poly)
: zero_lt_one_cert p (0 : Var).denote ctx = One.one p.denote' ctx < 0 := by
simp [zero_lt_one_cert]; intro _ h; subst p; simp [Poly.denote, h, One.one, neg_hmul]
rw [neg_lt_iff, neg_zero]; apply Ring.IsOrdered.zero_lt_one
def zero_ne_one_cert (p : Poly) : Bool :=
p == .add 1 0 .nil
theorem zero_ne_one_of_ord_ring {α} [Ring α] [Preorder α] [Ring.IsOrdered α] (ctx : Context α) (p : Poly)
: zero_ne_one_cert p (0 : Var).denote ctx = One.one p.denote' ctx 0 := by
simp [zero_ne_one_cert]; intro _ h; subst p; simp [Poly.denote, h, One.one]
intro h; have := Ring.IsOrdered.zero_lt_one (R := α); simp [h, Preorder.lt_irrefl] at this
theorem zero_ne_one_of_field {α} [Field α] (ctx : Context α) (p : Poly)
: zero_ne_one_cert p (0 : Var).denote ctx = One.one p.denote' ctx 0 := by
simp [zero_ne_one_cert]; intro _ h; subst p; simp [Poly.denote, h, One.one]
intro h; have := Field.zero_ne_one (α := α); simp [h] at this
theorem zero_ne_one_of_char0 {α} [Ring α] [IsCharP α 0] (ctx : Context α) (p : Poly)
: zero_ne_one_cert p (0 : Var).denote ctx = One.one p.denote' ctx 0 := by
simp [zero_ne_one_cert]; intro _ h; subst p; simp [Poly.denote, h, One.one]
intro h; have := IsCharP.intCast_eq_zero_iff (α := α) 0 1; simp [Ring.intCast_one] at this
contradiction
def zero_ne_one_of_charC_cert (c : Nat) (p : Poly) : Bool :=
(c:Int) > 1 && p == .add 1 0 .nil
theorem zero_ne_one_of_charC {α c} [Ring α] [IsCharP α c] (ctx : Context α) (p : Poly)
: zero_ne_one_of_charC_cert c p (0 : Var).denote ctx = One.one p.denote' ctx 0 := by
simp [zero_ne_one_of_charC_cert]; intro hc _ h; subst p; simp [Poly.denote, h, One.one]
intro h; have h' := IsCharP.intCast_eq_zero_iff (α := α) c 1; simp [Ring.intCast_one] at h'
replace h' := h'.mp h
have := Int.emod_eq_of_lt (by decide) hc
simp [this] at h'
/-!
Coefficient normalization
-/
def eq_neg_cert (p₁ p₂ : Poly) :=
p₂ == p₁.mul (-1)
theorem eq_neg {α} [IntModule α] (ctx : Context α) (p₁ p₂ : Poly)
: eq_neg_cert p₁ p₂ p₁.denote' ctx = 0 p₂.denote' ctx = 0 := by
simp [eq_neg_cert]; intros; simp [*]
def eq_coeff_cert (p₁ p₂ : Poly) (k : Nat) :=
k != 0 && p₁ == p₂.mul k
theorem eq_coeff {α} [IntModule α] [NoNatZeroDivisors α] (ctx : Context α) (p₁ p₂ : Poly) (k : Nat)
: eq_coeff_cert p₁ p₂ k p₁.denote' ctx = 0 p₂.denote' ctx = 0 := by
simp [eq_coeff_cert]; intro h _; subst p₁; simp [*]
exact no_nat_zero_divisors k (p₂.denote ctx) h
def coeff_cert (p₁ p₂ : Poly) (k : Nat) :=
k > 0 && p₁ == p₂.mul k
theorem le_coeff {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ : Poly) (k : Nat)
: coeff_cert p₁ p₂ k p₁.denote' ctx 0 p₂.denote' ctx 0 := by
simp [coeff_cert]; intro h _; subst p₁; simp
have : k > (0 : Int) := Int.natCast_pos.mpr h
intro h₁; apply Classical.byContradiction
intro h₂; replace h₂ := LinearOrder.lt_of_not_le h₂
replace h₂ := IsOrdered.hmul_pos_iff (k) h₂ |>.mpr this
exact Preorder.lt_irrefl 0 (Preorder.lt_of_lt_of_le h₂ h₁)
theorem lt_coeff {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ : Poly) (k : Nat)
: coeff_cert p₁ p₂ k p₁.denote' ctx < 0 p₂.denote' ctx < 0 := by
simp [coeff_cert]; intro h _; subst p₁; simp
have : k > (0 : Int) := Int.natCast_pos.mpr h
intro h₁; apply Classical.byContradiction
intro h₂; replace h₂ := LinearOrder.le_of_not_lt h₂
replace h₂ := IsOrdered.hmul_nonneg (Int.le_of_lt this) h₂
exact Preorder.lt_irrefl 0 (Preorder.lt_of_le_of_lt h₂ h₁)
theorem diseq_neg {α} [IntModule α] (ctx : Context α) (p p' : Poly) : p' == p.mul (-1) p.denote' ctx 0 p'.denote' ctx 0 := by
simp; intro _ _; subst p'; simp [neg_hmul]
intro h; replace h := congrArg (- ·) h; simp [neg_neg, neg_zero] at h
contradiction
/-!
Substitution
-/
def eq_diseq_subst_cert (k₁ k₂ : Int) (p₁ p₂ p₃ : Poly) : Bool :=
k₁.natAbs 0 && p₃ == (p₁.mul k₂ |>.combine (p₂.mul k₁))
theorem eq_diseq_subst {α} [IntModule α] [NoNatZeroDivisors α] (ctx : Context α) (k₁ k₂ : Int) (p₁ p₂ p₃ : Poly)
: eq_diseq_subst_cert k₁ k₂ p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [eq_diseq_subst_cert, - Int.natAbs_eq_zero, -Int.natCast_eq_zero]; intro hne _ h₁ h₂; subst p₃
simp [h₁, h₂]; intro h₃
have : k₁.natAbs * Poly.denote ctx p₂ = 0 := by
have : (k₁.natAbs : Int) * Poly.denote ctx p₂ = 0 := by
cases Int.natAbs_eq_iff.mp (Eq.refl k₁.natAbs)
next h => rw [ h]; assumption
next h => replace h := congrArg (- ·) h; simp at h; rw [ h, IntModule.neg_hmul, h₃, IntModule.neg_zero]
exact this
have := no_nat_zero_divisors (k₁.natAbs) (p₂.denote ctx) hne this
contradiction
def eq_diseq_subst1_cert (k : Int) (p₁ p₂ p₃ : Poly) : Bool :=
p₃ == (p₁.mul k |>.combine p₂)
/-
Special case of `diseq_eq_subst` where leading coefficient `c₁` of `p₁` is `-k*c₂`, where
`c₂` is the leading coefficient of `p₂`.
-/
theorem eq_diseq_subst1 {α} [IntModule α] (ctx : Context α) (k : Int) (p₁ p₂ p₃ : Poly)
: eq_diseq_subst1_cert k p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [eq_diseq_subst1_cert]; intro _ h₁ h₂; subst p₃
simp [h₁, h₂]
def eq_le_subst_cert (x : Var) (p₁ p₂ p₃ : Poly) :=
let a := p₁.coeff x
let b := p₂.coeff x
a 0 && p₃ == (p₂.mul a |>.combine (p₁.mul (-b)))
theorem eq_le_subst {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (x : Var) (p₁ p₂ p₃ : Poly)
: eq_le_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx 0 p₃.denote' ctx 0 := by
simp [eq_le_subst_cert]; intro h _ h₁ h₂; subst p₃; simp [h₁]
exact hmul_nonpos h h₂
def eq_lt_subst_cert (x : Var) (p₁ p₂ p₃ : Poly) :=
let a := p₁.coeff x
let b := p₂.coeff x
a > 0 && p₃ == (p₂.mul a |>.combine (p₁.mul (-b)))
theorem eq_lt_subst {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (x : Var) (p₁ p₂ p₃ : Poly)
: eq_lt_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx < 0 p₃.denote' ctx < 0 := by
simp [eq_lt_subst_cert]; intro h _ h₁ h₂; subst p₃; simp [h₁]
exact IsOrdered.hmul_neg_iff (p₁.coeff x) h₂ |>.mpr h
def eq_eq_subst_cert (x : Var) (p₁ p₂ p₃ : Poly) :=
let a := p₁.coeff x
let b := p₂.coeff x
p₃ == (p₂.mul a |>.combine (p₁.mul (-b)))
theorem eq_eq_subst {α} [IntModule α] (ctx : Context α) (x : Var) (p₁ p₂ p₃ : Poly)
: eq_eq_subst_cert x p₁ p₂ p₃ p₁.denote' ctx = 0 p₂.denote' ctx = 0 p₃.denote' ctx = 0 := by
simp [eq_eq_subst_cert]; intro _ h₁ h₂; subst p₃; simp [h₁, h₂]
end Lean.Grind.Linarith

View File

@@ -12,12 +12,79 @@ import Init.Grind.Ordered.Order
namespace Lean.Grind
class NatModule.IsOrdered (M : Type u) [Preorder M] [NatModule M] where
add_le_left_iff : {a b : M} (c : M), a b a + c b + c
hmul_lt_hmul_iff : (k : Nat) {a b : M}, a < b (k * a < k * b 0 < k)
hmul_le_hmul : {k : Nat} {a b : M}, a b k * a k * b
class IntModule.IsOrdered (M : Type u) [Preorder M] [IntModule M] where
neg_le_iff : a b : M, -a b -b a
add_le_left : {a b : M}, a b (c : M) a + c b + c
hmul_pos : (k : Int) {a : M}, 0 < a (0 < k 0 < k * a)
hmul_pos_iff : (k : Int) {a : M}, 0 < a (0 < k * a 0 < k)
hmul_nonneg : {k : Int} {a : M}, 0 k 0 a 0 k * a
namespace NatModule.IsOrdered
variable {M : Type u} [Preorder M] [NatModule M] [NatModule.IsOrdered M]
theorem add_le_right_iff {a b : M} (c : M) : a b c + a c + b := by
rw [add_comm c a, add_comm c b, add_le_left_iff]
theorem add_le_left {a b : M} (h : a b) (c : M) : a + c b + c :=
(add_le_left_iff c).mp h
theorem add_le_right {a b : M} (c : M) (h : a b) : c + a c + b :=
(add_le_right_iff c).mp h
theorem add_lt_left {a b : M} (h : a < b) (c : M) : a + c < b + c := by
simp only [Preorder.lt_iff_le_not_le] at h
constructor
· exact add_le_left h.1 _
· intro w
apply h.2
exact (add_le_left_iff c).mpr w
theorem add_lt_right {a b : M} (c : M) (h : a < b) : c + a < c + b := by
rw [add_comm c a, add_comm c b]
exact add_lt_left h c
theorem add_lt_left_iff {a b : M} (c : M) : a < b a + c < b + c := by
constructor
· exact fun h => add_lt_left h c
· intro w
simp only [Preorder.lt_iff_le_not_le] at w
constructor
· exact (add_le_left_iff c).mpr w.1
· intro h
exact w.2 ((add_le_left_iff c).mp h)
theorem add_lt_right_iff {a b : M} (c : M) : a < b c + a < c + b := by
rw [add_comm c a, add_comm c b, add_lt_left_iff]
theorem hmul_pos_iff {k : Nat} {a : M} (h : 0 < a) : 0 < k * a 0 < k:= by
rw [ hmul_lt_hmul_iff k h, hmul_zero]
theorem hmul_nonneg {k : Nat} {a : M} (h : 0 a) : 0 k * a := by
have := hmul_le_hmul (k := k) h
rwa [hmul_zero] at this
theorem hmul_le_hmul_of_le_of_le_of_nonneg
{k₁ k₂ : Nat} {x y : M} (hk : k₁ k₂) (h : x y) (w : 0 x) :
k₁ * x k₂ * y := by
apply Preorder.le_trans
· change k₁ * x k₂ * x
obtain k', rfl := Nat.exists_eq_add_of_le hk
rw [add_hmul]
conv => lhs; rw [ add_zero (k₁ * x)]
rw [ add_le_right_iff]
exact hmul_nonneg w
· exact hmul_le_hmul h
theorem add_le_add {a b c d : M} (hab : a b) (hcd : c d) : a + c b + d :=
Preorder.le_trans (add_le_right a hcd) (add_le_left hab d)
end NatModule.IsOrdered
namespace IntModule.IsOrdered
variable {M : Type u} [Preorder M] [IntModule M] [IntModule.IsOrdered M]
@@ -41,7 +108,7 @@ theorem neg_pos_iff {a : M} : 0 < -a ↔ a < 0 := by
rw [lt_neg_iff, neg_zero]
theorem add_lt_left {a b : M} (h : a < b) (c : M) : a + c < b + c := by
simp [Preorder.lt_iff_le_not_le] at h
simp only [Preorder.lt_iff_le_not_le] at h
constructor
· exact add_le_left h.1 _
· intro w
@@ -58,12 +125,59 @@ theorem add_lt_right (a : M) {b c : M} (h : b < c) : a + b < a + c := by
rw [add_comm a b, add_comm a c]
exact add_lt_left h a
theorem hmul_neg (k : Int) {a : M} (h : a < 0) : 0 < k k * a < 0 := by
simpa [IntModule.hmul_neg, neg_pos_iff] using hmul_pos k (neg_pos_iff.mpr h)
theorem add_le_left_iff {a b : M} (c : M) : a b a + c b + c := by
constructor
· intro w
exact add_le_left w c
· intro w
have := add_le_left w (-c)
rwa [add_assoc, add_neg_cancel, add_zero, add_assoc, add_neg_cancel, add_zero] at this
theorem add_le_right_iff {a b : M} (c : M) : a b c + a c + b := by
constructor
· intro w
exact add_le_right c w
· intro w
have := add_le_right (-c) w
rwa [ add_assoc, neg_add_cancel, zero_add, add_assoc, neg_add_cancel, zero_add] at this
theorem add_lt_left_iff {a b : M} (c : M) : a < b a + c < b + c := by
constructor
· intro w
exact add_lt_left w c
· intro w
have := add_lt_left w (-c)
rwa [add_assoc, add_neg_cancel, add_zero, add_assoc, add_neg_cancel, add_zero] at this
theorem add_lt_right_iff {a b : M} (c : M) : a < b c + a < c + b := by
constructor
· intro w
exact add_lt_right c w
· intro w
have := add_lt_right (-c) w
rwa [ add_assoc, neg_add_cancel, zero_add, add_assoc, neg_add_cancel, zero_add] at this
theorem sub_nonneg_iff {a b : M} : 0 a - b b a := by
rw [add_le_left_iff b, zero_add, sub_add_cancel]
theorem hmul_neg_iff (k : Int) {a : M} (h : a < 0) : k * a < 0 0 < k := by
simpa [IntModule.hmul_neg, neg_pos_iff] using hmul_pos_iff k (neg_pos_iff.mpr h)
theorem hmul_nonpos {k : Int} {a : M} (hk : 0 k) (ha : a 0) : k * a 0 := by
simpa [IntModule.hmul_neg, neg_nonneg_iff] using hmul_nonneg hk (neg_nonneg_iff.mpr ha)
theorem hmul_le_hmul_of_le_of_le_of_nonneg_of_nonneg
{k₁ k₂ : Int} {x y : M} (hk : k₁ k₂) (h : x y) (w : 0 k₁) (w' : 0 x) :
k₁ * x k₂ * y := by
apply Preorder.le_trans
· have : 0 k₁ * (y - x) := hmul_nonneg w (sub_nonneg_iff.mpr h)
rwa [IntModule.hmul_sub, sub_nonneg_iff] at this
· have : 0 (k₂ - k₁) * y := hmul_nonneg (Int.sub_nonneg.mpr hk) (Preorder.le_trans w' h)
rwa [IntModule.sub_hmul, sub_nonneg_iff] at this
theorem add_le_add {a b c d : M} (hab : a b) (hcd : c d) : a + c b + d :=
Preorder.le_trans (add_le_right a hcd) (add_le_left hab d)
end IntModule.IsOrdered
end Lean.Grind

View File

@@ -91,6 +91,19 @@ theorem trichotomy (a b : α) : a < b a = b b < a := by
| inl h => right; right; exact h
| inr h => right; left; exact h.symm
theorem le_of_not_lt {α} [LinearOrder α] {a b : α} (h : ¬ a < b) : b a := by
cases LinearOrder.trichotomy a b
next => contradiction
next h => apply PartialOrder.le_iff_lt_or_eq.mpr; cases h <;> simp [*]
theorem lt_of_not_le {α} [LinearOrder α] {a b : α} (h : ¬ a b) : b < a := by
cases LinearOrder.trichotomy a b
next h₁ h₂ => have := Preorder.lt_iff_le_not_le.mp h₂; simp [h] at this
next h =>
cases h
next h => subst a; exact False.elim <| h (Preorder.le_refl b)
next => assumption
end LinearOrder
end Lean.Grind

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