Compare commits

..

106 Commits

Author SHA1 Message Date
Leonardo de Moura
03bae7b676 perf: cache visited exprs at CheckAssignmentQuick 2024-09-01 14:22:07 -07:00
Joachim Breitner
a993934839 feat: generate f.eq_unfold lemmas (#5141)
With this, lean produces the following zoo of rewrite rules:
```
Option.map.eq_1      : Option.map f none = none
Option.map.eq_2      : Option.map f (some x) = some (f x)
Option.map.eq_def    : Option.map f p = match o with | none => none | (some x) => some (f x)
Option.map.eq_unfold : Option.map = fun f p => match o with | none => none | (some x) => some (f x)
```

The `f.eq_unfold` variant is especially useful to rewrite with `rw`
under
binders.

This implements and fixes #5110
2024-08-29 16:47:40 +00:00
Joachim Breitner
aa3c87b2c7 fix: conv => arg n to handle .subsingletonInst (#5149)
this fixes #4394, see there for an analysis.
2024-08-29 15:48:31 +00:00
thorimur
869e42b7c3 fix: handle AttributeKinds in LabelAttributes correctly (#3698)
This PR propagates the `AttributeKind` to `SimpleScopedEnvExtension.add`
in attributes created with `register_label_attr`.

This also fixes a nearby stale docstring which referenced `Std`.

---

Closes #3697
2024-08-29 17:57:14 +02:00
Lean stage0 autoupdater
bdbadbd74b chore: update stage0 2024-08-29 13:56:52 +00:00
Henrik Böving
3120c3d8f8 feat: add bv_decide benchmarks (#5203) 2024-08-29 12:45:58 +00:00
FR
e1cbae26cc doc: fix typo in Quotient.liftOn docstring (#5202) 2024-08-29 12:00:36 +00:00
Marc Huisinga
9009c1ac91 fix: ilean loading performance (#4900)
This PR roughly halves the time needed to load the .ilean files by
optimizing the JSON parser and the conversion from JSON to Lean data
structures.

The code is optimized roughly as follows:
- String operations are inlined more aggressively
- Parsers are changed to use new `String.Iterator` functions `curr'` and
`next'` that receive a proof and hence do not need to perform an
additional check
- The `RefIdent` of .ilean files now uses a `String` instead of a `Name`
to avoid the expensive parse step from `String` to `Name` (despite the
fact that we only very rarely actually need a `Name` in downstream code)
- Instead of `List`s and `Subarray`s, the JSON to Lean conversion now
directly passes around arrays and array indices to avoid redundant
boxing
- Parsec's `peek?` sometimes generates redundant `Option` wrappers
because the generation of basic blocks interferes with the ctor-match
optimization, so it is changed to use an `isEof` check where possible
- Early returns and inline-do-blocks cause the code generator to
generate new functions, which then interfere with optimizations, so they
are now avoided
- Mutual defs are used instead of unspecialized passing of higher-order
functions to generate faster code
- The object parser is made tail-recursive

This PR also fixes a stack overflow in `Lean.Json.compress` that would
occur with long lists and adds a benchmark for the .ilean roundtrip
(compressed pretty-printing -> parsing).
2024-08-29 11:51:48 +00:00
Sebastian Ullrich
5c61ad38be chore: revert "chore: temporarily remove test broken by #4746" (#5201)
This reverts commit 7aec6c9ae7.
2024-08-29 08:47:48 +00:00
Kim Morrison
44985dc9a6 chore: remove >6 month deprecations (#5199) 2024-08-29 05:18:44 +00:00
Kim Morrison
3dfa7812f9 chore: cleanup allowUnsafeReducibility (#5198) 2024-08-29 05:12:54 +00:00
Kim Morrison
2dd6b2b9c8 chore: upstream Fin.le_antisymm (#5197) 2024-08-29 04:45:27 +00:00
Kim Morrison
6d0b00885e feat: List.Pairwise_erase and related lemmas (#5196) 2024-08-28 23:11:02 +00:00
Kim Morrison
75c0373c1a feat: lemmas about if-then-else improving confluence (#5191) 2024-08-28 23:10:13 +00:00
Henrik Böving
b37df8e31a chore: update-stage0 2024-08-28 18:14:39 +02:00
Henrik Böving
da9c68a37a feat: import LeanSAT's tactic frontends
Co-authored-by: Markus Himmel <markus@lean-fro.org>
2024-08-28 18:14:39 +02:00
Marc Huisinga
6fce7b82bc fix: duplicate "import out of date" messages (#5185)
This PR fixes a small bug where over time, "import out of data" messages
would accumulate in files when their size changed before restarting its
file worker.
2024-08-28 14:03:17 +00:00
Marc Huisinga
f220efc5ba doc: update quickstart guide for new display name (#5193)
https://github.com/leanprover/vscode-lean4/pull/521 changed the display
name of the VS Code extension so that it can be found more easily when
searching for "Lean" (before it would appear far down in the list). This
PR updates the quickstart guide to reflect this fact.
2024-08-28 13:29:16 +00:00
Kim Morrison
613dbf1637 feat: Int and Nat simp lemmas (#5190)
`@[simp]` lemmas for Int and Nat that improve confluence.
2024-08-28 10:53:28 +00:00
Kim Morrison
8e68c5d44e chore: cleanup simps in CNF.Basic / DHashMap.Internal.List (#5189)
A few unused implementation detail simp lemmas had leaked out and were
being detected by the confluence checker. Just remove them or make them
local.
2024-08-28 06:53:07 +00:00
Kim Morrison
9ce15fb0c6 chore: remove bad simp lemmas (#5180)
This disables some simp lemmas with bad discrimination tree keys, as
identified by @mattrobball on
[zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Infrastructure.20for.20tracking.20frequently.20applied.20simp.20theorems/near/459926416).
2024-08-28 02:55:17 +00:00
Kim Morrison
0dc317c73c feat: restore reduceCtorEq in norm_cast tactic (#5187)
#5167 removed `reduceCtorEq` from the default simproc set. `norm_cast`
relies on it, so we add it back in there.
2024-08-28 02:38:57 +00:00
Jannis Limperg
44366382d3 fix: ignore implementationDetail hyps in rename_i (#5183)
Closes #5176
2024-08-27 14:45:16 +00:00
Markus Himmel
095c7b2bfc chore: deprecate Nat.strongInductionOn (#5179) 2024-08-27 07:18:06 +00:00
Kim Morrison
c4e4248487 chore: remove @[simp] from List.getLast_eq_iff_getLast_eq_some (#5178)
This was not a great simp lemma, and hurts simp confluence. Better to
just use it locally where it is useful.

Similarly `List.head_eq_iff_head?_eq_some`.
2024-08-27 03:23:39 +00:00
Kim Morrison
9ef996259b feat: add BitVec.intMax_add_one 2024-08-27 11:26:16 +10:00
Kim Morrison
30fa18816c feat: activate and use boolToPropSimps 2024-08-27 11:26:16 +10:00
Kim Morrison
94fd406c04 chore: update stage0 2024-08-27 11:26:16 +10:00
Tobias Grosser
3411935e53 feat: add BitVec.intMin
This PR also pulls in some mathlib theorems on testBit and Nat and establishes facts about 2^w that are needed here and which are generally useful for bitvector reasoning.

The following theorem is not generalized to arbitrary x instead of 2, as this would require a condition to be added for x > 1 which would have to be passed to simp each time this theorem should fire.

chore: derive from testBit_two_pow

chore: convert first to prop and then decide

chore: move intMax down as well

chore: add simp set

Add simp-set into this PR

chore: fix simp extension

Move file to src/Lean to fix build

Add prelude

update date

Add university of cambridge as copyright holder

improve naming

use whitespace uniformly

use decide (n = m)

Drop the 'Nat.' namespace

Update src/Init/Data/BitVec/Lemmas.lean

Co-authored-by: Siddharth <siddu.druid@gmail.com>

Update src/Init/Data/BitVec/Lemmas.lean

Co-authored-by: Siddharth <siddu.druid@gmail.com>

Fix build

add some theorems

Revert "add some theorems"

This reverts commit fb97bc2007e371854b40badb3d6014da034c1f5e.

WIP

Shorten proof

Update src/Init/Data/Nat/Lemmas.lean

finish proofs

Update src/Init/Data/BitVec/Lemmas.lean

Co-authored-by: Kim Morrison <scott@tqft.net>

Update src/Init/Data/Nat/Lemmas.lean

Co-authored-by: Kim Morrison <scott@tqft.net>

chore: move BoolToPropSimps
2024-08-27 11:26:16 +10:00
Kim Morrison
b518091bd4 chore: better statement for List.find?_filterMap (#5177) 2024-08-27 00:22:59 +00:00
Siddharth
a58a09056f feat: relate BitVec.signExtend to truncate (#4392)
This adds helper lemmas to relate sign extension to truncation, and as a
corollary shows that sign extension to the same width is a no-op.
2024-08-26 23:39:49 +00:00
Jon Eugster
c45a6a93f9 chore: use emoji variant of ️,️,💥️ (#5173)
First part of #5015, using emoji variant of unicode symbols for
️,️,💥️.

---

(Partially) closes #5015
2024-08-26 19:46:37 +00:00
Leonardo de Moura
f917f811c8 chore: cleanup #5167 workarounds after update stage0 (#5175)
PR #5167 implemented RFC #5046, but it required several workarounds due
to staging issues. This PR cleans up these workarounds.
2024-08-26 17:53:30 +00:00
Lean stage0 autoupdater
3c687df6d5 chore: update stage0 2024-08-26 15:32:27 +00:00
Leonardo de Moura
45475d6434 feat: allow users to disable simpCtorEq simproc (#5167)
`simp only` will not apply this simproc anymore. Users must now write
`simp only [reduceCtorEq]`. See RFC #5046 for motivation.
This PR also renames simproc to `reduceCtorEq`. 

close #5046 


@semorrison A few `simp only ...` tactics will probably break in
Mathlib. Fix: include `reduceCtorEq`.
2024-08-26 13:51:21 +00:00
Tobias Grosser
c6feffa2bd feat: add Bitvec.ofInt_ofNat (#5081)
We use `no_index` to work around special-handling of `OfNat.ofNat` in
`DiscrTree`, which has been reported as an issue in
https://github.com/leanprover/lean4/issues/2867 and is currently in the
process of being fixed in https://github.com/leanprover/lean4/pull/3684.
As the potential fix seems non-trivial and might need some time to
arrive in-tree, we meanwhile add the `no_index` keyword to the
problematic subterm.

---------

Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
2024-08-26 13:12:40 +00:00
Matthew Robert Ballard
b54a9ec9b9 feat: swap arguments to Membership.mem (#5020)
We swap the arguments for `Membership.mem` so that when proceeded by a
`SetLike` coercion, as is often the case in Mathlib, the resulting
expression is recognized as eta expanded and reduce for many
computations. The most beneficial outcome is that the discrimination
tree keys for instances and simp lemmas concerning subsets become more
robust resulting in more efficient searches.

Closes `RFC` #4932

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Henrik Böving <hargonix@gmail.com>
2024-08-26 12:35:47 +00:00
Tobias Grosser
68bb92a35a feat: add BitVec.toInt_[pos|neg]_iff (#5083)
Co-authored-by: Kim Morrison <scott@tqft.net>
2024-08-26 09:44:58 +00:00
Sebastian Ullrich
dcdbb9b411 fix: Syntax.unsetTrailing (#5170)
Fixes #4958
2024-08-26 07:56:04 +00:00
Jeremy Tan Jie Rui
dd22447afd chore: @[elab_as_elim] additions (#5147)
This adds `@[elab_as_elim]` to `Quot.rec`, `Nat.strongInductionOn` and
`Nat.casesStrongInductionOn`, and also renames the latter two to
`Nat.strongRecOn` and `Nat.casesStrongRecOn`.

The first change resolves the todos in
[`Mathlib.Init.Quot`](ca6a6fdc07/Mathlib/Init/Quot.lean)
while the other two are based on a suggestion of @YaelDillies on [the
Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Technical.20Debt.20Counters/near/464804567)
and related to
https://github.com/leanprover-community/mathlib4/pull/16096.
2024-08-26 07:44:54 +00:00
Kim Morrison
f0b0c60e0f chore: running simpNF linter (#5168) 2024-08-26 07:07:52 +00:00
Kim Morrison
9305049f1e feat: lemmas about List.find? and range'/range/iota (#5164) 2024-08-26 04:44:17 +00:00
Kim Morrison
852ee1683f feat: Int lemmas relating neg and emod/mod (#5166) 2024-08-26 03:05:16 +00:00
Kim Morrison
4c9db2fab8 feat: adjusting Int simp lemmas (#5165) 2024-08-26 03:05:10 +00:00
Kim Morrison
70c1e5690d feat: more improvements to List simp confluence (#5163) 2024-08-26 03:04:58 +00:00
Kim Morrison
5d84aebeb9 feat: lemmas about Function.comp that help confluence (#5162) 2024-08-26 03:04:53 +00:00
Kim Morrison
7e5d1103c2 feat: more lemmas about List.pmap/attach (#5160) 2024-08-26 02:15:58 +00:00
Kim Morrison
2d9cbdb450 feat: more List.findSome? lemmas (#5161) 2024-08-26 01:51:40 +00:00
Kim Morrison
fcdecacc4f feat: head/getLast lemmas for List.range (#5158) 2024-08-26 01:48:45 +00:00
Leonardo de Moura
c9c2c8720a fix: PANIC at Fin.isValue (#5159)
closes #4983
2024-08-26 00:36:47 +00:00
Leonardo de Moura
703658391e fix: PANIC at Lean.MVarId.falseOrByContra (#5157)
closes #4985
closes #4984
2024-08-26 00:28:28 +00:00
Kim Morrison
8898c8eaa9 feat: Bool lemmas improving confluence (#5155) 2024-08-25 11:15:07 +00:00
Kim Morrison
2d89693b71 chore: Option lemmas (#5154) 2024-08-25 09:20:24 +00:00
Kim Morrison
c3655b626e chore: remove bad simp lemma in omega theory (#5156) 2024-08-25 07:47:16 +00:00
Wojciech Nawrocki
644a12744b doc: fix option name (#5150)
Small typo fix. I don't believe there is an `autoBoundImplicitLocal`
option.
2024-08-25 07:16:44 +00:00
Kim Morrison
92b271ee64 feat: lemmas about List.erase(|P|Idx) (#5152) 2024-08-25 07:01:46 +00:00
Joachim Breitner
24f550fd6f feat: same equational lemmas for recursive and non-recursive functions (#5129)
This is part of #3983.

After #4154 introduced equational lemmas for non-recursive functions and
#5055
unififed the lemmas for structural and wf recursive funcitons, this now
disables the special handling of recursive functions in
`findMatchToSplit?`, so that the equational lemmas should be the same no
matter how the function was defined.

The new option `eqns.deepRecursiveSplit` can be disabled to get the old
behavior.

### Breaking change

This can break existing code, as there now can be extra equational
lemmas:

* Explicit uses of `f.eq_2` might have to be adjusted if the numbering
  changed.

* Uses of `rw [f]` or `simp [f]` may no longer apply if they previously
  matched (and introduced a `match` statement), when the equational
  lemmas got more fine-grained.

  In this case either case analysis on the parameters before rewriting
  helps, or setting the option `opt.deepRecursiveSplit false` while
  defining the function
2024-08-25 06:51:03 +00:00
Kim Morrison
cee84286e6 feat: improving confluence of List simp lemmas (#5151)
More theorems coming shortly that are easier after these changes, but
I'll test Mathlib on these simp changes first.
2024-08-25 04:32:45 +00:00
Kim Morrison
75781b46f5 feat: lemmas about List.attach/pmap (#5153) 2024-08-25 03:58:54 +00:00
Kim Morrison
ea97aac83b feat: improve Nat simp lemma confluence (#5148) 2024-08-24 11:37:37 +00:00
Kim Morrison
b1ebe7b484 feat: missing Nat.and_xor_distrib_(left|right) (#5146) 2024-08-24 07:46:57 +00:00
Kim Morrison
07013da720 chore: running the simpNF linter over Lean (#5133)
This should resolve nearly all of the simpNF lints. This is a follow-up
to #4620.
2024-08-24 07:10:07 +00:00
Wojciech Nawrocki
2bc87298d9 doc: update user widget manual (#5006)
Updates the user widget manual to account for more recent changes. One
issue is that the samples no longer work on https://live.lean-lang.org/
because it uses an outdated version of the `@leanprover/infoview` NPM
package. They work on https://lean.math.hhu.de/ and in recent versions
of the VSCode extension.
2024-08-23 19:03:39 +00:00
Sebastian Ullrich
390a9a63a2 fix: mixing variable binder updates and declarations (#5142)
Fixes #2143
2024-08-23 09:31:49 +00:00
Sebastian Ullrich
6d4ec153ad feat: ship cadical (#4325)
Co-authored-by: Henrik Böving <hargonix@gmail.com>
2024-08-23 09:13:27 +00:00
Kim Morrison
bf304769e0 feat: misc List lemma updates (#5127) 2024-08-23 01:17:17 +00:00
Kim Morrison
7488b27b0d feat: lemmas about membership of sublists (#5132) 2024-08-23 01:16:53 +00:00
Sebastian Ullrich
33d24c3bca fix: improper handling of strict-implicit section variables (#5138)
This was actually broken even before `include`
2024-08-22 14:20:25 +00:00
Sebastian Ullrich
f71a1fb4ae test: add missing test 2024-08-22 16:48:11 +02:00
Joachim Breitner
01ec8c5e14 doc: unfold tactic docstring (#5109) 2024-08-22 13:58:42 +00:00
Joachim Breitner
d975e4302e feat: fine-grained equational lemmas for non-recursive functions (#4154)
This is part of #3983.

Fine-grained equational lemmas are useful even for non-recursive
functions, so this adds them.

The new option `eqns.nonrecursive` can be set to `false` to have the old
behavior.

### Breaking channge

This is a breaking change: Previously, `rw [Option.map]` would rewrite
`Option.map f o` to `match o with … `. Now this rewrite will fail
because the equational lemmas require constructors here (like they do
for, say, `List.map`).

Remedies:

 * Split on `o` before rewriting.
* Use `rw [Option.map.eq_def]`, which rewrites any (saturated)
application of `Option.map`
* Use `set_option eqns.nonrecursive false` when *defining* the function
in question.

### Interaction with simp

The `simp` tactic so far had a special provision for non-recursive
functions so that `simp [f]` will try to use the equational lemmas, but
will also unfold `f` else, so less breakage here (but maybe performance
improvements with functions with many cases when applied to a
constructor, as the simplifier will no longer unfold to a large
`match`-statement and then collapse it right away).

For projection functions and functions marked `[reducible]`, `simp [f]`
won’t use the equational theorems, and will only use its internal
unfolding machinery.

### Implementation notes

It uses the same `mkEqnTypes` function as for recursive functions, so we
are close to a consistency here. There is still the wrinkle that for
recursive functions we don't split matches without an interesting
recursive call inside. Unifying that is future work.
2024-08-22 13:26:58 +00:00
Henrik Böving
74715a0f9c feat: support for secure temporary files (#5125)
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-08-22 13:01:40 +00:00
Kim Morrison
d540ba787a feat: Option lemmas (#5128) 2024-08-22 12:43:16 +00:00
Kim Morrison
b33d08078d feat: more lemmas about List.append (#5131) 2024-08-22 12:42:57 +00:00
Kim Morrison
e9025bdf79 feat: lemmas about List.join (#5130) 2024-08-22 12:09:45 +00:00
Sebastian Ullrich
5651a11ac8 feat: improve unused section variable warning (#5036)
See
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Opt.20out.20of.20.22included.20section.20variable.20is.20not.20used.22.20linter
2024-08-22 10:18:09 +00:00
Kim Morrison
481894e95d feat: range/iota lemmas (#5123) 2024-08-22 06:09:42 +00:00
Kim Morrison
7213583c8d feat: lemmas about List.find? (#5124) 2024-08-22 06:09:42 +00:00
Lean stage0 autoupdater
6a473e67aa chore: update stage0 2024-08-21 21:35:52 +00:00
Joachim Breitner
e5d44f4033 fix: hover text over _ in ?_ (#5118)
in principle we'd like to use the existing parser
```
   "?" >> (ident <|> hole)
```
but somehow annotate it so that hovering the `hole` will not show the
hole's hover. But for now it was easier to just change the parser to
```
   "?" >> (ident <|> "_")
```
and be done with it.

Fixes #5021
2024-08-21 20:47:19 +00:00
Joachim Breitner
c78bb62c51 fix: get_elem_tactic_trivial to not loop in the presence of mvars (#5119)
The goal at the crucial step is
```
a : Array Nat
i : Fin ?m.27
⊢ ↑i < a.size
```
and after the `apply Fin.val_lt_of_le;` we have
```
a : Array Nat
i : Fin ?m.27
⊢ ?m.27 ≤ a.size
```
and now `apply Fin.val_lt_of_le` applies again, due to accidential
defeq. Adding `with_reducible` helps here.

fixes #5061
2024-08-21 19:51:58 +00:00
Joachim Breitner
e620cf3c80 fix: count let-bound variables in induction … with correctly (#5117)
This fixes #5058 and is a follow-up to #3505.
2024-08-21 18:49:51 +00:00
Henrik Böving
edecf3d4ba chore: move Lean.Data.Parsec to Std.Internal.Parsec (#5115)
Again as discussed for bootstrapping reasons.
2024-08-21 15:26:17 +00:00
Lean stage0 autoupdater
1c73983dcf chore: update stage0 2024-08-21 14:11:59 +00:00
Sebastian Ullrich
4b7b69c20a feat: omit (#5000) 2024-08-21 13:22:34 +00:00
Henrik Böving
87d361d9b6 chore: move LeanSAT logic to Std (#5113)
As discussed for bootstrapping reasons. The only new files here are
`Std.Tactic` and `Std.Tactic.BVDecide`. The rest is move +
renamespacing.
2024-08-21 13:00:41 +00:00
Lean stage0 autoupdater
a3ae75f847 chore: update stage0 2024-08-21 12:24:33 +00:00
Kim Morrison
0a8d1bf808 feat: basic instances for ULift and PLift (#5112) 2024-08-21 11:37:13 +00:00
Kim Morrison
a58da122b9 feat: change statement of List.getLast?_cons (#5106)
To avoid using `getLastD`, which is not simp-normal-form.
2024-08-21 10:59:34 +00:00
Kim Morrison
3b1af163eb feat: adjust List simp lemmas (#5102)
I'll do this in a few stages, testing against Mathlib as we go.
2024-08-21 07:25:36 +00:00
Kim Morrison
0e823710e3 feat: Nat.add_left_eq_self and relatives (#5104) 2024-08-21 04:11:57 +00:00
Kim Morrison
c38d271283 feat: lemmas about Option/if-then-else (#5101) 2024-08-21 03:16:48 +00:00
Kim Morrison
4dbd20343f chore: remove @[simp] from mem_of_find?_eq_some (#5105) 2024-08-21 03:16:22 +00:00
Kim Morrison
0203cb091d feat: more aggressive simp lemmas for List.subset (#5103) 2024-08-21 03:14:23 +00:00
Kim Morrison
f6ce866e39 chore: add mergeSort lemmas (#5107)
Some missing easy lemmas.
2024-08-21 03:03:05 +00:00
Henrik Böving
95549f17da feat: LeanSAT's LRAT parsers + SAT solver interface (#5100)
Step 5/6 in upstreaming LeanSAT.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2024-08-20 11:42:26 +00:00
Joachim Breitner
15c6ac2076 chore: restart-on-label: Also filter by commit SHA (#5099) 2024-08-20 07:45:43 +00:00
Kim Morrison
4aa74d9c0b feat: List.mergeSort (#5092)
Defines `mergeSort`, a naive stable merge sort algorithm, replaces it
via a `@[csimp]` lemma with something faster at runtime, and proves the
following results:

* `mergeSort_sorted`: `mergeSort` produces a sorted list.
* `mergeSort_perm`: `mergeSort` is a permutation of the input list.
* `mergeSort_of_sorted`: `mergeSort` does not change a sorted list.
* `mergeSort_cons`: proves `mergeSort le (x :: xs) = l₁ ++ x :: l₂` for
some `l₁, l₂`
so that `mergeSort le xs = l₁ ++ l₂`, and no `a ∈ l₁` satisfies `le a
x`.
* `mergeSort_stable`: if `c` is a sorted sublist of `l`, then `c` is
still a sublist of `mergeSort le l`.
2024-08-20 06:32:52 +00:00
Joachim Breitner
efbecf272d feat: explain reduce steps in trace.Debug.Meta.Tactic.simp (#5054) 2024-08-19 15:05:13 +00:00
Joachim Breitner
78146190e5 feat: mutual recursion: allow common prefix up to alpha-equivalence (#5041)
@arthur-adjedj was very confused when a mutually recursive definition
didn't work as expected, and the reason was that he used different names
for the fixed parameters.

It seems plausible to simply allow that and calculate the fixed-prefix
up to alpha renaming.

It does mean, though, that, for example, termination proof goals will
mention the names as used by the first function. But probably better
than simply failing. And we could even fix that later (by passing down
the
actual names, and renmaing the variables in the context of the mvar,
depending on the “current function”) should it bother our users.
2024-08-19 15:00:03 +00:00
Joachim Breitner
b4db495f98 feat: unify equational theorems between wf and structural recursion (#5055)
by removing the `tryRefl` variation between the two.

Part of #3983
2024-08-19 14:59:15 +00:00
Henrik Böving
9f47e08ecc feat: import LeanSAT LRAT (#5074)
This PR imports LeanSAT's LRAT module as step 4/~6 (step 7 could go
after I did some refactorings to import this) of the LeanSAT
upstreaming. It is the last large component, after this only the LRAT
parser and the reflection tactic that hooks everything up to the meta
level remains. In particular it is the last component that contains
notable proofs, yay!

Again a few remarks:
1. Why is this not in `Std`? I'm not quite sure whether it should be
there. At the current level of code/proof quality we can certainly not
import the checker itself into `Std` but maybe having the data type as
well as the trimming algorithm there might be of interested? I'm hoping
that as we refactor the checker in the future its quality will be high
enough to be also put into `Std`. At this point we would have a full AIG
-> CNF -> LRAT verification pipeline in `Std` for everyone to use. One
additional blocker in this is that we cannot provide the parsers for the
format in `Std` as of today because `Parsec` is still in `Lean` so that
would also have to change.
2. There do exist two abstraction levels to make sure we can swap out
the LRAT implementation at any time:
- The public interface is just all files in the top level `LRAT`
directory. It basically only contains the LRAT format itself, the
checker + soundness proof and the trimming algorithm. As long as we
don't need to change their API (which we shouldn't have to I think) we
can always swap out the entire `Internal` directory without breaking
anything else in LeanSAT.
- The `Internal` module itself contains another layer of abstraction in
the form of the `Formula` class. This allows us to swap out the most
complex component in `Internal` as well, without having to touch any of
the infrastructure that is built around it either.
3. I mostly performed stylistic cleanups on the `Internal` module. In my
experience over upgrading to many nightlies during the course of LeanSAT
development, I have gotten these proofs cleaned up to the point, where
they only break if we change the `List` or `Array` proof API
significantly. Given that we are currently in the process of stabilizing
it I'm hoping that these proofs do not have to be touched anymore unless
we do something crazy. All of the custom theory that the LRAT component
developed around various basic data types has been upstreamed into Lean
over the course of various other PRs.
4. If there are some simple tricks that we can pull off to increase the
code / proof quality in `Internal` and in particular `Internal.Formula`
(this module is not for the light-hearted Lean reviewer) I'm all for it.
Otherwise the best course of action to provide LeanSAT to our users soon
would probably be to merge it as is and do a cut + rewrite at one of the
two interface points described above.
2024-08-19 14:31:00 +00:00
Joachim Breitner
728980443f refactor: rename new option to debug.rawDecreasingByGoal (#5066)
as suggested by @semorrison in 

https://leanprover.zulipchat.com/#narrow/stream/116290-rss/topic/cleanDecreasingBy/near/462659021


Follow-up to #5016.
2024-08-19 11:53:54 +00:00
Sebastian Ullrich
ca945be133 fix: disable incrementality in case .. | .. (#5090) 2024-08-19 09:17:03 +00:00
Eric Wieser
f2573dc51e fix: Do not overwrite existing signal handlers (#5062)
Such handlers can come from address sanitizers and similar. When
combined with #4971, this forward-ports
676b9bc477
/ rust-lang/rust#69685

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-08-19 09:11:38 +00:00
Joachim Breitner
51f01d8c8a feat: expose index option to dsimp tactic (#5071)
makes the option introduced in #4202 also available when using `dsimp`
2024-08-19 07:57:16 +00:00
869 changed files with 15339 additions and 1454 deletions

View File

@@ -176,7 +176,7 @@ jobs:
"check-level": 2,
"CMAKE_PRESET": "debug",
// exclude seriously slow tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress'"
},
// TODO: suddenly started failing in CI
/*{

View File

@@ -14,8 +14,9 @@ jobs:
# (unfortunately cannot search by PR number, only base branch,
# and that is't even unique given PRs from forks, but the risk
# of confusion is low and the danger is mild)
run_id=$(gh run list -e pull_request -b "$head_ref" --workflow 'CI' --limit 1 \
--limit 1 --json databaseId --jq '.[0].databaseId')
echo "Trying to find a run with branch $head_ref and commit $head_sha"
run_id="$(gh run list -e pull_request -b "$head_ref" -c "$head_sha" \
--workflow 'CI' --limit 1 --json databaseId --jq '.[0].databaseId')"
echo "Run id: ${run_id}"
gh run view "$run_id"
echo "Cancelling (just in case)"
@@ -29,5 +30,6 @@ jobs:
shell: bash
env:
head_ref: ${{ github.head_ref }}
head_sha: ${{ github.event.pull_request.head.sha }}
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}

View File

@@ -30,6 +30,35 @@ if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
endif()
# Don't do anything with cadical on wasm
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# On CI Linux, we source cadical from Nix instead; see flake.nix
find_program(CADICAL cadical)
if(NOT CADICAL)
set(CADICAL_CXX c++)
find_program(CCACHE ccache)
if(CCACHE)
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
endif()
# missing stdio locking API on Windows
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif()
ExternalProject_add(cadical
PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-1.9.5
CONFIGURE_COMMAND ""
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
BUILD_IN_SOURCE ON
INSTALL_COMMAND "")
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
set(EXTRA_DEPENDS "cadical")
endif()
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
endif()
ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src

View File

@@ -43,3 +43,5 @@
/src/Init/Guard.lean @digama0
/src/Lean/Server/CodeActions/ @digama0
/src/Std/ @TwoFX
/src/Std/Tactic/BVDecide/ @hargoniX
/src/Lean/Elab/Tactic/BVDecide/ @hargoniX

View File

@@ -1341,3 +1341,33 @@ whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
==============================================================================
CaDiCaL is under the MIT License:
==============================================================================
MIT License
Copyright (c) 2016-2021 Armin Biere, Johannes Kepler University Linz, Austria
Copyright (c) 2020-2021 Mathias Fleury, Johannes Kepler University Linz, Austria
Copyright (c) 2020-2021 Nils Froleyks, Johannes Kepler University Linz, Austria
Copyright (c) 2022-2024 Katalin Fazekas, Vienna University of Technology, Austria
Copyright (c) 2021-2024 Armin Biere, University of Freiburg, Germany
Copyright (c) 2021-2024 Mathias Fleury, University of Freiburg, Germany
Copyright (c) 2023-2024 Florian Pollitt, University of Freiburg, Germany
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -4,15 +4,18 @@ open Lean Widget
/-!
# The user-widgets system
Proving and programming are inherently interactive tasks. Lots of mathematical objects and data
structures are visual in nature. *User widgets* let you associate custom interactive UIs with
sections of a Lean document. User widgets are rendered in the Lean infoview.
Proving and programming are inherently interactive tasks.
Lots of mathematical objects and data structures are visual in nature.
*User widgets* let you associate custom interactive UIs
with sections of a Lean document.
User widgets are rendered in the Lean infoview.
![Rubik's cube](../images/widgets_rubiks.png)
## Trying it out
To try it out, simply type in the following code and place your cursor over the `#widget` command.
To try it out, type in the following code and place your cursor over the `#widget` command.
You can also [view this manual entry in the online editor](https://live.lean-lang.org/#url=https%3A%2F%2Fraw.githubusercontent.com%2Fleanprover%2Flean4%2Fmaster%2Fdoc%2Fexamples%2Fwidgets.lean).
-/
@[widget_module]
@@ -21,38 +24,37 @@ def helloWidget : Widget.Module where
import * as React from 'react';
export default function(props) {
const name = props.name || 'world'
return React.createElement('p', {}, name + '!')
return React.createElement('p', {}, 'Hello ' + name + '!')
}"
#widget helloWidget
/-!
If you want to dive into a full sample right away, check out
[`RubiksCube`](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/).
[`Rubiks`](https://github.com/leanprover-community/ProofWidgets4/blob/main/ProofWidgets/Demos/Rubiks.lean).
This sample uses higher-level widget components from the ProofWidgets library.
Below, we'll explain the system piece by piece.
⚠️ WARNING: All of the user widget APIs are **unstable** and subject to breaking changes.
## Widget sources and instances
## Widget modules and instances
A *widget source* is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
which exports a [React component](https://reactjs.org/docs/components-and-props.html). To access
React, the module must use `import * as React from 'react'`. Our first example of a widget source
is of course the value of `helloWidget.javascript`.
A [widget module](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/UserWidget.html#Lean.Widget.Module)
is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
that can execute in the Lean infoview.
Most widget modules export a [React component](https://reactjs.org/docs/components-and-props.html)
as the piece of user interface to be rendered.
To access React, the module can use `import * as React from 'react'`.
Our first example of a widget module is `helloWidget` above.
Widget modules must be registered with the `@[widget_module]` attribute.
We can register a widget source with the `@[widget]` attribute, giving it a friendlier name
in the `name` field. This is bundled together in a `UserWidgetDefinition`.
A *widget instance* is then the identifier of a `UserWidgetDefinition` (so `` `helloWidget ``,
not `"Hello"`) associated with a range of positions in the Lean source code. Widget instances
are stored in the *infotree* in the same manner as other information about the source file
such as the type of every expression. In our example, the `#widget` command stores a widget instance
with the entire line as its range. We can think of a widget instance as an instruction for the
infoview: "when the user places their cursor here, please render the following widget".
Every widget instance also contains a `props : Json` value. This value is passed as an argument
to the React component. In our first invocation of `#widget`, we set it to `.null`. Try out what
happens when you type in:
A [widget instance](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/Types.html#Lean.Widget.WidgetInstance)
is then the identifier of a widget module (e.g. `` `helloWidget ``)
bundled with a value for its props.
This value is passed as the argument to the React component.
In our first invocation of `#widget`, we set it to `.null`.
Try out what happens when you type in:
-/
structure HelloWidgetProps where
@@ -62,21 +64,37 @@ structure HelloWidgetProps where
#widget helloWidget with { name? := "<your name here>" : HelloWidgetProps }
/-!
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
is the web-based infoview in VSCode.
Under the hood, widget instances are associated with a range of positions in the source file.
Widget instances are stored in the *infotree*
in the same manner as other information about the source file
such as the type of every expression.
In our example, the `#widget` command stores a widget instance
with the entire line as its range.
One can think of the infotree entry as an instruction for the infoview:
"when the user places their cursor here, please render the following widget".
-/
/-!
## Querying the Lean server
Besides enabling us to create cool client-side visualizations, user widgets come with the ability
to communicate with the Lean server. Thanks to this, they have the same metaprogramming capabilities
as custom elaborators or the tactic framework. To see this in action, let's implement a `#check`
command as a web input form. This example assumes some familiarity with React.
💡 NOTE: The RPC system presented below does not depend on JavaScript.
However, the primary use case is the web-based infoview in VSCode.
The first thing we'll need is to create an *RPC method*. Meaning "Remote Procedure Call", this
is basically a Lean function callable from widget code (possibly remotely over the internet).
Besides enabling us to create cool client-side visualizations,
user widgets have the ability to communicate with the Lean server.
Thanks to this, they have the same metaprogramming capabilities
as custom elaborators or the tactic framework.
To see this in action, let's implement a `#check` command as a web input form.
This example assumes some familiarity with React.
The first thing we'll need is to create an *RPC method*.
Meaning "Remote Procedure Call",this is a Lean function callable from widget code
(possibly remotely over the internet).
Our method will take in the `name : Name` of a constant in the environment and return its type.
By convention, we represent the input data as a `structure`. Since it will be sent over from JavaScript,
we need `FromJson` and `ToJson`. We'll see below why the position field is needed.
By convention, we represent the input data as a `structure`.
Since it will be sent over from JavaScript,
we need `FromJson` and `ToJson` instnace.
We'll see why the position field is needed later.
-/
structure GetTypeParams where
@@ -87,25 +105,33 @@ structure GetTypeParams where
deriving FromJson, ToJson
/-!
After its arguments, we define the `getType` method. Every RPC method executes in the `RequestM`
monad and must return a `RequestTask α` where `α` is its "actual" return type. The `Task` is so
that requests can be handled concurrently. A first guess for `α` might be `Expr`. However,
expressions in general can be large objects which depend on an `Environment` and `LocalContext`.
Thus we cannot directly serialize an `Expr` and send it to the widget. Instead, there are two
options:
- One is to send a *reference* which points to an object residing on the server. From JavaScript's
point of view, references are entirely opaque, but they can be sent back to other RPC methods for
further processing.
- Two is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
This representation contains extra data which the infoview uses for interactivity. We take this
strategy here.
After its argument structure, we define the `getType` method.
RPCs method execute in the `RequestM` monad and must return a `RequestTask α`
where `α` is the "actual" return type.
The `Task` is so that requests can be handled concurrently.
As a first guess, we'd use `Expr` as `α`.
However, expressions in general can be large objects
which depend on an `Environment` and `LocalContext`.
Thus we cannot directly serialize an `Expr` and send it to JavaScript.
Instead, there are two options:
RPC methods execute in the context of a file, but not any particular `Environment` so they don't
know about the available `def`initions and `theorem`s. Thus, we need to pass in a position at which
we want to use the local `Environment`. This is why we store it in `GetTypeParams`. The `withWaitFindSnapAtPos`
method launches a concurrent computation whose job is to find such an `Environment` and a bit
more information for us, in the form of a `snap : Snapshot`. With this in hand, we can call
`MetaM` procedures to find out the type of `name` and pretty-print it.
- One is to send a *reference* which points to an object residing on the server.
From JavaScript's point of view, references are entirely opaque,
but they can be sent back to other RPC methods for further processing.
- The other is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
This representation contains extra data which the infoview uses for interactivity.
We take this strategy here.
RPC methods execute in the context of a file,
but not of any particular `Environment`,
so they don't know about the available `def`initions and `theorem`s.
Thus, we need to pass in a position at which we want to use the local `Environment`.
This is why we store it in `GetTypeParams`.
The `withWaitFindSnapAtPos` method launches a concurrent computation
whose job is to find such an `Environment` for us,
in the form of a `snap : Snapshot`.
With this in hand, we can call `MetaM` procedures
to find out the type of `name` and pretty-print it.
-/
open Server RequestM in
@@ -121,18 +147,22 @@ def getType (params : GetTypeParams) : RequestM (RequestTask CodeWithInfos) :=
/-!
## Using infoview components
Now that we have all we need on the server side, let's write the widget source. By importing
`@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
For example, the `<InteractiveCode>` component displays expressions with `term : type` tooltips
as seen in the goal view. We will use it to implement our custom `#check` display.
Now that we have all we need on the server side, let's write the widget module.
By importing `@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
For example, the `<InteractiveCode>` component displays expressions
with `term : type` tooltips as seen in the goal view.
We will use it to implement our custom `#check` display.
⚠️ WARNING: Like the other widget APIs, the infoview JS API is **unstable** and subject to breaking changes.
The code below demonstrates useful parts of the API. To make RPC method calls, we use the `RpcContext`.
The `useAsync` helper packs the results of a call into an `AsyncState` structure which indicates
whether the call has resolved successfully, has returned an error, or is still in-flight. Based
on this we either display an `InteractiveCode` with the type, `mapRpcError` the error in order
to turn it into a readable message, or show a `Loading..` message, respectively.
The code below demonstrates useful parts of the API.
To make RPC method calls, we invoke the `useRpcSession` hook.
The `useAsync` helper packs the results of an RPC call into an `AsyncState` structure
which indicates whether the call has resolved successfully,
has returned an error, or is still in-flight.
Based on this we either display an `InteractiveCode` component with the result,
`mapRpcError` the error in order to turn it into a readable message,
or show a `Loading..` message, respectively.
-/
@[widget_module]
@@ -140,10 +170,10 @@ def checkWidget : Widget.Module where
javascript := "
import * as React from 'react';
const e = React.createElement;
import { RpcContext, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
import { useRpcSession, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
export default function(props) {
const rs = React.useContext(RpcContext)
const rs = useRpcSession()
const [name, setName] = React.useState('getType')
const st = useAsync(() =>
@@ -159,7 +189,7 @@ export default function(props) {
"
/-!
Finally we can try out the widget.
We can now try out the widget.
-/
#widget checkWidget
@@ -169,30 +199,31 @@ Finally we can try out the widget.
## Building widget sources
While typing JavaScript inline is fine for a simple example, for real developments we want to use
packages from NPM, a proper build system, and JSX. Thus, most actual widget sources are built with
Lake and NPM. They consist of multiple files and may import libraries which don't work as ESModules
by default. On the other hand a widget source must be a single, self-contained ESModule in the form
of a string. Readers familiar with web development may already have guessed that to obtain such a
string, we need a *bundler*. Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
and [`esbuild`](https://esbuild.github.io/). If we go with `rollup.js`, to make a widget work with
the infoview we need to:
While typing JavaScript inline is fine for a simple example,
for real developments we want to use packages from NPM, a proper build system, and JSX.
Thus, most actual widget sources are built with Lake and NPM.
They consist of multiple files and may import libraries which don't work as ESModules by default.
On the other hand a widget module must be a single, self-contained ESModule in the form of a string.
Readers familiar with web development may already have guessed that to obtain such a string, we need a *bundler*.
Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
and [`esbuild`](https://esbuild.github.io/).
If we go with `rollup.js`, to make a widget work with the infoview we need to:
- Set [`output.format`](https://rollupjs.org/guide/en/#outputformat) to `'es'`.
- [Externalize](https://rollupjs.org/guide/en/#external) `react`, `react-dom`, `@leanprover/infoview`.
These libraries are already loaded by the infoview so they should not be bundled.
In the RubiksCube sample, we provide a working `rollup.js` build configuration in
[rollup.config.js](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/widget/rollup.config.js).
ProofWidgets provides a working `rollup.js` build configuration in
[rollup.config.js](https://github.com/leanprover-community/ProofWidgets4/blob/main/widget/rollup.config.js).
## Inserting text
We can also instruct the editor to insert text, copy text to the clipboard, or
reveal a certain location in the document.
To do this, use the `React.useContext(EditorContext)` React context.
This will return an `EditorConnection` whose `api` field contains a number of methods to
interact with the text editor.
Besides making RPC calls, widgets can instruct the editor to carry out certain actions.
We can insert text, copy text to the clipboard, or highlight a certain location in the document.
To do this, use the `EditorContext` React context.
This will return an `EditorConnection`
whose `api` field contains a number of methods that interact with the editor.
You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
The full API can be viewed [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52).
-/
@[widget_module]
@@ -212,6 +243,4 @@ export default function(props) {
}
"
/-! Finally, we can try this out: -/
#widget insertTextWidget

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -5,11 +5,11 @@ See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
1. Install [VS Code](https://code.visualstudio.com/).
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
1. Launch VS Code and install the `Lean 4` extension by clicking on the 'Extensions' sidebar entry and searching for 'Lean 4'.
![installing the vscode-lean4 extension](images/code-ext.png)
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Docs: Show Setup Guide".
1. Open the Lean 4 setup guide by creating a new text file using 'File > New Text File' (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting 'Documentation… > Docs: Show Setup Guide'.
![show setup guide](images/show-setup-guide.png)

17
flake.lock generated
View File

@@ -34,6 +34,22 @@
"type": "github"
}
},
"nixpkgs-cadical": {
"locked": {
"lastModified": 1722221733,
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"type": "github"
}
},
"nixpkgs-old": {
"flake": false,
"locked": {
@@ -55,6 +71,7 @@
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"nixpkgs-cadical": "nixpkgs-cadical",
"nixpkgs-old": "nixpkgs-old"
}
},

View File

@@ -5,6 +5,8 @@
# old nixpkgs used for portable release with older glibc (2.27)
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
inputs.nixpkgs-old.flake = false;
# for cadical 1.9.5; sync with CMakeLists.txt
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
@@ -14,6 +16,11 @@
pkgsDist-old = import nixpkgs-old { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
cadical = if pkgs.stdenv.isLinux then
# use statically-linked cadical on Linux to avoid glibc versioning troubles
pkgsCadical.pkgsStatic.cadical.overrideAttrs { doCheck = false; }
else pkgsCadical.cadical;
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; };
@@ -21,11 +28,9 @@
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp libuv ccache
cmake gmp libuv ccache cadical
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb
# TODO: only add when proven to not affect the flakification
#pkgs.python3
tree # for CI
];
# https://github.com/NixOS/nixpkgs/issues/60919

View File

@@ -1,5 +1,5 @@
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
stdenv, lib, cmake, gmp, libuv, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
stdenv, lib, cmake, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
... } @ args:
with builtins;
lib.warn "The Nix-based build is deprecated" rec {
@@ -17,7 +17,7 @@ lib.warn "The Nix-based build is deprecated" rec {
'';
} // args // {
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
preConfigure = args.preConfigure or "" + ''
# ignore absence of submodule
sed -i 's!lake/Lake.lean!!' CMakeLists.txt
@@ -158,7 +158,7 @@ lib.warn "The Nix-based build is deprecated" rec {
test = buildCMake {
name = "lean-test-${desc}";
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
buildInputs = [ gmp libuv perl git ];
buildInputs = [ gmp libuv perl git cadical ];
preConfigure = ''
cd src
'';

View File

@@ -0,0 +1,17 @@
**breaking change**
The effect of the `variable` command on proofs of `theorem`s has been changed. Whether such section variables are accessible in the proof now depends only on the theorem signature and other top-level commands, not on the proof itself.
This change ensures that
* the statement of a theorem is independent of its proof. In other words, changes in the proof cannot change the theorem statement.
* tactics such as `induction` cannot accidentally include a section variable.
* the proof can be elaborated in parallel to subsequent declarations in a future version of Lean.
The effect of `variable`s on the theorem header as well as on other kinds of declarations is unchanged.
Specifically, section variables are included if they
* are directly referenced by the theorem header,
* are included via the new `include` command in the current section and not subsequently mentioned in an `omit` statement,
* are directly referenced by any variable included by these rules, OR
* are instance-implicit variables that reference only variables included by these rules.
For porting, a new option `deprecated.oldSectionVars` is included to locally switch back to the old behavior.

View File

@@ -535,6 +535,12 @@ else()
OUTPUT_NAME leancpp)
endif()
if((${STAGE} GREATER 0) AND CADICAL)
add_custom_target(copy-cadical
COMMAND cmake -E copy_if_different "${CADICAL}" "${CMAKE_BINARY_DIR}/bin/cadical${CMAKE_EXECUTABLE_SUFFIX}")
add_dependencies(leancpp copy-cadical)
endif()
# MSYS2 bash usually handles Windows paths relatively well, but not when putting them in the PATH
string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
@@ -633,6 +639,10 @@ file(COPY ${LEAN_SOURCE_DIR}/bin/leanmake DESTINATION ${CMAKE_BINARY_DIR}/bin)
install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION bin)
if (${STAGE} GREATER 0 AND CADICAL)
install(PROGRAMS "${CADICAL}" DESTINATION bin)
endif()
add_custom_target(clean-stdlib
COMMAND rm -rf "${CMAKE_BINARY_DIR}/lib" || true)

View File

@@ -57,7 +57,7 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
-- We don't mark this as `simp` as it is already handled by `ite_eq_right_iff`.
theorem ite_some_none_eq_none [Decidable P] :
(if P then some x else none) = none ¬ P := by
simp only [ite_eq_right_iff]
simp only [ite_eq_right_iff, reduceCtorEq]
rfl
@[simp] theorem ite_some_none_eq_some [Decidable P] :

View File

@@ -313,12 +313,6 @@ instance optionCoe {α : Type u} : Coe α (Option α) where
instance subtypeCoe {α : Sort u} {p : α Prop} : CoeOut (Subtype p) α where
coe v := v.val
instance boolPredToPred : Coe (α Bool) (α Prop) where
coe r := fun a => Eq (r a) true
instance boolRelToRel : Coe (α α Bool) (α α Prop) where
coe r := fun a b => Eq (r a b) true
/-! # Coe bridge -/
/--

View File

@@ -36,6 +36,17 @@ and `flip (·<·)` is the greater-than relation.
theorem Function.comp_def {α β δ} (f : β δ) (g : α β) : f g = fun x => f (g x) := rfl
@[simp] theorem Function.const_comp {f : α β} {c : γ} :
(Function.const β c f) = Function.const α c := by
rfl
@[simp] theorem Function.comp_const {f : β γ} {b : β} :
(f Function.const α b) = Function.const α (f b) := by
rfl
@[simp] theorem Function.true_comp {f : α β} : ((fun _ => true) f) = fun _ => true := by
rfl
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false := by
rfl
attribute [simp] namedPattern
/--
@@ -1553,7 +1564,7 @@ so you should consider the simpler versions if they apply:
* `Quot.recOnSubsingleton`, when the target type is a `Subsingleton`
* `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption
-/
protected abbrev rec
@[elab_as_elim] protected abbrev rec
(f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
(q : Quot r) : motive q :=
@@ -1639,7 +1650,7 @@ protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s → Pro
/--
The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`,
then it lifts to a function on `Quotient s` such that `lift (mk a) f h = f a`.
then it lifts to a function on `Quotient s` such that `liftOn (mk a) f h = f a`.
-/
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α β) (c : (a b : α) a b f a = f b) : β :=
Quot.liftOn q f c

View File

@@ -37,3 +37,5 @@ import Init.Data.Cast
import Init.Data.Sum
import Init.Data.BEq
import Init.Data.Subtype
import Init.Data.ULift
import Init.Data.PLift

View File

@@ -260,7 +260,7 @@ theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm
simp [ContextInformation.isComm, Option.isSome] at h
match h₂ : ctx.comm with
| none =>
simp only [h₂] at h
simp [h₂] at h
| some val =>
simp [h₂] at h
exact val.down

View File

@@ -13,11 +13,11 @@ namespace Array
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
-- NB: This is defined as a structure rather than a plain def so that a lemma
-- like `sizeOf_lt_of_mem` will not apply with no actual arrays around.
structure Mem (a : α) (as : Array α) : Prop where
structure Mem (as : Array α) (a : α) : Prop where
val : a as.data
instance : Membership α (Array α) where
mem a as := Mem a as
mem := Mem
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
cases as with | _ as =>

View File

@@ -152,9 +152,6 @@ theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
getLsb (BitVec.ofNat n x) i = (i < n && x.testBit i) := by
simp [getLsb, BitVec.ofNat, Fin.val_ofNat']
@[simp, deprecated toNat_ofNat (since := "2024-02-22")]
theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
@[simp] theorem getLsb_zero : (0#w).getLsb i = false := by simp [getLsb]
@[simp] theorem getMsb_zero : (0#w).getMsb i = false := by simp [getMsb]
@@ -303,6 +300,17 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
@[simp] theorem ofInt_natCast (w n : Nat) :
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
@[simp] theorem ofInt_ofNat (w n : Nat) :
BitVec.ofInt w (no_index (OfNat.ofNat n)) = BitVec.ofNat w (OfNat.ofNat n) := rfl
theorem toInt_neg_iff {w : Nat} {x : BitVec w} :
BitVec.toInt x < 0 2 ^ w 2 * x.toNat := by
simp [toInt_eq_toNat_cond]; omega
theorem toInt_pos_iff {w : Nat} {x : BitVec w} :
0 BitVec.toInt x 2 * x.toNat < 2 ^ w := by
simp [toInt_eq_toNat_cond]; omega
/-! ### zeroExtend and truncate -/
theorem truncate_eq_zeroExtend {v : Nat} {x : BitVec w} :
@@ -413,11 +421,9 @@ theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k := b
(x.truncate l).truncate k = x.truncate k :=
zeroExtend_zeroExtend_of_le x h
/--Truncating by the bitwidth has no effect. -/
@[simp]
theorem truncate_eq_self {x : BitVec w} : x.truncate w = x := by
ext i
simp [getLsb_zeroExtend]
/-- Truncating by the bitwidth has no effect. -/
-- This doesn't need to be a `@[simp]` lemma, as `zeroExtend_eq` applies.
theorem truncate_eq_self {x : BitVec w} : x.truncate w = x := zeroExtend_eq _
@[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k := by
apply eq_of_getLsb_eq
@@ -776,7 +782,6 @@ theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
@[simp]
theorem shiftLeft_eq' {x : BitVec w₁} {y : BitVec w₂} : x <<< y = x <<< y.toNat := by rfl
@[simp]
theorem shiftLeft_zero' {x : BitVec w₁} : x <<< 0#w₂ = x := by simp
theorem shiftLeft_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {z : BitVec w₃} :
@@ -986,7 +991,7 @@ theorem signExtend_eq_not_zeroExtend_not_of_msb_false {x : BitVec w} {v : Nat} (
ext i
by_cases hv : i < v
· simp only [signExtend, getLsb, getLsb_zeroExtend, hv, decide_True, Bool.true_and, toNat_ofInt,
BitVec.toInt_eq_msb_cond, hmsb, reduceIte]
BitVec.toInt_eq_msb_cond, hmsb, reduceIte, reduceCtorEq]
rw [Int.ofNat_mod_ofNat, Int.toNat_ofNat, Nat.testBit_mod_two_pow]
simp [BitVec.testBit_toNat]
· simp only [getLsb_zeroExtend, hv, decide_False, Bool.false_and]
@@ -1022,6 +1027,18 @@ theorem signExtend_eq_not_zeroExtend_not_of_msb_true {x : BitVec w} {v : Nat} (h
· rw [signExtend_eq_not_zeroExtend_not_of_msb_true hmsb]
by_cases (i < v) <;> by_cases (i < w) <;> simp_all <;> omega
/-- Sign extending to a width smaller than the starting width is a truncation. -/
theorem signExtend_eq_truncate_of_lt (x : BitVec w) {v : Nat} (hv : v w):
x.signExtend v = x.truncate v := by
ext i
simp only [getLsb_signExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_zeroExtend,
ite_eq_left_iff, Nat.not_lt]
omega
/-- Sign extending to the same bitwidth is a no op. -/
theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_truncate_of_lt _ (Nat.le_refl _), truncate_eq]
/-! ### append -/
theorem append_def (x : BitVec v) (y : BitVec w) :
@@ -1449,20 +1466,6 @@ protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x
simp
exact Nat.lt_of_le_of_ne
/-! ### intMax -/
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) : BitVec w := BitVec.ofNat w (2^w - 1)
theorem getLsb_intMax_eq (w : Nat) : (intMax w).getLsb i = decide (i < w) := by
simp [intMax, getLsb]
theorem toNat_intMax_eq : (intMax w).toNat = 2^w - 1 := by
have h : 2^w - 1 < 2^w := by
have pos : 2^w > 0 := Nat.pow_pos (by decide)
omega
simp [intMax, Nat.shiftLeft_eq, Nat.one_mul, natCast_eq_ofNat, toNat_ofNat, Nat.mod_eq_of_lt h]
/-! ### ofBoolList -/
@[simp] theorem getMsb_ofBoolListBE : (ofBoolListBE bs).getMsb i = bs.getD i false := by
@@ -1786,4 +1789,54 @@ theorem getLsb_replicate {n w : Nat} (x : BitVec w) :
simp only [show ¬i < w * n by omega, decide_False, cond_false, hi, Bool.false_and]
apply BitVec.getLsb_ge (x := x) (i := i - w * n) (ge := by omega)
/-! ### intMin -/
/-- The bitvector of width `w` that has the smallest value when interpreted as an integer. -/
abbrev intMin (w : Nat) := twoPow w (w - 1)
theorem getLsb_intMin (w : Nat) : (intMin w).getLsb i = decide (i + 1 = w) := by
simp only [getLsb_twoPow, boolToPropSimps]
omega
@[simp, bv_toNat]
theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
simp
@[simp]
theorem neg_intMin {w : Nat} : -intMin w = intMin w := by
by_cases h : 0 < w
· simp [bv_toNat, h]
· simp only [Nat.not_lt, Nat.le_zero_eq] at h
simp [bv_toNat, h]
/-! ### intMax -/
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
abbrev intMax (w : Nat) := (twoPow w (w - 1)) - 1
@[simp, bv_toNat]
theorem toNat_intMax : (intMax w).toNat = 2 ^ (w - 1) - 1 := by
simp only [intMax]
by_cases h : w = 0
· simp [h]
· have h' : 0 < w := by omega
rw [toNat_sub, toNat_twoPow, Nat.sub_add_comm (by simpa [h'] using Nat.one_le_two_pow),
Nat.add_sub_assoc (by simpa [h'] using Nat.one_le_two_pow),
Nat.two_pow_pred_mod_two_pow h', ofNat_eq_ofNat, toNat_ofNat, Nat.one_mod_two_pow h',
Nat.add_mod_left, Nat.mod_eq_of_lt]
have := Nat.two_pow_pred_lt_two_pow h'
have := Nat.two_pow_pos w
omega
@[simp]
theorem getLsb_intMax (w : Nat) : (intMax w).getLsb i = decide (i + 1 < w) := by
rw [ testBit_toNat, toNat_intMax, Nat.testBit_two_pow_sub_one, decide_eq_decide]
omega
@[simp] theorem intMax_add_one {w : Nat} : intMax w + 1#w = intMin w := by
simp only [toNat_eq, toNat_intMax, toNat_add, toNat_intMin, toNat_ofNat, Nat.add_mod_mod]
by_cases h : w = 0
· simp [h]
· rw [Nat.sub_add_cancel (Nat.two_pow_pos (w - 1)), Nat.two_pow_pred_mod_two_pow (by omega)]
end BitVec

View File

@@ -55,6 +55,12 @@ theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
-- These lemmas assist with confluence.
@[simp] theorem eq_false_imp_eq_true_iff :
(a b : Bool), ((a = false b = true) (b = false a = true)) = True := by decide
@[simp] theorem eq_true_imp_eq_false_iff :
(a b : Bool), ((a = true b = false) (b = true a = false)) = True := by decide
/-! ### and -/
@[simp] theorem and_self_left : (a b : Bool), (a && (a && b)) = (a && b) := by decide
@@ -91,6 +97,11 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
@[simp] theorem iff_self_and : (a b : Bool), (a = (a && b)) (a b) := by decide
@[simp] theorem iff_and_self : (a b : Bool), (b = (a && b)) (b a) := by decide
@[simp] theorem not_and_iff_left_iff_imp : (a b : Bool), ((!a && b) = a) !a !b := by decide
@[simp] theorem and_not_iff_right_iff_imp : (a b : Bool), ((a && !b) = b) !a !b := by decide
@[simp] theorem iff_not_self_and : (a b : Bool), (a = (!a && b)) !a !b := by decide
@[simp] theorem iff_and_not_self : (a b : Bool), (b = (a && !b)) !a !b := by decide
/-! ### or -/
@[simp] theorem or_self_left : (a b : Bool), (a || (a || b)) = (a || b) := by decide
@@ -120,6 +131,11 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
@[simp] theorem iff_self_or : (a b : Bool), (a = (a || b)) (b a) := by decide
@[simp] theorem iff_or_self : (a b : Bool), (b = (a || b)) (a b) := by decide
@[simp] theorem not_or_iff_left_iff_imp : (a b : Bool), ((!a || b) = a) a b := by decide
@[simp] theorem or_not_iff_right_iff_imp : (a b : Bool), ((a || !b) = b) a b := by decide
@[simp] theorem iff_not_self_or : (a b : Bool), (a = (!a || b)) a b := by decide
@[simp] theorem iff_or_not_self : (a b : Bool), (b = (a || !b)) a b := by decide
theorem or_comm : (x y : Bool), (x || y) = (y || x) := by decide
instance : Std.Commutative (· || ·) := or_comm
@@ -134,7 +150,7 @@ theorem and_or_distrib_right : ∀ (x y z : Bool), ((x || y) && z) = (x && z ||
theorem or_and_distrib_left : (x y z : Bool), (x || y && z) = ((x || y) && (x || z)) := by decide
theorem or_and_distrib_right : (x y z : Bool), (x && y || z) = ((x || z) && (y || z)) := by decide
theorem and_xor_distrib_left : (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
theorem and_xor_distrib_left : (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
theorem and_xor_distrib_right : (x y z : Bool), (xor x y && z) = xor (x && z) (y && z) := by decide
/-- De Morgan's law for boolean and -/
@@ -202,8 +218,11 @@ instance : Std.LawfulIdentity (· != ·) false where
@[simp] theorem not_beq_self : (x : Bool), ((!x) == x) = false := by decide
@[simp] theorem beq_not_self : (x : Bool), (x == !x) = false := by decide
@[simp] theorem not_bne_self : (x : Bool), ((!x) != x) = true := by decide
@[simp] theorem bne_not_self : (x : Bool), (x != !x) = true := by decide
@[simp] theorem not_bne : (a b : Bool), ((!a) != b) = !(a != b) := by decide
@[simp] theorem bne_not : (a b : Bool), (a != !b) = !(a != b) := by decide
theorem not_bne_self : (x : Bool), ((!x) != x) = true := by decide
theorem bne_not_self : (x : Bool), (x != !x) = true := by decide
/-
Added for equivalence with `Bool.not_beq_self` and needed for confluence
@@ -235,8 +254,10 @@ theorem beq_eq_decide_eq [BEq α] [LawfulBEq α] [DecidableEq α] (a b : α) :
· simp [ne_of_beq_false h]
· simp [eq_of_beq h]
@[simp] theorem not_eq_not : {a b : Bool}, ¬a = !b a = b := by decide
theorem eq_not : (a b : Bool), (a = (!b)) (a b) := by decide
theorem not_eq : (a b : Bool), ((!a) = b) (a b) := by decide
@[simp] theorem not_eq_not : {a b : Bool}, ¬a = !b a = b := by decide
@[simp] theorem not_not_eq : {a b : Bool}, ¬(!a) = b a = b := by decide
@[simp] theorem coe_iff_coe : (a b : Bool), (a b) a = b := by decide
@@ -360,9 +381,6 @@ def toNat (b : Bool) : Nat := cond b 1 0
theorem toNat_le (c : Bool) : c.toNat 1 := by
cases c <;> trivial
@[deprecated toNat_le (since := "2024-02-23")]
abbrev toNat_le_one := toNat_le
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
Nat.lt_succ_of_le (toNat_le _)
@@ -427,16 +445,18 @@ theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
cases h with | _ p => simp [p]
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = true then True else b = true`
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
`if b = true then True else b = true`.
However the discrimination tree key is just `→`, so this is tried too often.
-/
@[simp] theorem eq_false_imp_eq_true : (b:Bool), (b = false b = true) (b = true) := by decide
theorem eq_false_imp_eq_true : (b:Bool), (b = false b = true) (b = true) := by decide
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = false then True else b = false`
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
`if b = false then True else b = false`.
However the discrimination tree key is just `→`, so this is tried too often.
-/
@[simp] theorem eq_true_imp_eq_false : (b:Bool), (b = true b = false) (b = false) := by decide
theorem eq_true_imp_eq_false : (b:Bool), (b = true b = false) (b = false) := by decide
/-! ### forall -/
@@ -509,6 +529,10 @@ protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := co
@[simp] theorem cond_true_right : (c t : Bool), cond c t true = (!c || t) := by decide
@[simp] theorem cond_false_right : (c t : Bool), cond c t false = ( c && t) := by decide
-- These restore confluence between the above lemmas and `cond_not`.
@[simp] theorem cond_true_not_same : (c b : Bool), cond c (!c) b = (!c && b) := by decide
@[simp] theorem cond_false_not_same : (c b : Bool), cond c b (!c) = (!c || b) := by decide
@[simp] theorem cond_true_same : (c b : Bool), cond c c b = (c || b) := by decide
@[simp] theorem cond_false_same : (c b : Bool), cond c b c = (c && b) := by decide
@@ -522,7 +546,7 @@ theorem apply_cond (f : α → β) {b : Bool} {a a' : α} :
f (bif b then a else a') = bif b then f a else f a' := by
cases b <;> simp
/-# decidability -/
/-! # decidability -/
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true
@@ -538,6 +562,21 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
decide (p q) = (decide p == decide q) := by
cases dp with | _ p => simp [p]
@[boolToPropSimps]
theorem and_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p && q) = decide (p q) := by
cases dp with | _ p => simp [p]
@[boolToPropSimps]
theorem or_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p || q) = decide (p q) := by
cases dp with | _ p => simp [p]
@[boolToPropSimps]
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(decide p == decide q) = decide (p q) := by
cases dp with | _ p => simp [p]
end Bool
export Bool (cond_eq_if)
@@ -549,3 +588,19 @@ export Bool (cond_eq_if)
@[simp] theorem true_eq_decide_iff {p : Prop} [h : Decidable p] : true = decide p p := by
cases h with | _ q => simp [q]
/-! ### coercions -/
/--
This should not be turned on globally as an instance because it degrades performance in Mathlib,
but may be used locally.
-/
def boolPredToPred : Coe (α Bool) (α Prop) where
coe r := fun a => Eq (r a) true
/--
This should not be turned on globally as an instance because it degrades performance in Mathlib,
but may be used locally.
-/
def boolRelToRel : Coe (α α Bool) (α α Prop) where
coe r := fun a b => Eq (r a b) true

View File

@@ -275,6 +275,22 @@ def atEnd : Iterator → Bool
def hasNext : Iterator Bool
| arr, i => i < arr.size
/-- The byte at the current position. --/
@[inline]
def curr' (it : Iterator) (h : it.hasNext) : UInt8 :=
match it with
| arr, i =>
have : i < arr.size := by
simp only [hasNext, decide_eq_true_eq] at h
assumption
arr[i]
/-- Moves the iterator's position forward by one byte. --/
@[inline]
def next' (it : Iterator) (_h : it.hasNext) : Iterator :=
match it with
| arr, i => arr, i + 1
/-- True if the position is not zero. -/
@[inline]
def hasPrev : Iterator Bool

View File

@@ -63,27 +63,27 @@ instance : Inhabited Char where
default := 'A'
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
def isWhitespace (c : Char) : Bool :=
@[inline] def isWhitespace (c : Char) : Bool :=
c = ' ' || c = '\t' || c = '\r' || c = '\n'
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
def isUpper (c : Char) : Bool :=
@[inline] def isUpper (c : Char) : Bool :=
c.val 65 && c.val 90
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
def isLower (c : Char) : Bool :=
@[inline] def isLower (c : Char) : Bool :=
c.val 97 && c.val 122
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
def isAlpha (c : Char) : Bool :=
@[inline] def isAlpha (c : Char) : Bool :=
c.isUpper || c.isLower
/-- Is the character in `0123456789`? -/
def isDigit (c : Char) : Bool :=
@[inline] def isDigit (c : Char) : Bool :=
c.val 48 && c.val 57
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
def isAlphanum (c : Char) : Bool :=
@[inline] def isAlphanum (c : Char) : Bool :=
c.isAlpha || c.isDigit
/-- Convert an upper case character to its lower case character.

View File

@@ -11,9 +11,6 @@ import Init.ByCases
import Init.Conv
import Init.Omega
-- Remove after the next stage0 update
set_option allowUnsafeReducibility true
namespace Fin
/-- If you actually have an element of `Fin n`, then the `n` is always positive -/
@@ -57,9 +54,6 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[simp] theorem val_ofNat' (a : Nat) (is_pos : n > 0) :
(Fin.ofNat' a is_pos).val = a % n := rfl
@[deprecated ofNat'_zero_val (since := "2024-02-22")]
theorem ofNat'_zero_val : (Fin.ofNat' 0 h).val = 0 := Nat.zero_mod _
@[simp] theorem mod_val (a b : Fin n) : (a % b).val = a.val % b.val :=
rfl
@@ -141,6 +135,12 @@ theorem eq_zero_or_eq_succ {n : Nat} : ∀ i : Fin (n + 1), i = 0 ∃ j : Fi
theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i 0) : j : Fin n, i = j.succ :=
(eq_zero_or_eq_succ i).resolve_left hi
protected theorem le_antisymm_iff {x y : Fin n} : x = y x y y x :=
Fin.ext_iff.trans Nat.le_antisymm_iff
protected theorem le_antisymm {x y : Fin n} (h1 : x y) (h2 : y x) : x = y :=
Fin.le_antisymm_iff.2 h1, h2
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by

View File

@@ -10,5 +10,6 @@ import Init.Data.Int.DivMod
import Init.Data.Int.DivModLemmas
import Init.Data.Int.Gcd
import Init.Data.Int.Lemmas
import Init.Data.Int.LemmasAux
import Init.Data.Int.Order
import Init.Data.Int.Pow

View File

@@ -14,9 +14,6 @@ import Init.RCases
# Lemmas about integer division needed to bootstrap `omega`.
-/
-- Remove after the next stage0 update
set_option allowUnsafeReducibility true
open Nat (succ)
namespace Int
@@ -57,7 +54,7 @@ protected theorem dvd_mul_right (a b : Int) : a a * b := ⟨_, rfl⟩
protected theorem dvd_mul_left (a b : Int) : b a * b := _, Int.mul_comm ..
protected theorem neg_dvd {a b : Int} : -a b a b := by
@[simp] protected theorem neg_dvd {a b : Int} : -a b a b := by
constructor <;> exact fun k, e =>
-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]
@@ -357,6 +354,7 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c a) : (a + b) / c = a / c +
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a 0) : (a * b) / a = b :=
Int.mul_comm .. Int.mul_ediv_cancel _ H
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b 0 a 0 := by
rw [Int.div_def]
match b, h with
@@ -454,6 +452,12 @@ theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
@[simp] theorem add_mul_emod_self_left (a b c : Int) : (a + b * c) % b = a % b := by
rw [Int.mul_comm, Int.add_mul_emod_self]
@[simp] theorem add_neg_mul_emod_self {a b c : Int} : (a + -(b * c)) % c = a % c := by
rw [Int.neg_mul_eq_neg_mul, add_mul_emod_self]
@[simp] theorem add_neg_mul_emod_self_left {a b c : Int} : (a + -(b * c)) % b = a % b := by
rw [Int.neg_mul_eq_mul_neg, add_mul_emod_self_left]
@[simp] theorem add_emod_self {a b : Int} : (a + b) % b = a % b := by
have := add_mul_emod_self_left a b 1; rwa [Int.mul_one] at this
@@ -498,9 +502,12 @@ theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_emod_self_left,
Int.mul_assoc, add_mul_emod_self]
@[local simp] theorem emod_self {a : Int} : a % a = 0 := by
@[simp] theorem emod_self {a : Int} : a % a = 0 := by
have := mul_emod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem neg_emod_self (a : Int) : -a % a = 0 := by
rw [neg_emod, Int.sub_self, zero_emod]
@[simp] theorem emod_emod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n % k) % m = n % m := by
conv => rhs; rw [ emod_add_ediv n k]
@@ -596,6 +603,14 @@ theorem emod_eq_zero_of_dvd : ∀ {a b : Int}, a b → b % a = 0
theorem dvd_iff_emod_eq_zero (a b : Int) : a b b % a = 0 :=
emod_eq_zero_of_dvd, dvd_of_emod_eq_zero
@[simp] theorem neg_mul_emod_left (a b : Int) : -(a * b) % b = 0 := by
rw [ dvd_iff_emod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_left a b
@[simp] theorem neg_mul_emod_right (a b : Int) : -(a * b) % a = 0 := by
rw [ dvd_iff_emod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_right a b
instance decidableDvd : DecidableRel (α := Int) (· ·) := fun _ _ =>
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
@@ -620,6 +635,12 @@ theorem neg_ediv_of_dvd : ∀ {a b : Int}, b a → (-a) / b = -(a / b)
· simp [bz]
· rw [Int.neg_mul_eq_mul_neg, Int.mul_ediv_cancel_left _ bz, Int.mul_ediv_cancel_left _ bz]
@[simp] theorem neg_mul_ediv_cancel (a b : Int) (h : b 0) : -(a * b) / b = -a := by
rw [neg_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ h]
@[simp] theorem neg_mul_ediv_cancel_left (a b : Int) (h : a 0) : -(a * b) / a = -b := by
rw [neg_ediv_of_dvd (Int.dvd_mul_right a b), mul_ediv_cancel_left _ h]
theorem sub_ediv_of_dvd (a : Int) {b c : Int}
(hcb : c b) : (a - b) / c = a / c - b / c := by
rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg, Int.add_ediv_of_dvd_right (Int.dvd_neg.2 hcb)]
@@ -635,13 +656,22 @@ theorem sub_ediv_of_dvd (a : Int) {b c : Int}
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp] protected theorem neg_ediv_self (a : Int) (h : a 0) : (-a) / a = -1 := by
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
@[simp]
theorem emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
theorem emod_sub_cancel (x y : Int): (x - y) % y = x % y := by
by_cases h : y = 0
· simp [h]
· simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
@[simp] theorem add_neg_emod_self (a b : Int) : (a + -b) % b = a % b := by
rw [ Int.sub_eq_add_neg, emod_sub_cancel]
@[simp] theorem neg_add_emod_self (a b : Int) : (-a + b) % a = b % a := by
rw [Int.add_comm, add_neg_emod_self]
/-- If `a % b = c` then `b` divides `a - c`. -/
theorem dvd_sub_of_emod_eq {a b c : Int} (h : a % b = c) : b a - c := by
have hx : (a % b) % b = c % b := by
@@ -891,6 +921,14 @@ theorem mod_eq_zero_of_dvd : ∀ {a b : Int}, a b → mod b a = 0
theorem dvd_iff_mod_eq_zero (a b : Int) : a b mod b a = 0 :=
mod_eq_zero_of_dvd, dvd_of_mod_eq_zero
@[simp] theorem neg_mul_mod_right (a b : Int) : (-(a * b)).mod a = 0 := by
rw [ dvd_iff_mod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_right a b
@[simp] theorem neg_mul_mod_left (a b : Int) : (-(a * b)).mod b = 0 := by
rw [ dvd_iff_mod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_left a b
protected theorem div_mul_cancel {a b : Int} (H : b a) : a.div b * b = a :=
div_mul_cancel_of_mod_eq_zero (mod_eq_zero_of_dvd H)
@@ -903,6 +941,10 @@ protected theorem eq_mul_of_div_eq_right {a b c : Int}
@[simp] theorem mod_self {a : Int} : a.mod a = 0 := by
have := mul_mod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem neg_mod_self (a : Int) : (-a).mod a = 0 := by
rw [ dvd_iff_mod_eq_zero, Int.dvd_neg]
exact Int.dvd_refl a
theorem lt_div_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.div b + 1) * b := by
rw [Int.add_mul, Int.one_mul, Int.mul_comm]
exact Int.lt_add_of_sub_left_lt <| Int.mod_def .. mod_lt_of_pos _ H
@@ -1091,8 +1133,7 @@ theorem bmod_mul_bmod : Int.bmod (Int.bmod x n * y) n = Int.bmod (x * y) n := by
next p =>
simp
next p =>
rw [Int.sub_mul, Int.sub_eq_add_neg, Int.mul_neg]
simp
rw [Int.sub_mul, Int.sub_eq_add_neg, Int.mul_neg, bmod_add_mul_cancel, emod_mul_bmod_congr]
@[simp] theorem mul_bmod_bmod : Int.bmod (x * Int.bmod y n) n = Int.bmod (x * y) n := by
rw [Int.mul_comm x, bmod_mul_bmod, Int.mul_comm x]

View File

@@ -7,6 +7,7 @@ prelude
import Init.Data.Int.Basic
import Init.Conv
import Init.NotationExtra
import Init.PropLemmas
namespace Int
@@ -288,7 +289,7 @@ protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
simp [Int.sub_eq_add_neg, Int.add_assoc]
protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
@[simp] protected theorem sub_add_cancel (a b : Int) : a - b + b = a :=
Int.neg_add_cancel_right a b
@@ -444,10 +445,10 @@ protected theorem neg_mul_eq_neg_mul (a b : Int) : -(a * b) = -a * b :=
protected theorem neg_mul_eq_mul_neg (a b : Int) : -(a * b) = a * -b :=
Int.neg_eq_of_add_eq_zero <| by rw [ Int.mul_add, Int.add_right_neg, Int.mul_zero]
@[local simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
@[simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
(Int.neg_mul_eq_neg_mul a b).symm
@[local simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
@[simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
(Int.neg_mul_eq_mul_neg a b).symm
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp
@@ -486,6 +487,9 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 b = 0 := by
protected theorem mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 :=
Or.rec a0 b0 Int.mul_eq_zero.mp
@[simp] protected theorem mul_ne_zero_iff (a b : Int) : a * b 0 a 0 b 0 := by
rw [ne_eq, Int.mul_eq_zero, not_or, ne_eq]
protected theorem eq_of_mul_eq_mul_right {a b c : Int} (ha : a 0) (h : b * a = c * a) : b = c :=
have : (b - c) * a = 0 := by rwa [Int.sub_mul, Int.sub_eq_zero]
Int.sub_eq_zero.1 <| (Int.mul_eq_zero.mp this).resolve_right ha

View File

@@ -0,0 +1,40 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Int.Order
import Init.Omega
/-!
# Further lemmas about `Int` relying on `omega` automation.
-/
namespace Int
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : a.toNat - b = (a - b).toNat := by
simp only [Int.toNat]
split <;> rename_i x a
· simp only [Int.ofNat_eq_coe]
split <;> rename_i y b h
· simp at h
omega
· simp [Int.negSucc_eq] at h
omega
· simp only [Nat.zero_sub]
split <;> rename_i y b h
· simp [Int.negSucc_eq] at h
omega
· rfl
@[simp] theorem toNat_sub_max_self (a : Int) : (a - max a 0).toNat = 0 := by
simp [toNat]
split <;> simp_all <;> omega
@[simp] theorem toNat_sub_self_max (a : Int) : (a - max 0 a).toNat = 0 := by
simp [toNat]
split <;> simp_all <;> omega
end Int

View File

@@ -240,9 +240,24 @@ theorem le_natAbs {a : Int} : a ≤ natAbs a :=
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
Int.not_le.1 fun h => let _, h := eq_ofNat_of_zero_le h; nomatch h
theorem negSucc_le_zero (n : Nat) : -[n+1] 0 :=
Int.le_of_lt (negSucc_lt_zero n)
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 -[n+1] False := by
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (ofNat_zero_le n)]
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
rw [Int.max_eq_right (ofNat_zero_le n)]
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
rw [Int.max_eq_right (negSucc_le_zero _)]
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
rw [Int.max_eq_left (negSucc_le_zero _)]
protected theorem add_le_add_left {a b : Int} (h : a b) (c : Int) : c + a c + b :=
let n, hn := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
@@ -470,8 +485,16 @@ theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
@[simp] theorem toNat_ofNat (n : Nat) : toNat n = n := rfl
@[simp] theorem toNat_negSucc (n : Nat) : (Int.negSucc n).toNat = 0 := by
simp [toNat]
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
match a with
| Int.ofNat n => simp
| Int.negSucc n => simp
theorem self_le_toNat (a : Int) : a toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
@[simp] theorem le_toNat {n : Nat} {z : Int} (h : 0 z) : n z.toNat (n : Int) z := by
@@ -1006,7 +1029,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
theorem eq_nat_or_neg (a : Int) : n : Nat, a = n a = -n := _, natAbs_eq a
theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs]
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs.eq_def]
@[simp] theorem natAbs_mul_self' (a : Int) : (natAbs a * natAbs a : Int) = a * a := by
rw [ Int.ofNat_mul, natAbs_mul_self]

View File

@@ -22,3 +22,4 @@ import Init.Data.List.Sublist
import Init.Data.List.TakeDrop
import Init.Data.List.Zip
import Init.Data.List.Perm
import Init.Data.List.Sort

View File

@@ -73,6 +73,13 @@ theorem pmap_map {p : β → Prop} (g : ∀ b, p b → γ) (f : α → β) (l H)
· rfl
· simp only [*, pmap, map]
@[simp] theorem attach_cons (x : α) (xs : List α) :
(x :: xs).attach = x, mem_cons_self x xs :: xs.attach.map fun y, h => y, mem_cons_of_mem x h := by
simp only [attach, attachWith, pmap, map_pmap, cons.injEq, true_and]
apply pmap_congr
intros a _ m' _
rfl
theorem pmap_eq_map_attach {p : α Prop} (f : a, p a β) (l H) :
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
rw [attach, attachWith, map_pmap]; exact pmap_congr l fun _ _ _ _ => rfl
@@ -86,7 +93,7 @@ theorem attach_map_val (l : List α) (f : α → β) : (l.attach.map fun i => f
@[simp]
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
(attach_map_coe _ _).trans l.map_id
(attach_map_coe _ _).trans (List.map_id _)
theorem countP_attach (l : List α) (p : α Bool) : l.attach.countP (fun a : {x // x l} => p a) = l.countP p := by
simp only [ Function.comp_apply (g := Subtype.val), countP_map, attach_map_subtype_val]
@@ -121,23 +128,14 @@ theorem length_attach (L : List α) : L.attach.length = L.length :=
theorem pmap_eq_nil {p : α Prop} {f : a, p a β} {l H} : pmap f l H = [] l = [] := by
rw [ length_eq_zero, length_pmap, length_eq_zero]
theorem pmap_ne_nil {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) : xs.pmap f H [] xs [] := by
simp
@[simp]
theorem attach_eq_nil (l : List α) : l.attach = [] l = [] :=
pmap_eq_nil
theorem getLast_pmap (p : α Prop) (f : a, p a β) (l : List α)
(hl₁ : a l, p a) (hl₂ : l []) :
(l.pmap f hl₁).getLast (mt List.pmap_eq_nil.1 hl₂) =
f (l.getLast hl₂) (hl₁ _ (List.getLast_mem hl₂)) := by
induction l with
| nil => apply (hl₂ rfl).elim
| cons l_hd l_tl l_ih =>
by_cases hl_tl : l_tl = []
· simp [hl_tl]
· simp only [pmap]
rw [getLast_cons, l_ih _ hl_tl]
simp only [getLast_cons hl_tl]
theorem getElem?_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) (n : Nat) :
(pmap f l h)[n]? = Option.pmap f l[n]? fun x H => h x (getElem?_mem H) := by
induction l generalizing n with
@@ -181,7 +179,22 @@ theorem get_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h :
simp only [get_eq_getElem]
simp [getElem_pmap]
theorem pmap_append {p : ι Prop} (f : a : ι, p a α) (l₁ l₂ : List ι)
@[simp] theorem head?_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) : (xs.pmap f H).head? = xs.attach.head?.map fun a, m => f a (H a m) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp at ih
simp [head?_pmap, ih]
@[simp] theorem head_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) (h : xs.pmap f H []) :
(xs.pmap f H).head h = f (xs.head (by simpa using h)) (H _ (head_mem _)) := by
induction xs with
| nil => simp at h
| cons x xs ih => simp [head_pmap, ih]
@[simp] theorem pmap_append {p : ι Prop} (f : a : ι, p a α) (l₁ l₂ : List ι)
(h : a l₁ ++ l₂, p a) :
(l₁ ++ l₂).pmap f h =
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
@@ -197,3 +210,63 @@ theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ l₂ :
((l₁ ++ l₂).pmap f fun a ha => (List.mem_append.1 ha).elim (h₁ a) (h₂ a)) =
l₁.pmap f h₁ ++ l₂.pmap f h₂ :=
pmap_append f l₁ l₂ _
@[simp] theorem pmap_reverse {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs.reverse P a) : xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
induction xs <;> simp_all
theorem reverse_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) : (xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
rw [pmap_reverse]
@[simp] theorem attach_append (xs ys : List α) :
(xs ++ ys).attach = xs.attach.map (fun x, h => x, mem_append_of_mem_left ys h) ++
ys.attach.map fun x, h => x, mem_append_of_mem_right xs h := by
simp only [attach, attachWith, pmap, map_pmap, pmap_append]
congr 1 <;>
exact pmap_congr _ fun _ _ _ _ => rfl
@[simp] theorem attach_reverse (xs : List α) : xs.reverse.attach = xs.attach.reverse.map fun x, h => x, by simpa using h := by
simp only [attach, attachWith, reverse_pmap, map_pmap]
apply pmap_congr
intros
rfl
theorem reverse_attach (xs : List α) : xs.attach.reverse = xs.reverse.attach.map fun x, h => x, by simpa using h := by
simp only [attach, attachWith, reverse_pmap, map_pmap]
apply pmap_congr
intros
rfl
theorem getLast?_attach {xs : List α} :
xs.attach.getLast? = match h : xs.getLast? with | none => none | some a => some a, mem_of_getLast?_eq_some h := by
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map]
split <;> rename_i h
· simp only [getLast?_eq_none_iff] at h
subst h
simp
· obtain ys, rfl := getLast?_eq_some_iff.mp h
simp
@[simp] theorem getLast?_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) : (xs.pmap f H).getLast? = xs.attach.getLast?.map fun a, m => f a (H a m) := by
simp only [getLast?_eq_head?_reverse]
rw [reverse_pmap, reverse_attach, head?_map, pmap_eq_map_attach, head?_map]
simp only [Option.map_map]
congr
@[simp] theorem getLast_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) (h : xs.pmap f H []) :
(xs.pmap f H).getLast h = f (xs.getLast (by simpa using h)) (H _ (getLast_mem _)) := by
simp only [getLast_eq_iff_getLast_eq_some, getLast?_pmap, Option.map_eq_some', Subtype.exists]
refine xs.getLast (by simpa using h), by simp, ?_
simp only [getLast?_attach, and_true]
split <;> rename_i h'
· simp only [getLast?_eq_none_iff] at h'
subst h'
simp at h
· symm
simpa [getLast_eq_iff_getLast_eq_some]
end List

View File

@@ -96,7 +96,7 @@ namespace List
/-! ### concat -/
@[simp high] theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
induction as with
| nil => rfl
| cons _ xs ih => simp [concat, ih]
@@ -278,8 +278,9 @@ def getLastD : (as : List α) → (fallback : α) → α
| [], a₀ => a₀
| a::as, _ => getLast (a::as) (fun h => List.noConfusion h)
@[simp] theorem getLastD_nil (a) : @getLastD α [] a = a := rfl
@[simp] theorem getLastD_cons (a b l) : @getLastD α (b::l) a = getLastD l b := by cases l <;> rfl
-- These aren't `simp` lemmas since we always simplify `getLastD` in terms of `getLast?`.
theorem getLastD_nil (a) : @getLastD α [] a = a := rfl
theorem getLastD_cons (a b l) : @getLastD α (b::l) a = getLastD l b := by cases l <;> rfl
/-! ## Head and tail -/
@@ -688,7 +689,7 @@ inductive Mem (a : α) : List α → Prop
| tail (b : α) {as : List α} : Mem a as Mem a (b::as)
instance : Membership α (List α) where
mem := Mem
mem l a := Mem a l
theorem mem_of_elem_eq_true [BEq α] [LawfulBEq α] {a : α} {as : List α} : elem a as = true a as := by
match as with
@@ -962,6 +963,26 @@ def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists f
@[inherit_doc] infixl:50 " <:+: " => IsInfix
/-! ### splitAt -/
/--
Split a list at an index.
```
splitAt 2 [a, b, c] = ([a, b], [c])
```
-/
def splitAt (n : Nat) (l : List α) : List α × List α := go l n [] where
/--
Auxiliary for `splitAt`:
`splitAt.go l xs n acc = (acc.reverse ++ take n xs, drop n xs)` if `n < xs.length`,
and `(l, [])` otherwise.
-/
go : List α Nat List α List α × List α
| [], _, _ => (l, []) -- This branch ensures the pointer equality of the result with the input
-- without any runtime branching cost.
| x :: xs, n+1, acc => go xs n (x :: acc)
| xs, _, acc => (acc.reverse, xs)
/-! ### rotateLeft -/
/--

View File

@@ -222,7 +222,7 @@ theorem append_cancel_right {as bs cs : List α} (h : as ++ bs = cs ++ bs) : as
next => apply append_cancel_right
next => intro h; simp [h]
@[simp] theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
match as, i with
| a::as, 0, _ => simp_arith [get]
| a::as, i+1, h =>

View File

@@ -47,11 +47,11 @@ theorem length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a
if h : p x then
rw [countP_cons_of_pos _ _ h, countP_cons_of_neg _ _ _, length, ih]
· rw [Nat.add_assoc, Nat.add_comm _ 1, Nat.add_assoc]
· simp only [h, not_true_eq_false, decide_False, not_false_eq_true]
· simp [h]
else
rw [countP_cons_of_pos (fun a => ¬p a) _ _, countP_cons_of_neg _ _ h, length, ih]
· rfl
· simp only [h, not_false_eq_true, decide_True]
· simp [h]
theorem countP_eq_length_filter (l) : countP p l = length (filter p l) := by
induction l with
@@ -234,7 +234,7 @@ theorem count_erase (a b : α) :
rw [if_pos hc_beq, hc, count_cons, Nat.add_sub_cancel]
else
have hc_beq := beq_false_of_ne hc
simp only [hc_beq, if_false, count_cons, count_cons, count_erase a b l]
simp only [hc_beq, if_false, count_cons, count_cons, count_erase a b l, reduceCtorEq]
if ha : b = a then
rw [ha, eq_comm] at hc
rw [if_pos ((beq_iff_eq _ _).2 ha), if_neg (by simpa using Ne.symm hc), Nat.add_zero, Nat.add_zero]

View File

@@ -33,6 +33,25 @@ theorem eraseP_of_forall_not {l : List α} (h : ∀ a, a ∈ l → ¬p a) : l.er
| nil => rfl
| cons _ _ ih => simp [h _ (.head ..), ih (forall_mem_cons.1 h).2]
@[simp] theorem eraseP_eq_nil (xs : List α) (p : α Bool) : xs.eraseP p = [] xs = [] x, p x xs = [x] := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [eraseP_cons, cond_eq_if]
split <;> rename_i h
· simp only [reduceCtorEq, cons.injEq, false_or]
constructor
· rintro rfl
simpa
· rintro _, _, rfl, rfl
rfl
· simp only [reduceCtorEq, cons.injEq, false_or, false_iff, not_exists, not_and]
rintro x h' rfl
simp_all
theorem eraseP_ne_nil (xs : List α) (p : α Bool) : xs.eraseP p [] xs [] x, p x xs [x] := by
simp
theorem exists_of_eraseP : {l : List α} {a} (al : a l) (pa : p a),
a l₁ l₂, ( b l₁, ¬p b) p a l = l₁ ++ a :: l₂ l.eraseP p = l₁ ++ l₂
| b :: l, a, al, pa =>
@@ -159,6 +178,14 @@ theorem eraseP_append (l₁ l₂ : List α) :
rw [eraseP_append_right _]
simp_all
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
| zero => simp
| succ n ih =>
simp only [replicate_succ, eraseP_cons]
split <;> simp [*]
protected theorem IsPrefix.eraseP (h : l₁ <+: l₂) : l₁.eraseP p <+: l₂.eraseP p := by
rw [IsPrefix] at h
obtain t, rfl := h
@@ -213,8 +240,11 @@ theorem eraseP_eq_iff {p} {l : List α} :
(replicate n a).eraseP p = replicate n a := by
rw [eraseP_of_forall_not (by simp_all)]
theorem Pairwise.eraseP (q) : Pairwise p l Pairwise p (l.eraseP q) :=
Pairwise.sublist <| eraseP_sublist _
theorem Nodup.eraseP (p) : Nodup l Nodup (l.eraseP p) :=
Nodup.sublist <| eraseP_sublist _
Pairwise.eraseP p
theorem eraseP_comm {l : List α} (h : a l, ¬ p a ¬ q a) :
(l.eraseP p).eraseP q = (l.eraseP q).eraseP p := by
@@ -230,6 +260,12 @@ theorem eraseP_comm {l : List α} (h : ∀ a ∈ l, ¬ p a ¬ q a) :
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
theorem head_eraseP_mem (xs : List α) (p : α Bool) (h) : (xs.eraseP p).head h xs :=
(eraseP_sublist xs).head_mem h
theorem getLast_eraseP_mem (xs : List α) (p : α Bool) (h) : (xs.eraseP p).getLast h xs :=
(eraseP_sublist xs).getLast_mem h
/-! ### erase -/
section erase
variable [BEq α]
@@ -258,6 +294,16 @@ theorem erase_eq_eraseP [LawfulBEq α] (a : α) : ∀ l : List α, l.erase a =
| b :: l => by
if h : a = b then simp [h] else simp [h, Ne.symm h, erase_eq_eraseP a l]
@[simp] theorem erase_eq_nil [LawfulBEq α] (xs : List α) (a : α) :
xs.erase a = [] xs = [] xs = [a] := by
rw [erase_eq_eraseP]
simp
theorem erase_ne_nil [LawfulBEq α] (xs : List α) (a : α) :
xs.erase a [] xs [] xs [a] := by
rw [erase_eq_eraseP]
simp
theorem exists_erase_eq [LawfulBEq α] {a : α} {l : List α} (h : a l) :
l₁ l₂, a l₁ l = l₁ ++ a :: l₂ l.erase a = l₁ ++ l₂ := by
let _, l₁, l₂, h₁, e, h₂, h₃ := exists_of_eraseP h (beq_self_eq_true _)
@@ -294,7 +340,7 @@ theorem mem_of_mem_erase {a b : α} {l : List α} (h : a ∈ l.erase b) : a ∈
@[simp] theorem erase_eq_self_iff [LawfulBEq α] {l : List α} : l.erase a = l a l := by
rw [erase_eq_eraseP', eraseP_eq_self_iff]
simp
simp [forall_mem_ne']
theorem erase_filter [LawfulBEq α] (f : α Bool) (l : List α) :
(filter f l).erase a = filter f (l.erase a) := by
@@ -327,6 +373,11 @@ 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]
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]
simp [eraseP_replicate]
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 ?_
@@ -366,6 +417,9 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {l : List α} :
rw [erase_of_not_mem]
simp_all
theorem Pairwise.erase [LawfulBEq α] {l : List α} (a) : Pairwise p l Pairwise p (l.erase a) :=
Pairwise.sublist <| erase_sublist _ _
theorem Nodup.erase_eq_filter [LawfulBEq α] {l} (d : Nodup l) (a : α) : l.erase a = l.filter (· != a) := by
induction d with
| nil => rfl
@@ -386,7 +440,13 @@ theorem Nodup.not_mem_erase [LawfulBEq α] {a : α} (h : Nodup l) : a ∉ l.eras
simpa using ((Nodup.mem_erase_iff h).mp H).left
theorem Nodup.erase [LawfulBEq α] (a : α) : Nodup l Nodup (l.erase a) :=
Nodup.sublist <| erase_sublist _ _
Pairwise.erase a
theorem head_erase_mem (xs : List α) (a : α) (h) : (xs.erase a).head h xs :=
(erase_sublist a xs).head_mem h
theorem getLast_erase_mem (xs : List α) (a : α) (h) : (xs.erase a).getLast h xs :=
(erase_sublist a xs).getLast_mem h
end erase
@@ -408,11 +468,26 @@ theorem eraseIdx_eq_take_drop_succ :
| a::l, 0 => by simp
| a::l, i + 1 => by simp [eraseIdx_eq_take_drop_succ l i]
@[simp] theorem eraseIdx_eq_nil {l : List α} {i : Nat} : eraseIdx l i = [] l = [] (length l = 1 i = 0) := by
match l, i with
| [], _
| a::l, 0
| a::l, i + 1 => simp [Nat.succ_inj']
theorem eraseIdx_ne_nil {l : List α} {i : Nat} : eraseIdx l i [] 2 l.length (l.length = 1 i 0) := by
match l with
| []
| [a]
| a::b::l => simp [Nat.succ_inj']
theorem eraseIdx_sublist : (l : List α) (k : Nat), eraseIdx l k <+ l
| [], _ => by simp
| a::l, 0 => by simp
| a::l, k + 1 => by simp [eraseIdx_sublist l k]
theorem mem_of_mem_eraseIdx {l : List α} {i : Nat} {a : α} (h : a l.eraseIdx i) : a l :=
(eraseIdx_sublist _ _).mem h
theorem eraseIdx_subset (l : List α) (k : Nat) : eraseIdx l k l := (eraseIdx_sublist l k).subset
@[simp]
@@ -442,6 +517,23 @@ 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]
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
· rw [eq_replicate, length_eraseIdx (by simpa using h)]
simp only [length_replicate, true_and]
intro b m
replace m := mem_of_mem_eraseIdx m
simp only [mem_replicate] at m
exact m.2
· rw [eraseIdx_of_length_le (by simpa using h)]
theorem Pairwise.eraseIdx {l : List α} (k) : Pairwise p l Pairwise p (l.eraseIdx k) :=
Pairwise.sublist <| eraseIdx_sublist _ _
theorem Nodup.eraseIdx {l : List α} (k) : Nodup l Nodup (l.eraseIdx k) :=
Pairwise.eraseIdx k
protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
eraseIdx l k <+: eraseIdx l' k := by
rcases h with t, rfl

View File

@@ -10,92 +10,13 @@ import Init.Data.List.Sublist
import Init.Data.List.Range
/-!
# Lemmas about `List.find?`, `List.findSome?`, `List.findIdx`, `List.findIdx?`, and `List.indexOf`.
# Lemmas about `List.findSome?`, `List.find?`, `List.findIdx`, `List.findIdx?`, and `List.indexOf`.
-/
namespace List
open Nat
/-! ### find? -/
@[simp] theorem find?_cons_of_pos (l) (h : p a) : find? p (a :: l) = some a := by
simp [find?, h]
@[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
induction l <;> simp [find?_cons]; split <;> simp [*]
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
@[simp] 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)
@[simp] 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]
theorem find?_append {l₁ l₂ : List α} : (l₁ ++ l₂).find? p = (l₁.find? p).or (l₂.find? p) := by
induction l₁ with
| nil => simp
| cons x xs ih =>
simp only [cons_append, find?]
by_cases h : p x <;> simp [h, ih]
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
· by_cases p a <;> simp_all [replicate_succ]
@[simp] theorem find?_replicate_of_length_pos (h : 0 < n) : find? p (replicate n a) = if p a then some a else none := by
simp [find?_replicate, Nat.ne_of_gt h]
@[simp] theorem find?_replicate_of_pos (h : p a) : find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
theorem Sublist.find?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.find? p).isSome (l₂.find? p).isSome := by
induction h with
| slnil => simp
| cons a h ih
| cons₂ a h ih =>
simp only [find?]
split <;> simp_all
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)
theorem IsPrefix.find?_eq_some {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.find? p l₁ = some b List.find? p l₂ = some b := by
rw [IsPrefix] at h
obtain t, rfl := h
simp (config := {contextual := true}) [find?_append]
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
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
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
/-! ### findSome? -/
@[simp] theorem findSome?_cons_of_isSome (l) (h : (f a).isSome) : findSome? f (a :: l) = f a := by
@@ -117,11 +38,50 @@ theorem exists_of_findSome?_eq_some {l : List α} {f : α → Option β} (w : l.
@[simp] theorem findSome?_eq_none : 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 α) :
(l.findSome? f).isSome x, x l (f x).isSome := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findSome?_cons]
split <;> simp_all
@[simp] theorem findSome?_guard (l : List α) : findSome? (Option.guard fun x => p x) l = find? p l := by
induction l with
| nil => simp
| cons x xs ih =>
simp [guard, findSome?, find?]
split <;> rename_i h
· simp only [Option.guard_eq_some] at h
obtain rfl, h := h
simp [h]
· simp only [Option.guard_eq_none] at h
simp [ih, h]
@[simp] theorem filterMap_head? (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 filterMap_head (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 filterMap_getLast? (f : α Option β) (l : List α) : (l.filterMap f).getLast? = l.reverse.findSome? f := by
rw [getLast?_eq_head?_reverse]
simp [ filterMap_reverse]
@[simp] theorem filterMap_getLast (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 α) :
(l.findSome? f).map g = l.findSome? (Option.map g f) := by
induction l <;> simp [findSome?_cons]; split <;> simp [*]
@[simp] theorem findSome?_map (f : β γ) (l : List β) : findSome? p (l.map f) = l.findSome? (p f) := by
theorem findSome?_map (f : β γ) (l : List β) : findSome? p (l.map f) = l.findSome? (p f) := by
induction l with
| nil => simp
| cons x xs ih =>
@@ -146,10 +106,10 @@ theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none e
simp [findSome?_replicate, Nat.ne_of_gt h]
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem find?_replicate_of_isSome (_ : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate]
@[simp] theorem find?_replicate_of_isNone (h : (f a).isNone) : findSome? f (replicate n a) = none := by
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) : findSome? f (replicate n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_replicate, h]
@@ -160,7 +120,9 @@ theorem Sublist.findSome?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
| cons a h ih
| cons₂ a h ih =>
simp only [findSome?]
split <;> simp_all
split
· simp_all
· exact ih
theorem Sublist.findSome?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
l₂.findSome? f = none l₁.findSome? f = none := by
@@ -183,6 +145,265 @@ theorem IsInfix.findSome?_eq_none {l₁ l₂ : List α} {f : α → Option β} (
List.findSome? f l₂ = none List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
/-! ### find? -/
@[simp] theorem find?_singleton (a : α) (p : α Bool) : [a].find? p = if p a then some a else none := by
simp only [find?]
split <;> simp_all
@[simp] theorem find?_cons_of_pos (l) (h : p a) : find? p (a :: l) = some a := by
simp [find?, h]
@[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
induction l <;> simp [find?_cons]; split <;> simp [*]
theorem find?_eq_some : xs.find? p = some b p b as bs, xs = as ++ b :: bs a as, !p a := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, exists_and_right]
split <;> rename_i h
· simp only [Option.some.injEq]
constructor
· rintro rfl
exact h, [], xs, rfl, by simp
· rintro -, as, bs, h₁, h₂
cases as with
| nil => simp_all
| cons a as =>
specialize h₂ a (mem_cons_self _ _)
simp only [cons_append] at h₁
obtain rfl, - := h₁
simp_all
· simp only [ih, Bool.not_eq_true', exists_and_right, and_congr_right_iff]
intro pb
constructor
· rintro as, bs, rfl, h₁
refine x :: as, bs, rfl, ?_
intro a m
simp at m
obtain (rfl|m) := m
· exact h
· exact h₁ a m
· rintro as, bs, h₁, h₂
cases as with
| nil => simp_all
| cons a as =>
refine as, bs, ?_, fun a m => h₂ a (mem_cons_of_mem _ m)
cases h₁
simp
@[simp]
theorem find?_cons_eq_some : (a :: xs).find? p = some b (p a a = b) (!p a xs.find? p = some b) := by
rw [find?_cons]
split <;> simp_all
@[simp] 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
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
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)
@[simp] theorem get_find?_mem (xs : List α) (p : α Bool) (h) : (xs.find? p).get h xs := by
induction xs with
| nil => simp at h
| cons x xs ih =>
simp only [find?_cons]
by_cases h : p x
· simp [h]
· simp only [h]
right
apply ih
@[simp] 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
| cons x xs ih =>
simp only [filter_cons]
split <;>
· simp only [find?_cons]
split <;> simp_all
@[simp] theorem filter_head? (p : α Bool) (l : List α) : (l.filter p).head? = l.find? p := by
rw [ filterMap_eq_filter, filterMap_head?, findSome?_guard]
@[simp] theorem filter_head (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 filter_getLast? (p : α Bool) (l : List α) : (l.filter p).getLast? = l.reverse.find? p := by
rw [getLast?_eq_head?_reverse]
simp [ filter_reverse]
@[simp] theorem filter_getLast (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) :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [filterMap_cons]
split <;>
· 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
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?_append {l₁ l₂ : List α} : (l₁ ++ l₂).find? p = (l₁.find? p).or (l₂.find? p) := by
induction l₁ with
| nil => simp
| cons x xs ih =>
simp only [cons_append, find?]
by_cases h : p x <;> simp [h, ih]
@[simp] theorem find?_join (xs : List (List α)) (p : α Bool) :
xs.join.find? p = xs.findSome? (·.find? p) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [join_cons, find?_append, findSome?_cons, ih]
split <;> simp [*]
theorem find?_join_eq_none (xs : List (List α)) (p : α Bool) :
xs.join.find? p = none ys xs, x ys, !p x := by
simp
/--
If `find? p` returns `some a` from `xs.join`, then `p a` holds, and
some list in `xs` contains `a`, and no earlier element of that list satisfies `p`.
Moreover, no earlier list in `xs` has an element satisfying `p`.
-/
theorem find?_join_eq_some (xs : List (List α)) (p : α Bool) (a : α) :
xs.join.find? p = some a
p a as ys zs bs, xs = as ++ (ys ++ a :: zs) :: bs
( a as, x a, !p x) ( x ys, !p x) := by
rw [find?_eq_some]
constructor
· rintro h, ys, zs, h₁, h₂
refine h, ?_
rw [join_eq_append] at h₁
obtain (as, bs, rfl, rfl, h₁ | as, bs, c, cs, ds, rfl, rfl, h₁) := h₁
· replace h₁ := h₁.symm
rw [join_eq_cons] at h₁
obtain bs, cs, ds, rfl, h₁, rfl := h₁
refine as ++ bs, [], cs, ds, by simp, ?_
simp
rintro a (ma | mb) x m
· simpa using h₂ x (by simpa using a, ma, m)
· specialize h₁ _ mb
simp_all
· simp [h₁]
refine as, bs, ?_
refine ?_, ?_, ?_
· simp_all
· intro l ml a m
simpa using h₂ a (by simpa using .inl l, ml, m)
· intro x m
simpa using h₂ x (by simpa using .inr m)
· rintro h, as, ys, zs, bs, rfl, h₁, h₂
refine h, as.join ++ ys, zs ++ bs.join, by simp, ?_
intro a m
simp at m
obtain l, ml, m | m := m
· exact h₁ l ml a m
· exact h₂ a m
@[simp] theorem find?_bind (xs : List α) (f : α List β) (p : β Bool) :
(xs.bind f).find? p = xs.findSome? (fun x => (f x).find? p) := by
simp [bind_def, findSome?_map]; rfl
theorem find?_bind_eq_none (xs : List α) (f : α List β) (p : β Bool) :
(xs.bind f).find? p = none x xs, y f x, !p y := by
simp
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
· by_cases p a <;> simp_all [replicate_succ]
@[simp] theorem find?_replicate_of_length_pos (h : 0 < n) : find? p (replicate n a) = if p a then some a else none := by
simp [find?_replicate, Nat.ne_of_gt h]
@[simp] theorem find?_replicate_of_pos (h : p a) : find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
theorem find?_replicate_eq_none (n : Nat) (a : α) (p : α Bool) :
(replicate n a).find? p = none n = 0 !p a := by
simp [Classical.or_iff_not_imp_left]
@[simp] theorem find?_replicate_eq_some (n : Nat) (a b : α) (p : α Bool) :
(replicate n a).find? p = some b n 0 p a a = b := by
cases n <;> simp
@[simp] theorem get_find?_replicate (n : Nat) (a : α) (p : α Bool) (h) : ((replicate n a).find? p).get h = a := by
cases n with
| zero => simp at h
| succ n => simp
theorem Sublist.find?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.find? p).isSome (l₂.find? p).isSome := by
induction h with
| slnil => simp
| cons a h ih
| cons₂ a h ih =>
simp only [find?]
split
· simp
· simpa using ih
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)
theorem IsPrefix.find?_eq_some {l₁ l₂ : List α} {p : α Bool} (h : l₁ <+: l₂) :
List.find? p l₁ = some b List.find? p l₂ = some b := by
rw [IsPrefix] at h
obtain t, rfl := h
simp (config := {contextual := true}) [find?_append]
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
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
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
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
simp only [pmap_eq_map_attach, find?_map]
rfl
/-! ### findIdx -/
theorem findIdx_cons (p : α Bool) (b : α) (l : List α) :
@@ -518,8 +739,8 @@ theorem findIdx?_eq_enum_findSome? {xs : List α} {p : α → Bool} :
simp only [findIdx?_cons, Nat.zero_add, findIdx?_succ, enum]
split
· simp_all
· simp_all only [enumFrom_cons, ite_false, Option.isNone_none, findSome?_cons_of_isNone]
simp [Function.comp_def, map_fst_add_enum_eq_enumFrom]
· simp_all only [enumFrom_cons, ite_false, Option.isNone_none, findSome?_cons_of_isNone, reduceCtorEq]
simp [Function.comp_def, map_fst_add_enum_eq_enumFrom, findSome?_map]
theorem Sublist.findIdx?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
(l₁.findIdx? p).isSome (l₂.findIdx? p).isSome := by

View File

@@ -79,6 +79,11 @@ open Nat
/-! ## Preliminaries -/
/-! ### nil -/
@[simp] theorem nil_eq {α} (xs : List α) : [] = xs xs = [] := by
cases xs <;> simp
/-! ### cons -/
theorem cons_ne_nil (a : α) (l : List α) : a :: l [] := nofun
@@ -86,6 +91,10 @@ theorem cons_ne_nil (a : α) (l : List α) : a :: l ≠ [] := nofun
@[simp]
theorem cons_ne_self (a : α) (l : List α) : a :: l l := mt (congrArg length) (Nat.succ_ne_self _)
@[simp] theorem ne_cons_self {a : α} {l : List α} : l a :: l := by
rw [ne_eq, eq_comm]
simp
theorem head_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : h₁ = h₂ := (cons.inj H).1
theorem tail_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : t₁ = t₂ := (cons.inj H).2
@@ -260,6 +269,14 @@ theorem getElem?_eq (l : List α) (i : Nat) :
l[i]? = if h : i < l.length then some l[i] else none := by
split <;> simp_all
@[simp] theorem some_getElem_eq_getElem? {α} (xs : List α) (i : Nat) (h : i < xs.length) :
(some xs[i] = xs[i]?) True := by
simp [h]
@[simp] theorem getElem?_eq_some_getElem {α} (xs : List α) (i : Nat) (h : i < xs.length) :
(xs[i]? = some xs[i]) True := by
simp [h]
theorem getElem_eq_iff {l : List α} {n : Nat} {h : n < l.length} : l[n] = x l[n]? = some x := by
simp only [getElem?_eq_some]
exact fun w => h, w, fun h => h.2
@@ -276,6 +293,9 @@ theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by simp
simp only [ get?_eq_getElem?]
rfl
theorem getElem?_cons : (a :: l)[i]? = if i = 0 then some a else l[i-1]? := by
cases i <;> simp
theorem getElem?_len_le : {l : List α} {n}, length l n l[n]? = none
| [], _, _ => rfl
| _ :: l, _+1, h => by
@@ -344,6 +364,11 @@ theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = s
theorem mem_cons_self (a : α) (l : List α) : a a :: l := .head ..
theorem mem_concat_self (xs : List α) (a : α) : a xs ++ [a] :=
mem_append_of_mem_right xs (mem_cons_self a _)
theorem mem_append_cons_self : a xs ++ a :: ys := mem_append_of_mem_right _ (mem_cons_self _ _)
theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a l a y :: l := .tail _
theorem exists_mem_of_ne_nil (l : List α) (h : l []) : x, x l :=
@@ -363,27 +388,21 @@ theorem forall_mem_cons {p : α → Prop} {a : α} {l : List α} :
fun H => H _ (.head ..), fun _ h => H _ (.tail _ h),
fun H₁, H₂ _ => fun | .head .. => H₁ | .tail _ h => H₂ _ h
@[simp]
theorem forall_mem_ne {a : α} {l : List α} : ( a' : α, a' l ¬a = a') a l :=
fun h m => h _ m rfl, fun h _ m e => h (e.symm m)
@[simp]
theorem forall_mem_ne' {a : α} {l : List α} : ( a' : α, a' l ¬a' = a) a l :=
fun h m => h _ m rfl, fun h _ m e => h (e.symm m)
@[simp]
theorem any_beq [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => a == x) a l := by
induction l <;> simp_all
@[simp]
theorem any_beq' [BEq α] [LawfulBEq α] {l : List α} : (l.any fun x => x == a) a l := by
induction l <;> simp_all [eq_comm (a := a)]
@[simp]
theorem all_bne [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => a != x) a l := by
induction l <;> simp_all
@[simp]
theorem all_bne' [BEq α] [LawfulBEq α] {l : List α} : (l.all fun x => x != a) a l := by
induction l <;> simp_all [eq_comm (a := a)]
@@ -507,7 +526,7 @@ theorem isEmpty_iff_length_eq_zero {l : List α} : l.isEmpty ↔ l.length = 0 :=
@[simp] theorem isEmpty_eq_true {l : List α} : l.isEmpty l = [] := by
cases l <;> simp
@[simp] theorem isEmpty_eq_false {l : List α} : ¬ l.isEmpty l [] := by
@[simp] theorem isEmpty_eq_false {l : List α} : l.isEmpty = false l [] := by
cases l <;> simp
/-! ### any / all -/
@@ -546,8 +565,7 @@ theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length)
(l.set i a)[i]? = some a := by
simp_all [getElem?_eq_some]
@[simp]
theorem getElem?_set_eq' {l : List α} {i : Nat} {a : α} : (set l i a)[i]? = (fun _ => a) <$> l[i]? := by
theorem getElem?_set_eq' {l : List α} {i : Nat} {a : α} : (set l i a)[i]? = Function.const _ a <$> l[i]? := by
by_cases h : i < l.length
· simp [getElem?_set_eq h, getElem?_eq_getElem h]
· simp only [Nat.not_lt] at h
@@ -604,7 +622,7 @@ theorem getElem?_set {l : List α} {i j : Nat} {a : α} :
theorem getElem?_set' {l : List α} {i j : Nat} {a : α} :
(set l i a)[j]? = if i = j then (fun _ => a) <$> l[j]? else l[j]? := by
by_cases i = j
· simp only [getElem?_set_eq', Option.map_eq_map, reduceIte, *]
· simp only [getElem?_set_eq', Option.map_eq_map, reduceIte, *]; rfl
· simp only [ne_eq, not_false_eq_true, getElem?_set_ne, reduceIte, *]
theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length n) {a : α} :
@@ -619,7 +637,7 @@ theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α
exact Nat.succ_le_succ_iff.mp h
@[simp] theorem set_eq_nil (l : List α) (n : Nat) (a : α) : l.set n a = [] l = [] := by
cases l <;> cases n <;> simp only [set]
cases l <;> cases n <;> simp [set]
theorem set_comm (a b : α) : {n m : Nat} (l : List α), n m
(l.set n a).set m b = (l.set m b).set n a
@@ -714,9 +732,14 @@ theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
/-! ### foldl and foldr -/
@[simp] theorem foldr_self_append (l : List α) : l.foldr cons l' = l ++ l' := by
@[simp] theorem foldr_cons_eq_append (l : List α) : l.foldr cons l' = l ++ l' := by
induction l <;> simp [*]
@[deprecated foldr_cons_eq_append (since := "2024-08-22")] abbrev foldr_self_append := @foldr_cons_eq_append
@[simp] theorem foldl_flip_cons_eq_append (l : List α) : l.foldl (fun x y => y :: x) l' = l.reverse ++ l' := by
induction l generalizing l' <;> simp [*]
theorem foldr_self (l : List α) : l.foldr cons [] = l := by simp
theorem foldl_map (f : β₁ β₂) (g : α β₂ α) (l : List β₁) (init : α) :
@@ -835,7 +858,7 @@ theorem getLast_eq_getLastD (a l h) : @getLast α (a::l) h = getLastD l a := by
theorem getLast!_cons [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
simp [getLast!, getLast_eq_getLastD]
theorem getLast_mem : {l : List α} (h : l []), getLast l h l
@[simp] theorem getLast_mem : {l : List α} (h : l []), getLast l h l
| [], h => absurd rfl h
| [_], _ => .head ..
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
@@ -855,11 +878,14 @@ theorem get_cons_length (x : α) (xs : List α) (n : Nat) (h : n = xs.length) :
/-! ### getLast? -/
theorem getLast?_cons : @getLast? α (a::l) = getLastD l a := by
simp only [getLast?, getLast_eq_getLastD]
@[simp] theorem getLast?_singleton (a : α) : getLast? [a] = a := rfl
theorem getLast?_cons {a : α} : (a::l).getLast? = l.getLast?.getD a := by
cases l <;> simp [getLast?, getLast]
@[simp] theorem getLast?_cons_cons : (a :: b :: l).getLast? = (b :: l).getLast? := by
simp [getLast?_cons]
theorem getLast?_eq_getLast : l h, @getLast? α l = some (getLast l h)
| [], h => nomatch h rfl
| _::_, _ => rfl
@@ -873,10 +899,10 @@ theorem getLast?_eq_getElem? : ∀ (l : List α), getLast? l = l[l.length - 1]?
theorem getLast?_eq_get? (l : List α) : getLast? l = l.get? (l.length - 1) := by
simp [getLast?_eq_getElem?]
@[simp] theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
simp [getLast?_eq_getElem?, Nat.succ_sub_succ]
@[simp] theorem getLastD_concat (a b l) : @getLastD α (l ++ [b]) a = b := by
theorem getLastD_concat (a b l) : @getLastD α (l ++ [b]) a = b := by
rw [getLastD_eq_getLast?, getLast?_concat]; rfl
/-! ## Head and tail -/
@@ -889,6 +915,11 @@ theorem head!_of_head? [Inhabited α] : ∀ {l : List α}, head? l = some a →
theorem head?_eq_head : {l} h, @head? α l = some (head l h)
| _::_, _ => rfl
theorem head_eq_iff_head?_eq_some {xs : List α} (h) : xs.head h = a xs.head? = some a := by
cases xs with
| nil => simp at h
| cons x xs => simp
theorem head?_eq_getElem? : l : List α, head? l = l[0]?
| [] => rfl
| a::l => by simp
@@ -896,6 +927,9 @@ theorem head?_eq_getElem? : ∀ l : List α, head? l = l[0]?
@[simp] theorem head?_eq_none_iff : l.head? = none l = [] := by
cases l <;> simp
theorem head?_eq_some_iff {xs : List α} {a : α} : xs.head? = some a ys, xs = a :: ys := by
cases xs <;> simp_all
@[simp] theorem head_mem : {l : List α} (h : l []), head l h l
| [], h => absurd rfl h
| _::_, _ => .head ..
@@ -924,9 +958,16 @@ theorem tail_eq_tail? (l) : @tail α l = (tail? l).getD [] := by simp [tail_eq_t
/-! ### map -/
@[simp] theorem map_id (l : List α) : map id l = l := by induction l <;> simp_all
@[simp] theorem map_id_fun : map (id : α α) = id := by
funext l
induction l <;> simp_all
@[simp] theorem map_id' (l : List α) : map (fun a => a) l = l := by induction l <;> simp_all
@[simp] theorem map_id_fun' : map (fun (a : α) => a) = id := map_id_fun
theorem map_id (l : List α) : map (id : α α) l = l := by
induction l <;> simp_all
theorem map_id' (l : List α) : map (fun (a : α) => a) l = l := map_id l
theorem map_id'' {f : α α} (h : x, f x = x) (l : List α) : map f l = l := by
simp [show f = id from funext h]
@@ -1113,7 +1154,7 @@ theorem filter_length_eq_length {l} : (filter p l).length = l.length ↔ ∀ a
· simp_all [or_and_left]
· simp_all [or_and_right]
theorem filter_eq_nil {l} : filter p l = [] a, a l ¬p a := by
@[simp] theorem filter_eq_nil {l} : filter p l = [] a, a l ¬p a := by
simp only [eq_nil_iff_forall_not_mem, mem_filter, not_and]
theorem forall_mem_filter {l : List α} {p : α Bool} {P : α Prop} :
@@ -1201,8 +1242,13 @@ theorem head_filter_of_pos {p : α → Bool} {l : List α} (w : l ≠ []) (h : p
theorem filterMap_eq_map (f : α β) : filterMap (some f) = map f := by
funext l; induction l <;> simp [*, filterMap_cons]
@[simp] theorem filterMap_some (l : List α) : filterMap some l = l := by
erw [filterMap_eq_map, map_id]
@[simp] theorem filterMap_some_fun : filterMap (some : α Option α) = id := by
funext l
erw [filterMap_eq_map]
simp
theorem filterMap_some (l : List α) : filterMap some l = l := by
rw [filterMap_some_fun, id]
theorem map_filterMap_some_eq_filter_map_isSome (f : α Option β) (l : List α) :
(l.filterMap f).map some = (l.map f).filter fun b => b.isSome := by
@@ -1277,7 +1323,7 @@ theorem forall_mem_filterMap {f : α → Option β} {l : List α} {P : β → Pr
induction l <;> simp [filterMap_cons]; split <;> simp [*]
theorem map_filterMap_of_inv (f : α Option β) (g : β α) (H : x : α, (f x).map g = some x)
(l : List α) : map g (filterMap f l) = l := by simp only [map_filterMap, H, filterMap_some]
(l : List α) : map g (filterMap f l) = l := by simp only [map_filterMap, H, filterMap_some, id]
theorem head_filterMap_of_eq_some {f : α Option β} {l : List α} (w : l []) {b : β} (h : f (l.head w) = some b) :
(filterMap f l).head ((ne_nil_of_mem (mem_filterMap.2 _, head_mem w, h))) =
@@ -1300,7 +1346,7 @@ theorem forall_none_of_filterMap_eq_nil (h : filterMap f xs = []) : ∀ x ∈ xs
| tail _ hmem => exact ih h hmem
· contradiction
theorem filterMap_eq_nil {l} : filterMap f l = [] a l, f a = none := by
@[simp] theorem filterMap_eq_nil {l} : filterMap f l = [] a l, f a = none := by
constructor
· exact forall_none_of_filterMap_eq_nil
· intro h
@@ -1419,10 +1465,22 @@ theorem append_right_inj {t₁ t₂ : List α} (s) : s ++ t₁ = s ++ t₂ ↔ t
theorem append_left_inj {s₁ s₂ : List α} (t) : s₁ ++ t = s₂ ++ t s₁ = s₂ :=
fun h => append_inj_left' h rfl, congrArg (· ++ _)
@[simp] theorem append_left_eq_self {x y : List α} : x ++ y = y x = [] := by
rw [ append_left_inj (s₁ := x), nil_append]
@[simp] theorem self_eq_append_left {x y : List α} : y = x ++ y x = [] := by
rw [eq_comm, append_left_eq_self]
@[simp] theorem append_right_eq_self {x y : List α} : x ++ y = x y = [] := by
rw [ append_right_inj (t₁ := y), append_nil]
@[simp] theorem self_eq_append_right {x y : List α} : x = x ++ y y = [] := by
rw [eq_comm, append_right_eq_self]
@[simp] theorem append_eq_nil : p ++ q = [] p = [] q = [] := by
cases p <;> simp
@[simp] theorem getLast_concat {a : α} : (l : List α), getLast (l ++ [a]) (by simp) = a
theorem getLast_concat {a : α} : (l : List α), getLast (l ++ [a]) (by simp) = a
| [] => rfl
| a::t => by
simp [getLast_cons _, getLast_concat t]
@@ -1464,9 +1522,9 @@ theorem getElem?_append {l₁ l₂ : List α} {n : Nat} :
· exact getElem?_append_left h
· exact getElem?_append_right (by simpa using h)
@[simp] theorem head_append_of_ne_nil {l : List α} (w : l []) :
head (l ++ l') (by simp_all) = head l w := by
match l, w with
@[simp] theorem head_append_of_ne_nil {l : List α} {w} (w₂) :
head (l ++ l') w₁ = head l w := by
match l, w with
| a :: l, _ => rfl
theorem head_append {l₁ l₂ : List α} (w : l₁ ++ l₂ []) :
@@ -1489,7 +1547,7 @@ theorem head_append {l₁ l₂ : List α} (w : l₁ ++ l₂ ≠ []) :
-- `getLast_append_of_ne_nil`, `getLast_append` and `getLast?_append`
-- are stated and proved later in the `reverse` section.
@[simp] theorem nil_eq_append : [] = a ++ b a = [] b = [] := by
theorem nil_eq_append : [] = a ++ b a = [] b = [] := by
rw [eq_comm, append_eq_nil]
theorem append_ne_nil_of_left_ne_nil {s : List α} (h : s []) (t : List α) : s ++ t [] := by simp_all
@@ -1500,6 +1558,14 @@ theorem append_ne_nil_of_ne_nil_left {s : List α} (h : s ≠ []) (t : List α)
@[deprecated append_ne_nil_of_right_ne_nil (since := "2024-07-24")]
theorem append_ne_nil_of_ne_nil_right (s : List α) : t [] s ++ t [] := by simp_all
theorem tail_append (xs ys : List α) :
(xs ++ ys).tail = if xs.isEmpty then ys.tail else xs.tail ++ ys := by
cases xs <;> simp
@[simp] theorem tail_append_of_ne_nil (xs ys : List α) (h : xs []) :
(xs ++ ys).tail = xs.tail ++ ys := by
simp_all [tail_append]
theorem append_eq_cons :
a ++ b = x :: c (a = [] b = x :: c) ( a', a = x :: a' c = a' ++ b) := by
cases a with simp | cons a as => ?_
@@ -1693,9 +1759,15 @@ theorem join_singleton (l : List α) : [l].join = l := by simp
| [] => by simp
| b :: l => by simp [mem_join, or_and_right, exists_or]
@[simp] theorem join_eq_nil_iff {L : List (List α)} : L.join = [] l L, l = [] := by
@[simp] theorem join_eq_nil {L : List (List α)} : L.join = [] l L, l = [] := by
induction L <;> simp_all
@[deprecated join_eq_nil (since := "2024-08-22")]
theorem join_eq_nil_iff {L : List (List α)} : L.join = [] l L, l = [] := join_eq_nil
theorem join_ne_nil (xs : List (List α)) : xs.join [] x, x xs x [] := by
simp
theorem exists_of_mem_join : a join L l, l L a l := mem_join.1
theorem mem_join_of_mem (lL : l L) (al : a l) : a join L := mem_join.2 l, lL, al
@@ -1750,6 +1822,11 @@ theorem join_filter_ne_nil [DecidablePred fun l : List α => l ≠ []] {L : List
simp only [ne_eq, isEmpty_iff, Bool.not_eq_true, Bool.decide_eq_false,
join_filter_not_isEmpty]
@[deprecated filter_join (since := "2024-08-26")]
theorem join_map_filter (p : α Bool) (l : List (List α)) :
(l.map (filter p)).join = (l.join).filter p := by
rw [filter_join]
@[simp] theorem join_append (L₁ L₂ : List (List α)) : join (L₁ ++ L₂) = join L₁ ++ join L₂ := by
induction L₁ <;> simp_all
@@ -1759,6 +1836,55 @@ theorem join_concat (L : List (List α)) (l : List α) : join (L ++ [l]) = join
theorem join_join {L : List (List (List α))} : join (join L) = join (map join L) := by
induction L <;> simp_all
theorem join_eq_cons (xs : List (List α)) (y : α) (ys : List α) :
xs.join = y :: ys
as bs cs, xs = as ++ (y :: bs) :: cs ( l, l as l = []) ys = bs ++ cs.join := by
constructor
· induction xs with
| nil => simp
| cons x xs ih =>
intro h
simp only [join_cons] at h
replace h := h.symm
rw [cons_eq_append] at h
obtain (rfl, h | z) := h
· obtain as, bs, cs, rfl, _, rfl := ih h
refine [] :: as, bs, cs, ?_
simpa
· obtain a', rfl, rfl := z
refine [], a', xs, ?_
simp
· rintro as, bs, cs, rfl, h₁, rfl
simp [join_eq_nil.mpr h₁]
theorem join_eq_append (xs : List (List α)) (ys zs : List α) :
xs.join = ys ++ zs
( as bs, xs = as ++ bs ys = as.join zs = bs.join)
as bs c cs ds, xs = as ++ (bs ++ c :: cs) :: ds ys = as.join ++ bs
zs = c :: cs ++ ds.join := by
constructor
· induction xs generalizing ys with
| nil =>
simp only [join_nil, nil_eq, append_eq_nil, and_false, cons_append, false_and, exists_const,
exists_false, or_false, and_imp, List.cons_ne_nil]
rintro rfl rfl
exact [], [], by simp
| cons x xs ih =>
intro h
simp only [join_cons] at h
rw [append_eq_append_iff] at h
obtain (ys, rfl, h | c', rfl, h) := h
· obtain (as, bs, rfl, rfl, rfl | as, bs, c, cs, ds, rfl, rfl, rfl) := ih _ h
· exact .inl x :: as, bs, by simp
· exact .inr x :: as, bs, c, cs, ds, by simp
· simp only [h]
cases c' with
| nil => exact .inl [ys], xs, by simp
| cons x c' => exact .inr [], ys, x, c', xs, by simp
· rintro (as, bs, rfl, rfl, rfl | as, bs, c, cs, ds, rfl, rfl, rfl)
· simp
· simp
/-- Two lists of sublists are equal iff their joins coincide, as well as the lengths of the
sublists. -/
theorem eq_iff_join_eq : (L L' : List (List α)),
@@ -1793,7 +1919,7 @@ theorem mem_bind_of_mem {b : β} {l : List α} {f : α → List β} {a} (al : a
@[simp]
theorem bind_eq_nil {l : List α} {f : α List β} : List.bind l f = [] x l, f x = [] :=
join_eq_nil_iff.trans <| by
join_eq_nil.trans <| by
simp only [mem_map, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
theorem forall_mem_bind {p : β Prop} {l : List α} {f : α List β} :
@@ -1883,6 +2009,9 @@ theorem forall_mem_replicate {p : α → Prop} {a : α} {n} :
@[simp] theorem replicate_succ_ne_nil (n : Nat) (a : α) : replicate (n+1) a [] := by
simp [replicate_succ]
@[simp] theorem replicate_eq_nil (n : Nat) (a : α) : replicate n a = [] n = 0 := by
cases n <;> simp
@[simp] theorem getElem_replicate (a : α) {n : Nat} {m} (h : m < (replicate n a).length) :
(replicate n a)[m] = a :=
eq_of_mem_replicate (get_mem _ _ _)
@@ -1907,6 +2036,9 @@ theorem head?_replicate (a : α) (n : Nat) : (replicate n a).head? = if n = 0 th
· simp at w
· simp_all [replicate_succ]
@[simp] theorem tail_replicate (n : Nat) (a : α) : (replicate n a).tail = replicate (n - 1) a := by
cases n <;> simp [replicate_succ]
@[simp] theorem replicate_inj : replicate n a = replicate m b n = m (n = 0 a = b) :=
fun h => have eq : n = m := by simpa using congrArg length h
eq, by
@@ -1965,9 +2097,9 @@ theorem append_eq_replicate {l₁ l₂ : List α} {a : α} :
split <;> simp
theorem filter_replicate : (replicate n a).filter p = if p a then replicate n a else [] := by
induction n with
cases n with
| zero => simp
| succ n ih =>
| succ n =>
simp only [replicate_succ, filter_cons]
split <;> simp_all
@@ -2028,18 +2160,19 @@ theorem bind_replicate {β} (f : α → List β) : (replicate n a).bind f = (rep
| nil => rfl
| cons a as ih => simp [ih]
@[simp] theorem mem_reverseAux {x : α} : {as bs}, x reverseAux as bs x as x bs
theorem mem_reverseAux {x : α} : {as bs}, x reverseAux as bs x as x bs
| [], _ => .inr, fun | .inr h => h
| a :: _, _ => by rw [reverseAux, mem_cons, or_assoc, or_left_comm, mem_reverseAux, mem_cons]
@[simp] theorem mem_reverse {x : α} {as : List α} : x reverse as x as := by simp [reverse]
@[simp] theorem mem_reverse {x : α} {as : List α} : x reverse as x as := by
simp [reverse, mem_reverseAux]
@[simp] theorem reverse_eq_nil_iff {xs : List α} : xs.reverse = [] xs = [] := by
match xs with
| [] => simp
| x :: xs => simp
@[simp] theorem reverse_ne_nil_iff {xs : List α} : xs.reverse [] xs [] :=
theorem reverse_ne_nil_iff {xs : List α} : xs.reverse [] xs [] :=
not_congr reverse_eq_nil_iff
theorem getElem?_reverse' : {l : List α} (i j), i + j + 1 = length l
@@ -2085,11 +2218,25 @@ theorem reverseAux_reverseAux_nil (as bs : List α) : reverseAux (reverseAux as
theorem reverse_eq_iff {as bs : List α} : as.reverse = bs as = bs.reverse := by
constructor <;> (rintro rfl; simp)
@[simp] theorem getLast?_reverse (l : List α) : l.reverse.getLast? = l.head? := by cases l <;> simp
@[simp] theorem reverse_inj {xs ys : List α} : xs.reverse = ys.reverse xs = ys := by
simp [reverse_eq_iff]
@[simp] theorem reverse_eq_cons {xs : List α} {a : α} {ys : List α} :
xs.reverse = a :: ys xs = ys.reverse ++ [a] := by
rw [reverse_eq_iff, reverse_cons]
@[simp] theorem getLast?_reverse (l : List α) : l.reverse.getLast? = l.head? := by
cases l <;> simp [getLast?_concat]
@[simp] theorem head?_reverse (l : List α) : l.reverse.head? = l.getLast? := by
rw [ getLast?_reverse, reverse_reverse]
theorem getLast?_eq_head?_reverse {xs : List α} : xs.getLast? = xs.reverse.head? := by
simp
theorem head?_eq_getLast?_reverse {xs : List α} : xs.head? = xs.reverse.getLast? := by
simp
@[simp] theorem map_reverse (f : α β) (l : List α) : l.reverse.map f = (l.map f).reverse := by
induction l <;> simp [*]
@@ -2114,8 +2261,16 @@ theorem reverse_map (f : α → β) (l : List α) : (l.map f).reverse = l.revers
@[simp] theorem reverse_append (as bs : List α) : (as ++ bs).reverse = bs.reverse ++ as.reverse := by
induction as <;> simp_all
theorem reverse_concat (l : List α) (a : α) : (l.concat a).reverse = a :: l.reverse := by
rw [concat_eq_append, reverse_append]; rfl
@[simp] theorem reverse_eq_append {xs ys zs : List α} :
xs.reverse = ys ++ zs xs = zs.reverse ++ ys.reverse := by
rw [reverse_eq_iff, reverse_append]
theorem reverse_concat (l : List α) (a : α) : (l ++ [a]).reverse = a :: l.reverse := by
rw [reverse_append]; rfl
theorem reverse_eq_concat {xs ys : List α} {a : α} :
xs.reverse = ys ++ [a] xs = a :: ys.reverse := by
rw [reverse_eq_iff, reverse_concat]
/-- Reversing a join is the same as reversing the order of parts and reversing all parts. -/
theorem reverse_join (L : List (List α)) :
@@ -2133,7 +2288,7 @@ theorem reverse_bind {β} (l : List α) (f : α → List β) : (l.bind f).revers
theorem bind_reverse {β} (l : List α) (f : α List β) : (l.reverse.bind f) = (l.bind (reverse f)).reverse := by
induction l <;> simp_all
theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
@[simp] theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
reverseAux_eq_append ..
@[simp] theorem foldrM_reverse [Monad m] (l : List α) (f : α β m β) (b) :
@@ -2159,16 +2314,32 @@ theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
induction l with
| nil => contradiction
| cons a l ih =>
simp
simp only [reverse_cons]
by_cases h' : l = []
· simp_all
· rw [getLast_cons, head_append_of_ne_nil, ih]
simp_all
· simp only [head_eq_iff_head?_eq_some, head?_reverse] at ih
simp [ih, h, h', getLast_cons, head_eq_iff_head?_eq_some]
theorem getLast_eq_head_reverse {l : List α} (h : l []) :
l.getLast h = l.reverse.head (by simp_all) := by
rw [ head_reverse]
theorem getLast_eq_iff_getLast_eq_some {xs : List α} (h) : xs.getLast h = a xs.getLast? = some a := by
rw [getLast_eq_head_reverse, head_eq_iff_head?_eq_some]
simp
@[simp] theorem getLast?_eq_none_iff {xs : List α} : xs.getLast? = none xs = [] := by
rw [getLast?_eq_head?_reverse, head?_eq_none_iff, reverse_eq_nil_iff]
theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ys, xs = ys ++ [a] := by
rw [getLast?_eq_head?_reverse, head?_eq_some_iff]
simp only [reverse_eq_cons]
exact fun ys, h => ys.reverse, by simpa using h, fun ys, h => ys.reverse, by simpa using h
theorem mem_of_getLast?_eq_some {xs : List α} {a : α} (h : xs.getLast? = some a) : a xs := by
obtain ys, rfl := getLast?_eq_some_iff.1 h
exact mem_concat_self ys a
@[simp] theorem getLast_reverse {l : List α} (h : l.reverse []) :
l.reverse.getLast h = l.head (by simp_all) := by
simp [getLast_eq_head_reverse]
@@ -2177,8 +2348,8 @@ theorem head_eq_getLast_reverse {l : List α} (h : l ≠ []) :
l.head h = l.reverse.getLast (by simp_all) := by
rw [ getLast_reverse]
@[simp] theorem getLast_append_of_ne_nil {l : List α} (h : l' []) :
(l ++ l').getLast (append_ne_nil_of_right_ne_nil l h) = l'.getLast (by simp_all) := by
@[simp] theorem getLast_append_of_ne_nil {l : List α} {h₁} (h : l' []) :
(l ++ l').getLast h₁ = l'.getLast h₂ := by
simp only [getLast_eq_head_reverse, reverse_append]
rw [head_append_of_ne_nil]
@@ -2350,8 +2521,8 @@ theorem dropLast_append {l₁ l₂ : List α} :
(l₁ ++ l₂).dropLast = if l₂.isEmpty then l₁.dropLast else l₁ ++ l₂.dropLast := by
split <;> simp_all
@[simp] theorem dropLast_append_cons : dropLast (l₁ ++ b::l₂) = l₁ ++ dropLast (b::l₂) := by
simp only [ne_eq, not_false_eq_true, dropLast_append_of_ne_nil]
theorem dropLast_append_cons : dropLast (l₁ ++ b::l₂) = l₁ ++ dropLast (b::l₂) := by
simp
@[simp 1100] theorem dropLast_concat : dropLast (l₁ ++ [b]) = l₁ := by simp
@@ -2368,6 +2539,27 @@ theorem dropLast_append {l₁ l₂ : List α} :
dropLast (a :: replicate n a) = replicate n a := by
rw [ replicate_succ, dropLast_replicate, Nat.add_sub_cancel]
/-!
### splitAt
We don't provide any API for `splitAt`, beyond the `@[simp]` lemma
`splitAt n l = (l.take n, l.drop n)`,
which is proved in `Init.Data.List.TakeDrop`.
-/
theorem splitAt_go (n : Nat) (l acc : List α) :
splitAt.go l xs n acc =
if n < xs.length then (acc.reverse ++ xs.take n, xs.drop n) else (l, []) := by
induction xs generalizing n acc with
| nil => simp [splitAt.go]
| cons x xs ih =>
cases n with
| zero => simp [splitAt.go]
| succ n =>
rw [splitAt.go, take_succ_cons, drop_succ_cons, ih n (x :: acc),
reverse_cons, append_assoc, singleton_append, length_cons]
simp only [Nat.succ_lt_succ_iff]
/-! ## Manipulating elements -/
/-! ### replace -/

View File

@@ -7,6 +7,7 @@ prelude
import Init.Data.List.Nat.TakeDrop
import Init.Data.List.Range
import Init.Data.List.Pairwise
import Init.Data.List.Find
/-!
# Lemmas about `List.range` and `List.enum`
@@ -23,8 +24,6 @@ open Nat
theorem range'_succ (s n step) : range' s (n + 1) step = s :: range' (s + step) n step := by
simp [range', Nat.add_succ, Nat.mul_succ]
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
@[simp] theorem length_range' (s step) : n : Nat, length (range' s n step) = n
| 0 => rfl
| _ + 1 => congrArg succ (length_range' _ _ _)
@@ -32,6 +31,27 @@ theorem range'_succ (s n step) : range' s (n + 1) step = s :: range' (s + step)
@[simp] theorem range'_eq_nil : range' s n step = [] n = 0 := by
rw [ length_eq_zero, length_range']
theorem range'_ne_nil (s n : Nat) : range' s n [] n 0 := by
cases n <;> simp
@[simp] theorem range'_zero : range' s 0 = [] := by
simp
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
@[simp] theorem range'_inj : range' s n = range' s' n' n = n' (n = 0 s = s') := by
constructor
· intro h
have h' := congrArg List.length h
simp at h'
subst h'
cases n with
| zero => simp
| succ n =>
simp only [range'_succ] at h
simp_all
· rintro rfl, rfl | rfl <;> simp
theorem mem_range' : {n}, m range' s n step i < n, m = s + step * i
| 0 => by simp [range', Nat.not_lt_zero]
| n + 1 => by
@@ -45,6 +65,29 @@ theorem mem_range' : ∀{n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step *
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
theorem head?_range' (n : Nat) : (range' s n).head? = if n = 0 then none else some s := by
induction n <;> simp_all [range'_succ, head?_append]
@[simp] theorem head_range' (n : Nat) (h) : (range' s n).head h = s := by
repeat simp_all [head?_range', head_eq_iff_head?_eq_some]
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
| succ n ih =>
rw [range'_succ, getLast?_cons, ih]
by_cases h : n = 0
· rw [if_pos h]
simp [h]
· rw [if_neg h]
simp
omega
@[simp] 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]
theorem pairwise_lt_range' s n (step := 1) (pos : 0 < step := by simp) :
Pairwise (· < ·) (range' s n step) :=
match s, n, step, pos with
@@ -124,6 +167,67 @@ theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ [
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ [s + n] := by
simp [range'_concat]
theorem range'_eq_cons_iff : range' s n = a :: xs s = a 0 < n xs = range' (a + 1) (n - 1) := by
induction n generalizing s with
| zero => simp
| succ n ih =>
simp only [range'_succ]
simp only [cons.injEq, and_congr_right_iff]
rintro rfl
simp [eq_comm]
@[simp] theorem range'_eq_singleton {s n a : Nat} : range' s n = [a] s = a n = 1 := by
rw [range'_eq_cons_iff]
simp only [nil_eq, range'_eq_nil, and_congr_right_iff]
rintro rfl
omega
theorem range'_eq_append_iff : range' s n = xs ++ ys k, k n xs = range' s k ys = range' (s + k) (n - k) := by
induction n generalizing s xs ys with
| zero => simp
| succ n ih =>
simp only [range'_succ]
rw [cons_eq_append]
constructor
· rintro (rfl, rfl | a, rfl, h)
· exact 0, by simp [range'_succ]
· simp only [ih] at h
obtain k, h, rfl, rfl := h
refine k + 1, ?_
simp_all [range'_succ]
omega
· rintro k, h, rfl, rfl
cases k with
| zero => simp [range'_succ]
| succ k =>
simp only [range'_succ, reduceCtorEq, false_and, cons.injEq, true_and, ih, range'_inj, exists_eq_left', or_true, and_true, false_or]
refine k, ?_
simp_all
omega
@[simp] theorem find?_range'_eq_some (s n : Nat) (i : Nat) (p : Nat Bool) :
(range' s n).find? p = some i p i i range' s n j, s j j < i !p j := by
rw [find?_eq_some]
simp only [Bool.not_eq_true', exists_and_right, mem_range'_1, and_congr_right_iff]
simp only [range'_eq_append_iff, eq_comm (a := i :: _), range'_eq_cons_iff]
intro h
constructor
· rintro as, x, k, h₁, rfl, rfl, h₂, rfl, h₃
constructor
· omega
· simpa using h₃
· rintro h₁, h₂, h₃
refine range' s (i - s), range' (i + 1) (n - (i - s) - 1), i - s, ?_ , ?_
· simp; omega
· simp only [mem_range'_1, and_imp]
intro a a₁ a₂
exact h₃ a a₁ (by omega)
@[simp] 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
rw [find?_eq_none]
simp
/-! ### range -/
theorem range_loop_range' : s n : Nat, range.loop s (range' s n) = range' 0 (n + s)
@@ -153,6 +257,9 @@ theorem range'_eq_map_range (s n : Nat) : range' s n = map (s + ·) (range n) :=
@[simp] theorem range_eq_nil {n : Nat} : range n = [] n = 0 := by
rw [ length_eq_zero, length_range]
theorem range_ne_nil (n : Nat) : range n [] n 0 := by
cases n <;> simp
@[simp]
theorem range_sublist {m n : Nat} : range m <+ range n m n := by
simp only [range_eq_range', range'_sublist_right]
@@ -188,6 +295,30 @@ theorem range_add (a b : Nat) : range (a + b) = range a ++ (range b).map (a + ·
rw [ range'_eq_map_range]
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 0 a b).symm
theorem head?_range (n : Nat) : (range n).head? = if n = 0 then none else some 0 := by
induction n with
| zero => simp
| succ n ih =>
simp only [range_succ, head?_append, ih]
split <;> simp_all
@[simp] theorem head_range (n : Nat) (h) : (range n).head h = 0 := by
cases n with
| zero => simp at h
| succ n => simp [head?_range, head_eq_iff_head?_eq_some]
theorem getLast?_range (n : Nat) : (range n).getLast? = if n = 0 then none else some (n - 1) := by
induction n with
| zero => simp
| succ n ih =>
simp only [range_succ, getLast?_append, ih]
split <;> simp_all
@[simp] theorem getLast_range (n : Nat) (h) : (range n).getLast h = n - 1 := by
cases n with
| zero => simp at h
| succ n => simp [getLast?_range, getLast_eq_iff_getLast_eq_some]
theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
apply List.ext_getElem
· simp
@@ -196,6 +327,14 @@ theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
theorem nodup_range (n : Nat) : Nodup (range n) := by
simp (config := {decide := true}) only [range_eq_range', nodup_range']
@[simp] theorem find?_range_eq_some (n : Nat) (i : Nat) (p : Nat Bool) :
(range n).find? p = some i p i i range n j, j < i !p j := by
simp [range_eq_range']
@[simp] theorem find?_range_eq_none (n : Nat) (p : Nat Bool) :
(range n).find? p = none i, i < n !p i := by
simp [range_eq_range']
/-! ### iota -/
theorem iota_eq_reverse_range' : n : Nat, iota n = reverse (range' 1 n)
@@ -204,9 +343,49 @@ theorem iota_eq_reverse_range' : ∀ n : Nat, iota n = reverse (range' 1 n)
@[simp] theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
@[simp] theorem iota_eq_nil (n : Nat) : iota n = [] n = 0 := by
cases n <;> simp
theorem iota_ne_nil (n : Nat) : iota n [] n 0 := by
cases n <;> simp
@[simp]
theorem mem_iota {m n : Nat} : m iota n 1 m m n := by
theorem mem_iota {m n : Nat} : m iota n 0 < m m n := by
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
omega
@[simp] theorem iota_inj : iota n = iota n' n = n' := by
constructor
· intro h
have h' := congrArg List.length h
simp at h'
exact h'
· rintro rfl
simp
theorem iota_eq_cons_iff : iota n = a :: xs n = a 0 < n xs = iota (n - 1) := by
simp [iota_eq_reverse_range']
simp [range'_eq_append_iff, reverse_eq_iff]
constructor
· rintro k, h, rfl, h'
rw [eq_comm, range'_eq_singleton] at h'
simp only [reverse_inj, range'_inj, or_true, and_true]
omega
· rintro rfl, h, rfl
refine n - 1, by simp, rfl, ?_
rw [eq_comm, range'_eq_singleton]
omega
theorem iota_eq_append_iff : iota n = xs ++ ys k, k n xs = (range' (k + 1) (n - k)).reverse ys = iota k := by
simp only [iota_eq_reverse_range']
rw [reverse_eq_append]
rw [range'_eq_append_iff]
simp only [reverse_eq_iff]
constructor
· rintro k, h, rfl, rfl
simp; omega
· rintro k, h, rfl, rfl
exact k, by simp; omega
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range' 1 n
@@ -214,12 +393,85 @@ theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
(pairwise_gt_iota n).imp Nat.ne_of_gt
@[simp] theorem head?_iota (n : Nat) : (iota n).head? = if n = 0 then none else some n := by
cases n <;> simp
@[simp] theorem head_iota (n : Nat) (h) : (iota n).head h = n := by
cases n with
| zero => simp at h
| succ n => simp
@[simp] theorem reverse_iota : reverse (iota n) = range' 1 n := by
induction n with
| zero => simp
| succ n ih =>
rw [iota_succ, reverse_cons, ih, range'_1_concat, Nat.add_comm]
@[simp] theorem getLast?_iota (n : Nat) : (iota n).getLast? = if n = 0 then none else some 1 := by
rw [getLast?_eq_head?_reverse]
simp [head?_range']
@[simp] theorem getLast_iota (n : Nat) (h) : (iota n).getLast h = 1 := by
rw [getLast_eq_head_reverse]
simp
@[simp] theorem find?_iota_eq_none (n : Nat) (p : Nat Bool) :
(iota n).find? p = none i, 0 < i i n !p i := by
rw [find?_eq_none]
simp
@[simp] theorem find?_iota_eq_some (n : Nat) (i : Nat) (p : Nat Bool) :
(iota n).find? p = some i p i i iota n j, i < j j n !p j := by
rw [find?_eq_some]
simp only [iota_eq_reverse_range', reverse_eq_append, reverse_cons, append_assoc,
singleton_append, Bool.not_eq_true', exists_and_right, mem_reverse, mem_range'_1,
and_congr_right_iff]
intro h
constructor
· rintro as, xs, h, h'
constructor
· replace h : i range' 1 n := by
rw [h]
exact mem_append_cons_self
simpa using h
· rw [range'_eq_append_iff] at h
simp [reverse_eq_iff] at h
obtain k, h₁, rfl, h₂ := h
rw [eq_comm, range'_eq_cons_iff, reverse_eq_iff] at h₂
obtain rfl, -, rfl := h₂
intro j j₁ j₂
apply h'
simp; omega
· rintro i₁, i₂, h
refine (range' (i+1) (n-i)).reverse, (range' 1 (i-1)).reverse, ?_, ?_
· simp [ range'_succ]
rw [range'_eq_append_iff]
refine i-1, ?_
constructor
· omega
· simp
omega
· simp
intros a a₁ a₂
apply h
· omega
· omega
/-! ### enumFrom -/
@[simp]
theorem enumFrom_singleton (x : α) (n : Nat) : enumFrom n [x] = [(n, x)] :=
rfl
@[simp] theorem head?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).head? = l.head?.map fun a => (n, a) := by
simp [head?_eq_getElem?]
@[simp] theorem getLast?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).getLast? = l.getLast?.map fun a => (n + l.length - 1, a) := by
simp [getLast?_eq_getElem?]
cases l <;> simp; omega
theorem mk_add_mem_enumFrom_iff_getElem? {n i : Nat} {x : α} {l : List α} :
(n + i, x) enumFrom n l l[i]? = some x := by
simp [mem_iff_get?]
@@ -260,9 +512,24 @@ theorem enumFrom_map_snd : ∀ (n) (l : List α), map Prod.snd (enumFrom n l) =
theorem snd_mem_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) : x.2 l :=
enumFrom_map_snd n l mem_map_of_mem _ h
theorem mem_enumFrom {x : α} {i j : Nat} (xs : List α) (h : (i, x) xs.enumFrom j) :
j i i < j + xs.length x xs :=
le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_mem_of_mem_enumFrom h
theorem snd_eq_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.2 = l[x.1 - n]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) := by
induction l generalizing n with
| nil => cases h
| cons hd tl ih =>
cases h with
| head h => simp
| tail h m =>
specialize ih m
have : x.1 - n = x.1 - (n + 1) + 1 := by
have := le_fst_of_mem_enumFrom m
omega
simp [this, ih]
theorem mem_enumFrom {x : α} {i j : Nat} {xs : List α} (h : (i, x) xs.enumFrom j) :
j i i < j + xs.length
x = xs[i - j]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) :=
le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_eq_of_mem_enumFrom h
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
@@ -317,6 +584,14 @@ theorem getElem_enum (l : List α) (i : Nat) (h : i < l.enum.length) :
l.enum[i] = (i, l[i]'(by simpa [enum_length] using h)) := by
simp [enum]
@[simp] theorem head?_enum (l : List α) :
l.enum.head? = l.head?.map fun a => (0, a) := by
simp [head?_eq_getElem?]
@[simp] theorem getLast?_enum (l : List α) :
l.enum.getLast? = l.getLast?.map fun a => (l.length - 1, a) := by
simp [getLast?_eq_getElem?]
theorem mk_mem_enum_iff_getElem? {i : Nat} {x : α} {l : List α} : (i, x) enum l l[i]? = x := by
simp [enum, mk_mem_enumFrom_iff_le_and_getElem?_sub]
@@ -329,6 +604,14 @@ theorem fst_lt_of_mem_enum {x : Nat × α} {l : List α} (h : x ∈ enum l) : x.
theorem snd_mem_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.2 l :=
snd_mem_of_mem_enumFrom h
theorem snd_eq_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) :
x.2 = l[x.1]'(fst_lt_of_mem_enum h) :=
snd_eq_of_mem_enumFrom h
theorem mem_enum {x : α} {i : Nat} {xs : List α} (h : (i, x) xs.enum) :
i < xs.length x = xs[i]'(fst_lt_of_mem_enum h) :=
by simpa using mem_enumFrom h
theorem map_enum (f : α β) (l : List α) : map (Prod.map id f) (enum l) = enum (map f l) :=
map_enumFrom f 0 l

View File

@@ -126,4 +126,49 @@ theorem prefix_take_le_iff {L : List α} (hm : m < L.length) :
simp only [length_cons, Nat.succ_eq_add_one, Nat.add_lt_add_iff_right] at hm
simp [ @IH n ls hm, Nat.min_eq_left, Nat.le_of_lt hm]
@[simp] theorem append_left_sublist_self (xs ys : List α) : xs ++ ys <+ ys xs = [] := by
constructor
· intro h
replace h := h.length_le
simp only [length_append] at h
have : xs.length = 0 := by omega
simp_all
· rintro rfl
simp
@[simp] theorem append_right_sublist_self (xs ys : List α) : xs ++ ys <+ xs ys = [] := by
constructor
· intro h
replace h := h.length_le
simp only [length_append] at h
have : ys.length = 0 := by omega
simp_all
· rintro rfl
simp
theorem append_sublist_of_sublist_left (xs ys zs : List α) (h : zs <+ xs) :
xs ++ ys <+ zs ys = [] xs = zs := by
constructor
· intro h'
have hl := h.length_le
have hl' := h'.length_le
simp only [length_append] at hl'
have : ys.length = 0 := by omega
simp_all only [Nat.add_zero, length_eq_zero, true_and, append_nil]
exact Sublist.eq_of_length_le h' hl
· rintro rfl, rfl
simp
theorem append_sublist_of_sublist_right (xs ys zs : List α) (h : zs <+ ys) :
xs ++ ys <+ zs xs = [] ys = zs := by
constructor
· intro h'
have hl := h.length_le
have hl' := h'.length_le
simp only [length_append] at hl'
have : xs.length = 0 := by omega
simp_all only [Nat.zero_add, length_eq_zero, true_and, append_nil]
exact Sublist.eq_of_length_le h' hl
· rintro rfl, rfl
simp
end List

View File

@@ -70,20 +70,20 @@ theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
(l.take n).get? m = none := by
simp [getElem?_take_eq_none h]
theorem getElem?_take_eq_if {l : List α} {n m : Nat} :
theorem getElem?_take {l : List α} {n m : Nat} :
(l.take n)[m]? = if m < n then l[m]? else none := by
split
· next h => exact getElem?_take h
· next h => exact getElem?_take_of_lt h
· next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
@[deprecated getElem?_take_eq_if (since := "2024-06-12")]
@[deprecated getElem?_take (since := "2024-06-12")]
theorem get?_take_eq_if {l : List α} {n m : Nat} :
(l.take n).get? m = if m < n then l.get? m else none := by
simp [getElem?_take_eq_if]
simp [getElem?_take]
theorem head?_take {l : List α} {n : Nat} :
(l.take n).head? = if n = 0 then none else l.head? := by
simp [head?_eq_getElem?, getElem?_take_eq_if]
simp [head?_eq_getElem?, getElem?_take]
split
· rw [if_neg (by omega)]
· rw [if_pos (by omega)]
@@ -95,7 +95,7 @@ theorem head_take {l : List α} {n : Nat} (h : l.take n ≠ []) :
simp_all
theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none else l[n - 1]?.or l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_take_eq_if, length_take]
rw [getLast?_eq_getElem?, getElem?_take, length_take]
split
· rw [if_neg (by omega)]
rw [Nat.min_def]
@@ -128,7 +128,7 @@ theorem take_take : ∀ (n m) (l : List α), take n (take m l) = take (min n m)
theorem take_set_of_lt (a : α) {n m : Nat} (l : List α) (h : m < n) :
(l.set n a).take m = l.take m :=
List.ext_getElem? fun i => by
rw [getElem?_take_eq_if, getElem?_take_eq_if]
rw [getElem?_take, getElem?_take]
split
· next h' => rw [getElem?_set_ne (by omega)]
· rfl
@@ -203,7 +203,7 @@ theorem map_eq_append_split {f : α → β} {l : List α} {s₁ s₂ : List β}
theorem take_prefix_take_left (l : List α) {m n : Nat} (h : m n) : take m l <+: take n l := by
rw [isPrefix_iff]
intro i w
rw [getElem?_take, getElem_take', getElem?_eq_getElem]
rw [getElem?_take_of_lt, getElem_take', getElem?_eq_getElem]
simp only [length_take] at w
exact Nat.lt_of_lt_of_le (Nat.lt_of_lt_of_le w (Nat.min_le_left _ _)) h
@@ -275,7 +275,7 @@ theorem head?_drop (l : List α) (n : Nat) :
theorem head_drop {l : List α} {n : Nat} (h : l.drop n []) :
(l.drop n).head h = l[n]'(by simp_all) := by
have w : n < l.length := length_lt_of_drop_ne_nil h
simpa [head?_eq_head, getElem?_eq_getElem, h, w] using head?_drop l n
simpa [getElem?_eq_getElem, h, w, head_eq_iff_head?_eq_some] using head?_drop l n
theorem getLast?_drop {l : List α} : (l.drop n).getLast? = if l.length n then none else l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_drop]
@@ -334,7 +334,7 @@ theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
· ext1 m
by_cases h' : m < n
· rw [getElem?_append_left (by simp [length_take]; omega), getElem?_set_ne (by omega),
getElem?_take h']
getElem?_take_of_lt h']
· by_cases h'' : m = n
· subst h''
rw [getElem?_set_eq _, getElem?_append_right, length_take,
@@ -373,40 +373,67 @@ theorem drop_take : ∀ (m n : Nat) (l : List α), drop n (take m l) = take (m -
congr 1
omega
theorem take_reverse {α} {xs : List α} {n : Nat} (h : n xs.length) :
theorem take_reverse {α} {xs : List α} {n : Nat} :
xs.reverse.take n = (xs.drop (xs.length - n)).reverse := by
induction xs generalizing n <;>
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
next xs_hd xs_tl xs_ih =>
cases Nat.lt_or_eq_of_le h with
| inl h' =>
have h' := Nat.le_of_succ_le_succ h'
rw [take_append_of_le_length, xs_ih h']
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
· rwa [succ_eq_add_one, Nat.sub_add_comm]
· rwa [length_reverse]
| inr h' =>
subst h'
rw [length, Nat.sub_self, drop]
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
rw [this, take_length, reverse_cons]
rw [length_append, length_reverse]
rfl
by_cases h : n xs.length
· induction xs generalizing n <;>
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
next xs_hd xs_tl xs_ih =>
cases Nat.lt_or_eq_of_le h with
| inl h' =>
have h' := Nat.le_of_succ_le_succ h'
rw [take_append_of_le_length, xs_ih h']
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
· rwa [succ_eq_add_one, Nat.sub_add_comm]
· rwa [length_reverse]
| inr h' =>
subst h'
rw [length, Nat.sub_self, drop]
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
rw [this, take_length, reverse_cons]
rw [length_append, length_reverse]
rfl
· have w : xs.length - n = 0 := by omega
rw [take_of_length_le, w, drop_zero]
simp
omega
@[deprecated (since := "2024-06-15")] abbrev reverse_take := @take_reverse
theorem drop_reverse {α} {xs : List α} {n : Nat} (h : n xs.length) :
theorem drop_reverse {α} {xs : List α} {n : Nat} :
xs.reverse.drop n = (xs.take (xs.length - n)).reverse := by
conv =>
rhs
rw [ reverse_reverse xs]
rw [ reverse_reverse xs] at h
generalize xs.reverse = xs' at h
rw [take_reverse]
· simp only [length_reverse, reverse_reverse] at *
by_cases h : n xs.length
· conv =>
rhs
rw [ reverse_reverse xs]
rw [ reverse_reverse xs] at h
generalize xs.reverse = xs' at h
rw [take_reverse]
· simp only [length_reverse, reverse_reverse] at *
congr
omega
· have w : xs.length - n = 0 := by omega
rw [drop_of_length_le, w, take_zero, reverse_nil]
simp
omega
theorem reverse_take {l : List α} {n : Nat} :
(l.take n).reverse = l.reverse.drop (l.length - n) := by
by_cases h : n l.length
· rw [drop_reverse]
congr
omega
· simp only [length_reverse, sub_le]
· have w : l.length - n = 0 := by omega
rw [w, drop_zero, take_of_length_le]
omega
theorem reverse_drop {l : List α} {n : Nat} :
(l.drop n).reverse = l.reverse.take (l.length - n) := by
by_cases h : n l.length
· rw [take_reverse]
congr
omega
· have w : l.length - n = 0 := by omega
rw [w, take_zero, drop_of_length_le, reverse_nil]
omega
/-! ### rotateLeft -/

View File

@@ -123,7 +123,7 @@ theorem pairwise_filterMap (f : β → Option α) {l : List β} :
match e : f a with
| none =>
rw [filterMap_cons_none e, pairwise_cons]
simp only [e, false_implies, implies_true, true_and, IH]
simp only [e, false_implies, implies_true, true_and, IH, reduceCtorEq]
| some b =>
rw [filterMap_cons_some e]
simpa [IH, e] using fun _ =>
@@ -226,6 +226,18 @@ theorem pairwise_iff_forall_sublist : l.Pairwise R ↔ (∀ {a b}, [a,b] <+ l
intro a b hab
apply h; exact hab.cons _
theorem Pairwise.rel_of_mem_take_of_mem_drop
{l : List α} (h : l.Pairwise R) (hx : x l.take n) (hy : y l.drop n) : R x y := by
apply pairwise_iff_forall_sublist.mp h
rw [ take_append_drop n l, sublist_append_iff]
refine [x], [y], rfl, by simpa, by simpa
theorem Pairwise.rel_of_mem_append
{l₁ l₂ : List α} (h : (l₁ ++ l₂).Pairwise R) (hx : x l₁) (hy : y l₂) : R x y := by
apply pairwise_iff_forall_sublist.mp h
rw [sublist_append_iff]
exact [x], [y], rfl, by simpa, by simpa
theorem pairwise_of_forall_mem_list {l : List α} {r : α α Prop} (h : a l, b l, r a b) :
l.Pairwise r := by
rw [pairwise_iff_forall_sublist]

View File

@@ -123,10 +123,8 @@ theorem Perm.nil_eq {l : List α} (p : [] ~ l) : [] = l := p.symm.eq_nil.symm
@[simp] theorem nil_perm {l₁ : List α} : [] ~ l₁ l₁ = [] := perm_comm.trans perm_nil
@[simp]
theorem not_perm_nil_cons (x : α) (l : List α) : ¬[] ~ x :: l := (nomatch ·.symm.eq_nil)
@[simp]
theorem not_perm_cons_nil {l : List α} {a : α} : ¬(Perm (a::l) []) :=
fun h => by simpa using h.length_eq
@@ -400,6 +398,40 @@ theorem Pairwise.perm {R : αα → Prop} {l l' : List α} (hR : l.Pairwise
theorem Perm.pairwise {R : α α Prop} {l l' : List α} (hl : l ~ l') (hR : l.Pairwise R)
(hsymm : {x y}, R x y R y x) : l'.Pairwise R := hR.perm hl hsymm
/--
If two lists are sorted by an antisymmetric relation, and permutations of each other,
they must be equal.
-/
theorem Perm.eq_of_sorted : {l₁ l₂ : List α}
(_ : a b, a l₁ b l₂ le a b le b a a = b)
(_ : l₁.Pairwise le) (_ : l₂.Pairwise le) (_ : l₁ ~ l₂), l₁ = l₂
| [], [], _, _, _, _ => rfl
| [], b :: l₂, _, _, _, h => by simp_all
| a :: l₁, [], _, _, _, h => by simp_all
| a :: l₁, b :: l₂, w, h₁, h₂, h => by
have am : a b :: l₂ := h.subset (mem_cons_self _ _)
have bm : b a :: l₁ := h.symm.subset (mem_cons_self _ _)
have ab : a = b := by
simp only [mem_cons] at am
rcases am with rfl | am
· rfl
· simp only [mem_cons] at bm
rcases bm with rfl | bm
· rfl
· exact w _ _ (mem_cons_self _ _) (mem_cons_self _ _)
(rel_of_pairwise_cons h₁ bm) (rel_of_pairwise_cons h₂ am)
subst ab
simp only [perm_cons] at h
have := Perm.eq_of_sorted
(fun x y hx hy => w x y (mem_cons_of_mem a hx) (mem_cons_of_mem a hy))
h₁.tail h₂.tail h
simp_all
theorem Nodup.perm {l l' : List α} (hR : l.Nodup) (hl : l ~ l') : l'.Nodup :=
Pairwise.perm hR hl (by intro x y h h'; simp_all)
theorem Perm.nodup {l l' : List α} (hl : l ~ l') (hR : l.Nodup) : l'.Nodup := hR.perm hl
theorem Perm.nodup_iff {l₁ l₂ : List α} : l₁ ~ l₂ (Nodup l₁ Nodup l₂) :=
Perm.pairwise_iff <| @Ne.symm α

View File

@@ -0,0 +1,9 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Sort.Basic
import Init.Data.List.Sort.Impl
import Init.Data.List.Sort.Lemmas

View File

@@ -0,0 +1,81 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Impl
/-!
# Definition of `merge` and `mergeSort`.
These definitions are intended for verification purposes,
and are replaced at runtime by efficient versions in `Init.Data.List.Sort.Impl`.
-/
namespace List
/--
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
This version is not tail-recursive,
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
-/
def merge (le : α α Bool) : List α List α List α
| [], ys => ys
| xs, [] => xs
| x :: xs, y :: ys =>
if le x y then
x :: merge le xs (y :: ys)
else
y :: merge le (x :: xs) ys
@[simp] theorem nil_merge (ys : List α) : merge le [] ys = ys := by simp [merge]
@[simp] theorem merge_right (xs : List α) : merge le xs [] = xs := by
induction xs with
| nil => simp [merge]
| cons x xs ih => simp [merge, ih]
/--
Split a list in two equal parts. If the length is odd, the first part will be one element longer.
-/
def splitInTwo (l : { l : List α // l.length = n }) :
{ l : List α // l.length = (n+1)/2 } × { l : List α // l.length = n/2 } :=
let r := splitAt ((n+1)/2) l.1
(r.1, by simp [r, splitAt_eq, l.2]; omega, r.2, by simp [r, splitAt_eq, l.2]; omega)
/--
Simplified implementation of stable merge sort.
This function is designed for reasoning about the algorithm, and is not efficient.
(It particular it uses the non tail-recursive `merge` function,
and so can not be run on large lists, but also makes unnecessary traversals of lists.)
It is replaced at runtime in the compiler by `mergeSortTR₂` using a `@[csimp]` lemma.
Because we want the sort to be stable,
it is essential that we split the list in two contiguous sublists.
-/
def mergeSort (le : α α Bool) : List α List α
| [] => []
| [a] => [a]
| a :: b :: xs =>
let lr := splitInTwo a :: b :: xs, rfl
have := by simpa using lr.2.2
have := by simpa using lr.1.2
merge le (mergeSort le lr.1) (mergeSort le lr.2)
termination_by l => l.length
/--
Given an ordering relation `le : αα → Bool`,
construct the reverse lexicographic ordering on `Nat × α`.
which first compares the second components using `le`,
but if these are equivalent (in the sense `le a.2 b.2 && le b.2 a.2`)
then compares the first components using `≤`.
This function is only used in stating the stability properties of `mergeSort`.
-/
def enumLE (le : α α Bool) (a b : Nat × α) : Bool :=
if le a.2 b.2 then if le b.2 a.2 then a.1 b.1 else true else false
end List

View File

@@ -0,0 +1,237 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Sort.Lemmas
/-!
# Replacing `merge` and `mergeSort` at runtime with tail-recursive and faster versions.
We replace `merge` with `mergeTR` using a `@[csimp]` lemma.
We replace `mergeSort` in two steps:
* first with `mergeSortTR`, which while not tail-recursive itself (it can't be),
uses `mergeTR` internally.
* second with `mergeSortTR₂`, which achieves an ~20% speed-up over `mergeSortTR`
by avoiding some unnecessary list reversals.
There is no public API in this file; it solely exists to implement the `@[csimp]` lemmas
affecting runtime behaviour.
## Future work
The current runtime implementation could be further improved in a number of ways, e.g.:
* only walking the list once during splitting,
* using insertion sort for small chunks rather than splitting all the way down to singletons,
* identifying already sorted or reverse sorted chunks and skipping them.
Because the theory developed for `mergeSort` is independent of the runtime implementation,
as long as such improvements are carefully validated by benchmarking,
they can be done without changing the theory, as long as a `@[csimp]` lemma is provided.
-/
open List
namespace List.MergeSort.Internal
/--
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
-/
def mergeTR (le : α α Bool) (l₁ l₂ : List α) : List α :=
go l₁ l₂ []
where go : List α List α List α List α
| [], l₂, acc => reverseAux acc l₂
| l₁, [], acc => reverseAux acc l₁
| x :: xs, y :: ys, acc =>
if le x y then
go xs (y :: ys) (x :: acc)
else
go (x :: xs) ys (y :: acc)
theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge le l₁ l₂ := by
induction l₁ generalizing l₂ acc with
| nil => simp [mergeTR.go, merge, reverseAux_eq]
| cons x l₁ ih₁ =>
induction l₂ generalizing acc with
| nil => simp [mergeTR.go, merge, reverseAux_eq]
| cons y l₂ ih₂ =>
simp [mergeTR.go, merge]
split <;> simp [ih₁, ih₂]
@[csimp] theorem merge_eq_mergeTR : @merge = @mergeTR := by
funext
simp [mergeTR, mergeTR_go_eq]
/--
Variant of `splitAt`, that does not reverse the first list, i.e
`splitRevAt n l = ((l.take n).reverse, l.drop n)`.
This exists solely as an optimization for `mergeSortTR` and `mergeSortTR₂`,
and should not be used elsewhere.
-/
def splitRevAt (n : Nat) (l : List α) : List α × List α := go l n [] where
/-- Auxiliary for `splitAtRev`: `splitAtRev.go xs n acc = ((take n xs).reverse ++ acc, drop n xs)`. -/
go : List α Nat List α List α × List α
| x :: xs, n+1, acc => go xs n (x :: acc)
| xs, _, acc => (acc, xs)
theorem splitRevAt_go (xs : List α) (n : Nat) (acc : List α) :
splitRevAt.go xs n acc = ((take n xs).reverse ++ acc, drop n xs) := by
induction xs generalizing n acc with
| nil => simp [splitRevAt.go]
| cons x xs ih =>
cases n with
| zero => simp [splitRevAt.go]
| succ n =>
rw [splitRevAt.go, ih n (x :: acc), take_succ_cons, reverse_cons, drop_succ_cons,
append_assoc, singleton_append]
theorem splitRevAt_eq (n : Nat) (l : List α) : splitRevAt n l = ((l.take n).reverse, l.drop n) := by
rw [splitRevAt, splitRevAt_go, append_nil]
/--
An intermediate speed-up for `mergeSort`.
This version uses the tail-recurive `mergeTR` function as a subroutine.
This is not the final version we use at runtime, as `mergeSortTR₂` is faster.
This definition is useful as an intermediate step in proving the `@[csimp]` lemma for `mergeSortTR₂`.
-/
def mergeSortTR (le : α α Bool) (l : List α) : List α :=
run l, rfl
where run : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitInTwo xs
mergeTR le (run l) (run r)
/--
Split a list in two equal parts, reversing the first part.
If the length is odd, the first part will be one element longer.
-/
def splitRevInTwo (l : { l : List α // l.length = n }) :
{ l : List α // l.length = (n+1)/2 } × { l : List α // l.length = n/2 } :=
let r := splitRevAt ((n+1)/2) l.1
(r.1, by simp [r, splitRevAt_eq, l.2]; omega, r.2, by simp [r, splitRevAt_eq, l.2]; omega)
/--
Split a list in two equal parts, reversing the first part.
If the length is odd, the second part will be one element longer.
-/
def splitRevInTwo' (l : { l : List α // l.length = n }) :
{ l : List α // l.length = n/2 } × { l : List α // l.length = (n+1)/2 } :=
let r := splitRevAt (n/2) l.1
(r.1, by simp [r, splitRevAt_eq, l.2]; omega, r.2, by simp [r, splitRevAt_eq, l.2]; omega)
/--
Faster version of `mergeSortTR`, which avoids unnecessary list reversals.
-/
-- Per the benchmark in `tests/bench/mergeSort/`
-- (which averages over 4 use cases: already sorted lists, reverse sorted lists, almost sorted lists, and random lists),
-- for lists of length 10^6, `mergeSortTR₂` is about 20% faster than `mergeSortTR`.
def mergeSortTR₂ (le : α α Bool) (l : List α) : List α :=
run l, rfl
where
run : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitRevInTwo xs
mergeTR le (run' l) (run r)
run' : {n : Nat} { l : List α // l.length = n } List α
| 0, [], _ => []
| 1, [a], _ => [a]
| n+2, xs =>
let (l, r) := splitRevInTwo' xs
mergeTR le (run' r) (run l)
theorem splitRevInTwo'_fst (l : { l : List α // l.length = n }) :
(splitRevInTwo' l).1 = (splitInTwo l.1.reverse, by simpa using l.2).2.1, by have := l.2; simp; omega := by
simp only [splitRevInTwo', splitRevAt_eq, reverse_take, splitInTwo_snd]
congr
have := l.2
omega
theorem splitRevInTwo'_snd (l : { l : List α // l.length = n }) :
(splitRevInTwo' l).2 = (splitInTwo l.1.reverse, by simpa using l.2).1.1.reverse, by have := l.2; simp; omega := by
simp only [splitRevInTwo', splitRevAt_eq, reverse_take, splitInTwo_fst, reverse_reverse]
congr 2
have := l.2
simp
omega
theorem splitRevInTwo_fst (l : { l : List α // l.length = n }) :
(splitRevInTwo l).1 = (splitInTwo l).1.1.reverse, by have := l.2; simp; omega := by
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_fst]
theorem splitRevInTwo_snd (l : { l : List α // l.length = n }) :
(splitRevInTwo l).2 = (splitInTwo l).2.1, by have := l.2; simp; omega := by
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_snd]
theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort le l.1
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR.run, mergeSort]
| n+2, a :: b :: l, h => by
cases h
simp only [mergeSortTR.run, mergeSortTR.run, mergeSort]
rw [merge_eq_mergeTR]
rw [mergeSortTR_run_eq_mergeSort, mergeSortTR_run_eq_mergeSort]
-- We don't make this a `@[csimp]` lemma because `mergeSort_eq_mergeSortTR₂` is faster.
theorem mergeSort_eq_mergeSortTR : @mergeSort = @mergeSortTR := by
funext
rw [mergeSortTR, mergeSortTR_run_eq_mergeSort]
-- This mutual block is unfortunately quite slow to elaborate.
set_option maxHeartbeats 400000 in
mutual
theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort le l.1
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR₂.run, mergeSort]
| n+2, a :: b :: l, h => by
cases h
simp only [mergeSortTR₂.run, mergeSort]
rw [splitRevInTwo_fst, splitRevInTwo_snd]
rw [mergeSortTR₂_run_eq_mergeSort, mergeSortTR₂_run'_eq_mergeSort]
rw [merge_eq_mergeTR]
rw [reverse_reverse]
termination_by n => n
theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort le l'
| 0, [], _, w
| 1, [a], _, w => by simp_all [mergeSortTR₂.run', mergeSort]
| n+2, a :: b :: l, h, w => by
cases h
simp only [mergeSortTR₂.run', mergeSort]
rw [splitRevInTwo'_fst, splitRevInTwo'_snd]
rw [mergeSortTR₂_run_eq_mergeSort, mergeSortTR₂_run'_eq_mergeSort _ rfl]
rw [ merge_eq_mergeTR]
have w' := congrArg length w
simp at w'
cases l' with
| nil => simp at w'
| cons x l' =>
cases l' with
| nil => simp at w'
| cons y l' =>
rw [mergeSort]
congr 2
· dsimp at w
simp only [w]
simp only [splitInTwo_fst, splitInTwo_snd, reverse_take, take_reverse]
congr 1
rw [w, length_reverse]
simp
· dsimp at w
simp only [w]
simp only [reverse_cons, append_assoc, singleton_append, splitInTwo_snd, length_cons]
congr 1
simp at w'
omega
termination_by n => n
end
@[csimp] theorem mergeSort_eq_mergeSortTR₂ : @mergeSort = @mergeSortTR₂ := by
funext
rw [mergeSortTR₂, mergeSortTR₂_run_eq_mergeSort]
end List.MergeSort.Internal

View File

@@ -0,0 +1,406 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Perm
import Init.Data.List.Sort.Basic
import Init.Data.Bool
/-!
# Basic properties of `mergeSort`.
* `mergeSort_sorted`: `mergeSort` produces a sorted list.
* `mergeSort_perm`: `mergeSort` is a permutation of the input list.
* `mergeSort_of_sorted`: `mergeSort` does not change a sorted list.
* `mergeSort_cons`: proves `mergeSort le (x :: xs) = l₁ ++ x :: l₂` for some `l₁, l₂`
so that `mergeSort le xs = l₁ ++ l₂`, and no `a ∈ l₁` satisfies `le a x`.
* `mergeSort_stable`: if `c` is a sorted sublist of `l`, then `c` is still a sublist of `mergeSort le l`.
-/
namespace List
-- We enable this instance locally so we can write `Sorted le` instead of `Sorted (le · ·)` everywhere.
attribute [local instance] boolRelToRel
variable {le : α α Bool}
/-! ### splitInTwo -/
@[simp] theorem splitInTwo_fst (l : { l : List α // l.length = n }) : (splitInTwo l).1 = l.1.take ((n+1)/2), by simp [splitInTwo, splitAt_eq, l.2]; omega := by
simp [splitInTwo, splitAt_eq]
@[simp] theorem splitInTwo_snd (l : { l : List α // l.length = n }) : (splitInTwo l).2 = l.1.drop ((n+1)/2), by simp [splitInTwo, splitAt_eq, l.2]; omega := by
simp [splitInTwo, splitAt_eq]
theorem splitInTwo_fst_append_splitInTwo_snd (l : { l : List α // l.length = n }) : (splitInTwo l).1.1 ++ (splitInTwo l).2.1 = l.1 := by
simp
theorem splitInTwo_cons_cons_enumFrom_fst (i : Nat) (l : List α) :
(splitInTwo (i, a) :: (i+1, b) :: l.enumFrom (i+2), rfl).1.1 =
(splitInTwo a :: b :: l, rfl).1.1.enumFrom i := by
simp only [length_cons, splitInTwo_fst, enumFrom_length]
ext1 j
rw [getElem?_take, getElem?_enumFrom, getElem?_take]
split
· rw [getElem?_cons, getElem?_cons, getElem?_cons, getElem?_cons]
split
· simp; omega
· split
· simp; omega
· simp only [getElem?_enumFrom]
congr
ext <;> simp; omega
· simp
theorem splitInTwo_cons_cons_enumFrom_snd (i : Nat) (l : List α) :
(splitInTwo (i, a) :: (i+1, b) :: l.enumFrom (i+2), rfl).2.1 =
(splitInTwo a :: b :: l, rfl).2.1.enumFrom (i+(l.length+3)/2) := by
simp only [length_cons, splitInTwo_snd, enumFrom_length]
ext1 j
rw [getElem?_drop, getElem?_enumFrom, getElem?_drop]
rw [getElem?_cons, getElem?_cons, getElem?_cons, getElem?_cons]
split
· simp; omega
· split
· simp; omega
· simp only [getElem?_enumFrom]
congr
ext <;> simp; omega
theorem splitInTwo_fst_sorted (l : { l : List α // l.length = n }) (h : Pairwise le l.1) : Pairwise le (splitInTwo l).1.1 := by
rw [splitInTwo_fst]
exact h.take
theorem splitInTwo_snd_sorted (l : { l : List α // l.length = n }) (h : Pairwise le l.1) : Pairwise le (splitInTwo l).2.1 := by
rw [splitInTwo_snd]
exact h.drop
theorem splitInTwo_fst_le_splitInTwo_snd {l : { l : List α // l.length = n }} (h : Pairwise le l.1) :
a b, a (splitInTwo l).1.1 b (splitInTwo l).2.1 le a b := by
rw [splitInTwo_fst, splitInTwo_snd]
intro a b ma mb
exact h.rel_of_mem_take_of_mem_drop ma mb
/-! ### enumLE -/
theorem enumLE_trans (trans : a b c, le a b le b c le a c)
(a b c : Nat × α) : enumLE le a b enumLE le b c enumLE le a c := by
simp only [enumLE]
split <;> split <;> split <;> rename_i ab₂ ba₂ bc₂
· simp_all
intro ab₁
intro h
refine trans _ _ _ ab₂ bc₂, ?_
rcases h with (cd₂ | bc₁)
· exact Or.inl (Decidable.byContradiction
(fun ca₂ => by simp_all [trans _ _ _ (by simpa using ca₂) ab₂]))
· exact Or.inr (Nat.le_trans ab₁ bc₁)
· simp_all
· simp_all
intro h
refine trans _ _ _ ab₂ bc₂, ?_
left
rcases h with (cb₂ | _)
· exact (Decidable.byContradiction
(fun ca₂ => by simp_all [trans _ _ _ (by simpa using ca₂) ab₂]))
· exact (Decidable.byContradiction
(fun ca₂ => by simp_all [trans _ _ _ bc₂ (by simpa using ca₂)]))
· simp_all
· simp_all
· simp_all
· simp_all
· simp_all
theorem enumLE_total (total : a b, !le a b le b a)
(a b : Nat × α) : !enumLE le a b enumLE le b a := by
simp only [enumLE]
split <;> split
· simpa using Nat.le_of_lt
· simp
· simp
· simp_all [total a.2 b.2]
/-! ### merge -/
theorem merge_stable : (xs ys) (_ : x y, x xs y ys x.1 y.1),
(merge (enumLE le) xs ys).map (·.2) = merge le (xs.map (·.2)) (ys.map (·.2))
| [], ys, _ => by simp [merge]
| xs, [], _ => by simp [merge]
| (i, x) :: xs, (j, y) :: ys, h => by
simp only [merge, enumLE, map_cons]
split <;> rename_i w
· rw [if_pos (by simp [h _ _ (mem_cons_self ..) (mem_cons_self ..)])]
simp only [map_cons, cons.injEq, true_and]
rw [merge_stable, map_cons]
exact fun x' y' mx my => h x' y' (mem_cons_of_mem (i, x) mx) my
· simp only [reduceIte, map_cons, cons.injEq, true_and, reduceCtorEq]
rw [merge_stable, map_cons]
exact fun x' y' mx my => h x' y' mx (mem_cons_of_mem (j, y) my)
/--
The elements of `merge le xs ys` are exactly the elements of `xs` and `ys`.
-/
-- We subsequently prove that `mergeSort_perm : merge le xs ys ~ xs ++ ys`.
theorem mem_merge {a : α} {xs ys : List α} : a merge le xs ys a xs a ys := by
induction xs generalizing ys with
| nil => simp [merge]
| cons x xs ih =>
induction ys with
| nil => simp [merge]
| cons y ys ih =>
simp only [merge]
split <;> rename_i h
· simp_all [or_assoc]
· simp only [mem_cons, or_assoc, Bool.not_eq_true, ih, or_assoc]
apply or_congr_left
simp only [or_comm (a := a = y), or_assoc]
/--
If the ordering relation `le` is transitive and total (i.e. `le a b le b a` for all `a, b`)
then the `merge` of two sorted lists is sorted.
-/
theorem merge_sorted
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(l₁ l₂ : List α) (h₁ : l₁.Pairwise le) (h₂ : l₂.Pairwise le) : (merge le l₁ l₂).Pairwise le := by
induction l₁ generalizing l₂ with
| nil => simpa only [merge]
| cons x l₁ ih₁ =>
induction l₂ with
| nil => simpa only [merge]
| cons y l₂ ih₂ =>
simp only [merge]
split <;> rename_i h
· apply Pairwise.cons
· intro z m
rw [mem_merge, mem_cons] at m
rcases m with (m|rfl|m)
· exact rel_of_pairwise_cons h₁ m
· exact h
· exact trans _ _ _ h (rel_of_pairwise_cons h₂ m)
· exact ih₁ _ h₁.tail h₂
· apply Pairwise.cons
· intro z m
rw [mem_merge, mem_cons] at m
rcases m with (rfl|m|m)
· exact total _ _ (by simpa using h)
· exact trans _ _ _ (total _ _ (by simpa using h)) (rel_of_pairwise_cons h₁ m)
· exact rel_of_pairwise_cons h₂ m
· exact ih₂ h₂.tail
theorem merge_of_le : {xs ys : List α} (_ : a b, a xs b ys le a b),
merge le xs ys = xs ++ ys
| [], ys, _
| xs, [], _ => by simp [merge]
| x :: xs, y :: ys, h => by
simp only [merge, cons_append]
rw [if_pos, merge_of_le]
· intro a b ma mb
exact h a b (mem_cons_of_mem _ ma) mb
· exact h x y (mem_cons_self _ _) (mem_cons_self _ _)
variable (le) in
theorem merge_perm_append : {xs ys : List α}, merge le xs ys ~ xs ++ ys
| [], ys => by simp [merge]
| xs, [] => by simp [merge]
| x :: xs, y :: ys => by
simp only [merge]
split
· exact merge_perm_append.cons x
· exact (merge_perm_append.cons y).trans
((Perm.swap x y _).trans (perm_middle.symm.cons x))
/-! ### mergeSort -/
@[simp] theorem mergeSort_nil : [].mergeSort r = [] := by rw [List.mergeSort]
@[simp] theorem mergeSort_singleton (a : α) : [a].mergeSort r = [a] := by rw [List.mergeSort]
variable (le) in
theorem mergeSort_perm : (l : List α), mergeSort le l ~ l
| [] => by simp [mergeSort]
| [a] => by simp [mergeSort]
| a :: b :: xs => by
simp only [mergeSort]
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
exact (merge_perm_append le).trans
(((mergeSort_perm _).append (mergeSort_perm _)).trans
(Perm.of_eq (splitInTwo_fst_append_splitInTwo_snd _)))
termination_by l => l.length
@[simp] theorem mergeSort_length (l : List α) : (mergeSort le l).length = l.length :=
(mergeSort_perm le l).length_eq
@[simp] theorem mem_mergeSort {a : α} {l : List α} : a mergeSort le l a l :=
(mergeSort_perm le l).mem_iff
/--
The result of `mergeSort` is sorted,
as long as the comparison function is transitive (`le a b → le b c → le a c`)
and total in the sense that `le a b le b a`.
The comparison function need not be irreflexive, i.e. `le a b` and `le b a` is allowed even when `a ≠ b`.
-/
theorem mergeSort_sorted
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a) :
(l : List α) (mergeSort le l).Pairwise le
| [] => by simp [mergeSort]
| [a] => by simp [mergeSort]
| a :: b :: xs => by
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
rw [mergeSort]
apply merge_sorted @trans @total
apply mergeSort_sorted trans total
apply mergeSort_sorted trans total
termination_by l => l.length
/--
If the input list is already sorted, then `mergeSort` does not change the list.
-/
theorem mergeSort_of_sorted : {l : List α} (_ : Pairwise le l), mergeSort le l = l
| [], _ => by simp [mergeSort]
| [a], _ => by simp [mergeSort]
| a :: b :: xs, h => by
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
rw [mergeSort]
rw [mergeSort_of_sorted (splitInTwo_fst_sorted a :: b :: xs, rfl h)]
rw [mergeSort_of_sorted (splitInTwo_snd_sorted a :: b :: xs, rfl h)]
rw [merge_of_le (splitInTwo_fst_le_splitInTwo_snd h)]
rw [splitInTwo_fst_append_splitInTwo_snd]
termination_by l => l.length
/--
This merge sort algorithm is stable,
in the sense that breaking ties in the ordering function using the position in the list
has no effect on the output.
That is, elements which are equal with respect to the ordering function will remain
in the same order in the output list as they were in the input list.
See also:
* `mergeSort_stable`: if `c <+ l` and `c.Pairwise le`, then `c <+ mergeSort le l`.
* `mergeSort_stable_pair`: if `[a, b] <+ l` and `le a b`, then `[a, b] <+ mergeSort le l`)
-/
theorem mergeSort_enum {l : List α} :
(mergeSort (enumLE le) (l.enum)).map (·.2) = mergeSort le l :=
go 0 l
where go : (i : Nat) (l : List α),
(mergeSort (enumLE le) (l.enumFrom i)).map (·.2) = mergeSort le l
| _, []
| _, [a] => by simp [mergeSort]
| _, a :: b :: xs => by
have : (splitInTwo a :: b :: xs, rfl).1.1.length < xs.length + 1 + 1 := by simp [splitInTwo_fst]; omega
have : (splitInTwo a :: b :: xs, rfl).2.1.length < xs.length + 1 + 1 := by simp [splitInTwo_snd]; omega
simp only [mergeSort, enumFrom]
rw [splitInTwo_cons_cons_enumFrom_fst]
rw [splitInTwo_cons_cons_enumFrom_snd]
rw [merge_stable]
· rw [go, go]
· simp only [mem_mergeSort, Prod.forall]
intros j x k y mx my
have := mem_enumFrom mx
have := mem_enumFrom my
simp_all
omega
termination_by _ l => l.length
theorem mergeSort_cons {le : α α Bool}
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(a : α) (l : List α) :
l₁ l₂, mergeSort le (a :: l) = l₁ ++ a :: l₂ mergeSort le l = l₁ ++ l₂
b, b l₁ !le a b := by
rw [ mergeSort_enum]
rw [enum_cons]
have nd : Nodup ((a :: l).enum.map (·.1)) := by rw [enum_map_fst]; exact nodup_range _
have m₁ : (0, a) mergeSort (enumLE le) ((a :: l).enum) :=
mem_mergeSort.mpr (mem_cons_self _ _)
obtain l₁, l₂, h := append_of_mem m₁
have s := mergeSort_sorted (enumLE_trans trans) (enumLE_total total) ((a :: l).enum)
rw [h] at s
have p := mergeSort_perm (enumLE le) ((a :: l).enum)
rw [h] at p
refine l₁.map (·.2), l₂.map (·.2), ?_, ?_, ?_
· simpa using congrArg (·.map (·.2)) h
· rw [ mergeSort_enum.go 1, map_append]
congr 1
have q : mergeSort (enumLE le) (enumFrom 1 l) ~ l₁ ++ l₂ :=
(mergeSort_perm (enumLE le) (enumFrom 1 l)).trans
(p.symm.trans perm_middle).cons_inv
apply Perm.eq_of_sorted (le := enumLE le)
· rintro i, a j, b ha hb
simp only [mem_mergeSort] at ha
simp only [ q.mem_iff, mem_mergeSort] at hb
simp only [enumLE]
simp only [Bool.if_false_right, Bool.and_eq_true, Prod.mk.injEq, and_imp]
intro ab h ba h'
simp only [Bool.decide_eq_true] at ba
replace h : i j := by simpa [ab, ba] using h
replace h' : j i := by simpa [ab, ba] using h'
cases Nat.le_antisymm h h'
constructor
· rfl
· have := mem_enumFrom ha
have := mem_enumFrom hb
simp_all
· exact mergeSort_sorted (enumLE_trans trans) (enumLE_total total) ..
· exact s.sublist ((sublist_cons_self (0, a) l₂).append_left l₁)
· exact q
· intro b m
simp only [mem_map, Prod.exists, exists_eq_right] at m
obtain j, m := m
replace p := p.map (·.1)
have nd' := nd.perm p.symm
rw [map_append] at nd'
have j0 := nd'.rel_of_mem_append
(mem_map_of_mem (·.1) m) (mem_map_of_mem _ (mem_cons_self _ _))
simp only [ne_eq] at j0
have r := s.rel_of_mem_append m (mem_cons_self _ _)
simp_all [enumLE]
/--
Another statement of stability of merge sort.
If `c` is a sorted sublist of `l`,
then `c` is still a sublist of `mergeSort le l`.
-/
theorem mergeSort_stable
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a) :
{c : List α} (_ : c.Pairwise le) (_ : c <+ l),
c <+ mergeSort le l
| _, _, .slnil => nil_sublist _
| c, hc, @Sublist.cons _ _ l a h => by
obtain l₁, l₂, h₁, h₂, - := mergeSort_cons trans total a l
rw [h₁]
have h' := mergeSort_stable trans total hc h
rw [h₂] at h'
exact h'.middle a
| _, _, @Sublist.cons₂ _ l₁ l₂ a h => by
rename_i hc
obtain l₃, l₄, h₁, h₂, h₃ := mergeSort_cons trans total a l₂
rw [h₁]
have h' := mergeSort_stable trans total hc.tail h
rw [h₂] at h'
simp only [Bool.not_eq_true', tail_cons] at h₃ h'
exact
sublist_append_of_sublist_right (Sublist.cons₂ a
((fun w => Sublist.of_sublist_append_right w h') fun b m₁ m₃ =>
(Bool.eq_not_self true).mp ((rel_of_pairwise_cons hc m₁).symm.trans (h₃ b m₃))))
/--
Another statement of stability of merge sort.
If a pair `[a, b]` is a sublist of `l` and `le a b`,
then `[a, b]` is still a sublist of `mergeSort le l`.
-/
theorem mergeSort_stable_pair
(trans : (a b c : α), le a b le b c le a c)
(total : (a b : α), !le a b le b a)
(hab : le a b) (h : [a, b] <+ l) : [a, b] <+ mergeSort le l :=
mergeSort_stable trans total (pairwise_pair.mpr hab) h

View File

@@ -62,18 +62,18 @@ theorem subset_def {l₁ l₂ : List α} : l₁ ⊆ l₂ ↔ ∀ {a : α}, a ∈
theorem Subset.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ l₃) : l₁ l₃ :=
fun _ i => h₂ (h₁ i)
instance : Trans (Membership.mem : α List α Prop) Subset Membership.mem :=
fun h₁ h₂ => h h
instance : Trans (fun l₁ l₂ => Subset l₂ l₁) (Membership.mem : List α α Prop) Membership.mem :=
fun h₁ h₂ => h h
instance : Trans (Subset : List α List α Prop) Subset Subset :=
Subset.trans
@[simp] theorem subset_cons_self (a : α) (l : List α) : l a :: l := fun _ => Mem.tail _
theorem subset_cons_self (a : α) (l : List α) : l a :: l := fun _ => Mem.tail _
theorem subset_of_cons_subset {a : α} {l₁ l₂ : List α} : a :: l₁ l₂ l₁ l₂ :=
fun s _ i => s (mem_cons_of_mem _ i)
theorem subset_cons_of_subset (a : α) {l₁ l₂ : List α} : l₁ l₂ l₁ a :: l₂ :=
@[simp] theorem subset_cons_of_subset (a : α) {l₁ l₂ : List α} : l₁ l₂ l₁ a :: l₂ :=
fun s _ i => .tail _ (s i)
theorem cons_subset_cons {l₁ l₂ : List α} (a : α) (s : l₁ l₂) : a :: l₁ a :: l₂ :=
@@ -100,14 +100,14 @@ theorem filterMap_subset {l₁ l₂ : List α} (f : α → Option β) (H : l₁
rintro a, h, w
exact a, H h, w
@[simp] theorem subset_append_left (l₁ l₂ : List α) : l₁ l₁ ++ l₂ := fun _ => mem_append_left _
theorem subset_append_left (l₁ l₂ : List α) : l₁ l₁ ++ l₂ := fun _ => mem_append_left _
@[simp] theorem subset_append_right (l₁ l₂ : List α) : l₂ l₁ ++ l₂ := fun _ => mem_append_right _
theorem subset_append_right (l₁ l₂ : List α) : l₂ l₁ ++ l₂ := fun _ => mem_append_right _
theorem subset_append_of_subset_left (l₂ : List α) : l l₁ l l₁ ++ l₂ :=
@[simp] theorem subset_append_of_subset_left (l₂ : List α) : l l₁ l l₁ ++ l₂ :=
fun s => Subset.trans s <| subset_append_left _ _
theorem subset_append_of_subset_right (l₁ : List α) : l l₂ l l₁ ++ l₂ :=
@[simp] theorem subset_append_of_subset_right (l₁ : List α) : l l₂ l l₁ ++ l₂ :=
fun s => Subset.trans s <| subset_append_right _ _
@[simp] theorem append_subset {l₁ l₂ l : List α} :
@@ -155,7 +155,9 @@ theorem Sublist.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ <+ l₂) (h₂ : l
instance : Trans (@Sublist α) Sublist Sublist := Sublist.trans
@[simp] theorem sublist_cons_self (a : α) (l : List α) : l <+ a :: l := (Sublist.refl l).cons _
attribute [simp] Sublist.cons
theorem sublist_cons_self (a : α) (l : List α) : l <+ a :: l := (Sublist.refl l).cons _
theorem sublist_of_cons_sublist : a :: l₁ <+ l₂ l₁ <+ l₂ :=
(sublist_cons_self a l₁).trans
@@ -183,14 +185,20 @@ theorem Sublist.subset : l₁ <+ l₂ → l₁ ⊆ l₂
protected theorem Sublist.mem (hx : a l₁) (hl : l₁ <+ l₂) : a l₂ :=
hl.subset hx
theorem Sublist.head_mem (s : ys <+ xs) (h) : ys.head h xs :=
s.mem (List.head_mem h)
theorem Sublist.getLast_mem (s : ys <+ xs) (h) : ys.getLast h xs :=
s.mem (List.getLast_mem h)
instance : Trans (@Sublist α) Subset Subset :=
fun h₁ h₂ => trans h₁.subset h₂
instance : Trans Subset (@Sublist α) Subset :=
fun h₁ h₂ => trans h₁ h₂.subset
instance : Trans (Membership.mem : α List α Prop) Sublist Membership.mem :=
fun h₁ h₂ => h.subset h
instance : Trans (fun l₁ l₂ => Sublist l₂ l₁) (Membership.mem : List α α Prop) Membership.mem :=
fun h₁ h₂ => h.subset h
theorem mem_of_cons_sublist {a : α} {l₁ l₂ : List α} (s : a :: l₁ <+ l₂) : a l₂ :=
(cons_subset.1 s.subset).1
@@ -244,6 +252,12 @@ protected theorem Sublist.filterMap (f : α → Option β) (s : l₁ <+ l₂) :
protected theorem Sublist.filter (p : α Bool) {l₁ l₂} (s : l₁ <+ l₂) : filter p l₁ <+ filter p l₂ := by
rw [ filterMap_eq_filter]; apply s.filterMap
theorem head_filter_mem (xs : List α) (p : α Bool) (h) : (xs.filter p).head h xs :=
(filter_sublist xs).head_mem h
theorem getLast_filter_mem (xs : List α) (p : α Bool) (h) : (xs.filter p).getLast h xs :=
(filter_sublist xs).getLast_mem h
theorem sublist_filterMap_iff {l₁ : List β} {f : α Option β} :
l₁ <+ l₂.filterMap f l', l' <+ l₂ l₁ = l'.filterMap f := by
induction l₂ generalizing l₁ with
@@ -286,11 +300,11 @@ theorem sublist_filter_iff {l₁ : List α} {p : α → Bool} :
l₁ <+ l₂.filter p l', l' <+ l₂ l₁ = l'.filter p := by
simp only [ filterMap_eq_filter, sublist_filterMap_iff]
@[simp] theorem sublist_append_left : l₁ l₂ : List α, l₁ <+ l₁ ++ l₂
theorem sublist_append_left : l₁ l₂ : List α, l₁ <+ l₁ ++ l₂
| [], _ => nil_sublist _
| _ :: l₁, l₂ => (sublist_append_left l₁ l₂).cons₂ _
@[simp] theorem sublist_append_right : l₁ l₂ : List α, l₂ <+ l₁ ++ l₂
theorem sublist_append_right : l₁ l₂ : List α, l₂ <+ l₁ ++ l₂
| [], _ => Sublist.refl _
| _ :: l₁, l₂ => (sublist_append_right l₁ l₂).cons _
@@ -299,10 +313,10 @@ theorem sublist_filter_iff {l₁ : List α} {p : α → Bool} :
obtain _, _, rfl := append_of_mem h
exact ((nil_sublist _).cons₂ _).trans (sublist_append_right ..)
theorem sublist_append_of_sublist_left (s : l <+ l₁) : l <+ l₁ ++ l₂ :=
@[simp] theorem sublist_append_of_sublist_left (s : l <+ l₁) : l <+ l₁ ++ l₂ :=
s.trans <| sublist_append_left ..
theorem sublist_append_of_sublist_right (s : l <+ l₂) : l <+ l₁ ++ l₂ :=
@[simp] theorem sublist_append_of_sublist_right (s : l <+ l₂) : l <+ l₁ ++ l₂ :=
s.trans <| sublist_append_right ..
@[simp] theorem append_sublist_append_left : l, l ++ l₁ <+ l ++ l₂ l₁ <+ l₂
@@ -398,6 +412,27 @@ theorem append_sublist_iff {l₁ l₂ : List α} :
· rintro r₁, r₂, rfl, h₁, h₂
exact Sublist.append h₁ h₂
theorem Sublist.of_sublist_append_left (w : a, a l a l₂) (h : l <+ l₁ ++ l₂) : l <+ l₁ := by
rw [sublist_append_iff] at h
obtain l₁', l₂', rfl, h₁, h₂ := h
have : l₂' = [] := by
rw [eq_nil_iff_forall_not_mem]
exact fun x m => w x (mem_append_of_mem_right l₁' m) (h₂.mem m)
simp_all
theorem Sublist.of_sublist_append_right (w : a, a l a l₁) (h : l <+ l₁ ++ l₂) : l <+ l₂ := by
rw [sublist_append_iff] at h
obtain l₁', l₂', rfl, h₁, h₂ := h
have : l₁' = [] := by
rw [eq_nil_iff_forall_not_mem]
exact fun x m => w x (mem_append_of_mem_left l₂' m) (h₁.mem m)
simp_all
theorem Sublist.middle {l : List α} (h : l <+ l₁ ++ l₂) (a : α) : l <+ l₁ ++ a :: l₂ := by
rw [sublist_append_iff] at h
obtain l₁', l₂', rfl, h₁, h₂ := h
exact Sublist.append h₁ (h₂.cons a)
theorem Sublist.reverse : l₁ <+ l₂ l₁.reverse <+ l₂.reverse
| .slnil => Sublist.refl _
| .cons _ h => by rw [reverse_cons]; exact sublist_append_of_sublist_left h.reverse
@@ -467,7 +502,7 @@ theorem sublist_join_iff {L : List (List α)} {l} :
subst w
exact [], by simp, fun i x => by cases x
· rintro L', rfl, h
simp only [join_nil, sublist_nil, join_eq_nil_iff]
simp only [join_nil, sublist_nil, join_eq_nil]
simp only [getElem?_nil, Option.getD_none, sublist_nil] at h
exact (forall_getElem L' (· = [])).1 h
| cons l' L ih =>
@@ -732,7 +767,7 @@ theorem prefix_cons_iff : l₁ <+: a :: l₂ ↔ l₁ = [] ∃ t, l₁ = a :
refine s, by simp [h']
@[simp] theorem cons_prefix_cons : a :: l₁ <+: b :: l₂ a = b l₁ <+: l₂ := by
simp only [prefix_cons_iff, cons.injEq, false_or]
simp only [prefix_cons_iff, cons.injEq, false_or, List.cons_ne_nil]
constructor
· rintro t, rfl, rfl, h
exact rfl, h
@@ -763,12 +798,12 @@ theorem infix_cons_iff : l₁ <:+: a :: l₂ ↔ l₁ <+: a :: l₂ l₁ <:+
theorem prefix_concat_iff {l₁ l₂ : List α} {a : α} :
l₁ <+: l₂ ++ [a] l₁ = l₂ ++ [a] l₁ <+: l₂ := by
simp only [ concat_eq_append, reverse_suffix, reverse_concat, suffix_cons_iff]
simp only [ reverse_suffix, reverse_concat, suffix_cons_iff]
simp only [concat_eq_append, reverse_concat, reverse_eq_iff, reverse_reverse]
theorem suffix_concat_iff {l₁ l₂ : List α} {a : α} :
l₁ <:+ l₂ ++ [a] l₁ = [] t, l₁ = t ++ [a] t <:+ l₂ := by
rw [ reverse_prefix, concat_eq_append, reverse_concat, prefix_cons_iff]
rw [ reverse_prefix, reverse_concat, prefix_cons_iff]
simp only [reverse_eq_nil_iff]
apply or_congr_right
constructor
@@ -779,7 +814,7 @@ theorem suffix_concat_iff {l₁ l₂ : List α} {a : α} :
theorem infix_concat_iff {l₁ l₂ : List α} {a : α} :
l₁ <:+: l₂ ++ [a] l₁ <:+ l₂ ++ [a] l₁ <:+: l₂ := by
rw [ reverse_infix, concat_eq_append, reverse_concat, infix_cons_iff, reverse_infix,
rw [ reverse_infix, reverse_concat, infix_cons_iff, reverse_infix,
reverse_prefix, reverse_concat]
theorem isPrefix_iff : l₁ <+: l₂ i (h : i < l₁.length), l₂[i]? = some l₁[i] := by
@@ -886,7 +921,6 @@ theorem infix_of_mem_join : ∀ {L : List (List α)}, l ∈ L → l <:+: join L
theorem prefix_append_right_inj (l) : l ++ l₁ <+: l ++ l₂ l₁ <+: l₂ :=
exists_congr fun r => by rw [append_assoc, append_right_inj]
@[simp]
theorem prefix_cons_inj (a) : a :: l₁ <+: a :: l₂ l₁ <+: l₂ :=
prefix_append_right_inj [a]

View File

@@ -20,6 +20,11 @@ Further results on `List.take` and `List.drop`, which rely on stronger automatio
are given in `Init.Data.List.TakeDrop`.
-/
theorem take_cons {l : List α} (h : 0 < n) : take n (a :: l) = a :: take (n - 1) l := by
cases n with
| zero => exact absurd h (Nat.lt_irrefl _)
| succ n => rfl
@[simp]
theorem drop_one : l : List α, drop 1 l = tail l
| [] | _ :: _ => rfl
@@ -74,7 +79,7 @@ theorem drop_eq_get_cons {n} {l : List α} (h) : drop n l = get l ⟨n, h⟩ ::
simp [drop_eq_getElem_cons]
@[simp]
theorem getElem?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
theorem getElem?_take_of_lt {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
induction n generalizing l m with
| zero =>
exact absurd h (Nat.not_lt_of_le m.zero_le)
@@ -86,13 +91,11 @@ theorem getElem?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l
· simp
· simpa using hn (Nat.lt_of_succ_lt_succ h)
@[deprecated getElem?_take (since := "2024-06-12")]
@[deprecated getElem?_take_of_lt (since := "2024-06-12")]
theorem get?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n).get? m = l.get? m := by
simp [getElem?_take, h]
simp [getElem?_take_of_lt, h]
@[simp]
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? :=
getElem?_take (Nat.lt_succ_self n)
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? := by simp
@[simp] theorem drop_drop (n : Nat) : (m) (l : List α), drop n (drop m l) = drop (n + m) l
| m, [] => by simp
@@ -433,6 +436,12 @@ theorem take_takeWhile {l : List α} (p : α → Bool) n :
| nil => rfl
| cons h t ih => by_cases p h <;> simp_all
/-! ### splitAt -/
@[simp] theorem splitAt_eq (n : Nat) (l : List α) : splitAt n l = (l.take n, l.drop n) := by
rw [splitAt, splitAt_go, reverse_nil, nil_append]
split <;> simp_all [take_of_length_le, drop_of_length_le]
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_zero (l : List α) : rotateLeft l 0 = l := by

View File

@@ -78,13 +78,13 @@ theorem map_prod_left_eq_zip {l : List α} (f : α → β) :
(l.map fun x => (x, f x)) = l.zip (l.map f) := by
rw [ zip_map']
congr
exact map_id _
simp
theorem map_prod_right_eq_zip {l : List α} (f : α β) :
(l.map fun x => (f x, x)) = (l.map f).zip l := by
rw [ zip_map']
congr
exact map_id _
simp
/-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :

View File

@@ -158,7 +158,7 @@ theorem add_one (n : Nat) : n + 1 = succ n :=
rfl
@[simp] theorem add_one_ne_zero (n : Nat) : n + 1 0 := nofun
@[simp] theorem zero_ne_add_one (n : Nat) : 0 n + 1 := nofun
theorem zero_ne_add_one (n : Nat) : 0 n + 1 := by simp
protected theorem add_comm : (n m : Nat), n + m = m + n
| n, 0 => Eq.symm (Nat.zero_add n)
@@ -779,6 +779,11 @@ theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : ∀ {j}, i ≤
theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
pow_le_pow_of_le_right h (Nat.zero_le _)
@[simp] theorem zero_pow_of_pos (n : Nat) (h : 0 < n) : 0 ^ n = 0 := by
cases n with
| zero => cases h
| succ n => simp [Nat.pow_succ]
/-! # min/max -/
/--
@@ -887,7 +892,7 @@ theorem sub_succ_lt_self (a i : Nat) (h : i < a) : a - (i + 1) < a - i := by
theorem sub_ne_zero_of_lt : {a b : Nat} a < b b - a 0
| 0, 0, h => absurd h (Nat.lt_irrefl 0)
| 0, succ b, _ => by simp only [Nat.sub_zero, ne_eq, not_false_eq_true]
| 0, succ b, _ => by simp only [Nat.sub_zero, ne_eq, not_false_eq_true, Nat.succ_ne_zero]
| succ a, 0, h => absurd h (Nat.not_lt_zero a.succ)
| succ a, succ b, h => by rw [Nat.succ_sub_succ]; exact sub_ne_zero_of_lt (Nat.lt_of_succ_lt_succ h)

View File

@@ -40,7 +40,7 @@ An induction principal that works on divison by two.
-/
noncomputable def div2Induction {motive : Nat Sort u}
(n : Nat) (ind : (n : Nat), (n > 0 motive (n/2)) motive n) : motive n := by
induction n using Nat.strongInductionOn with
induction n using Nat.strongRecOn with
| ind n hyp =>
apply ind
intro n_pos
@@ -86,6 +86,12 @@ noncomputable def div2Induction {motive : Nat → Sort u}
@[simp] theorem testBit_zero (x : Nat) : testBit x 0 = decide (x % 2 = 1) := by
cases mod_two_eq_zero_or_one x with | _ p => simp [testBit, p]
theorem mod_two_eq_one_iff_testBit_zero : (x % 2 = 1) x.testBit 0 = true := by
cases mod_two_eq_zero_or_one x <;> simp_all
theorem mod_two_eq_zero_iff_testBit_zero : (x % 2 = 0) x.testBit 0 = false := by
cases mod_two_eq_zero_or_one x <;> simp_all
theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
unfold testBit
simp [shiftRight_succ_inside]
@@ -94,6 +100,9 @@ theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
unfold testBit
simp [shiftRight_succ_inside]
theorem testBit_div_two (x i : Nat) : testBit (x / 2) i = testBit x (i + 1) := by
simp
theorem testBit_to_div_mod {x : Nat} : testBit x i = decide (x / 2^i % 2 = 1) := by
induction i generalizing x with
| zero =>
@@ -114,7 +123,7 @@ theorem ne_zero_implies_bit_true {x : Nat} (xnz : x ≠ 0) : ∃ i, testBit x i
match mod_two_eq_zero_or_one x with
| Or.inl mod2_eq =>
rw [div_add_mod x 2] at xnz
simp only [mod2_eq, ne_eq, Nat.mul_eq_zero, Nat.add_zero, false_or] at xnz
simp only [mod2_eq, ne_eq, Nat.mul_eq_zero, Nat.add_zero, false_or, reduceCtorEq] at xnz
have d, dif := hyp x_pos xnz
apply Exists.intro (d+1)
simp_all
@@ -200,7 +209,7 @@ theorem lt_pow_two_of_testBit (x : Nat) (p : ∀i, i ≥ n → testBit x i = fal
have x_ge_n := Nat.ge_of_not_lt not_lt
have i, i_ge_n, test_true := ge_two_pow_implies_high_bit_true x_ge_n
have test_false := p _ i_ge_n
simp only [test_true] at test_false
simp [test_true] at test_false
private theorem succ_mod_two : succ x % 2 = 1 - x % 2 := by
induction x with
@@ -249,7 +258,7 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
@[simp] 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.strongInductionOn generalizing j i with
induction x using Nat.strongRecOn generalizing j i with
| ind x hyp =>
rw [mod_eq]
rcases Nat.lt_or_ge x (2^j) with x_lt_j | x_ge_j
@@ -315,12 +324,44 @@ theorem testBit_one_eq_true_iff_self_eq_zero {i : Nat} :
Nat.testBit 1 i = true i = 0 := by
cases i <;> simp
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
· simp [h, Nat.div_self (Nat.pow_pos Nat.zero_lt_two)]
· simp only [h]
cases Nat.lt_or_lt_of_ne h
· rw [div_eq_of_lt (Nat.pow_lt_pow_of_lt (by omega) (by omega))]
simp
· rw [Nat.pow_div _ Nat.two_pos,
Nat.sub_add_cancel (succ_le_of_lt <| Nat.sub_pos_of_lt (by omega))]
simp [Nat.pow_succ, and_one_is_mod, mul_mod_left]
omega
@[simp]
theorem testBit_two_pow_self {n : Nat} : testBit (2 ^ n) n = true := by
simp [testBit_two_pow]
@[simp]
theorem testBit_two_pow_of_ne {n m : Nat} (hm : n m) : testBit (2 ^ n) m = false := by
simp [testBit_two_pow]
omega
@[simp] theorem two_pow_sub_one_mod_two : (2 ^ n - 1) % 2 = 1 % 2 ^ n := by
cases n with
| zero => simp
| succ n =>
rw [mod_eq_of_lt (a := 1) (Nat.one_lt_two_pow (by omega)), mod_two_eq_one_iff_testBit_zero, testBit_two_pow_sub_one ]
simp only [zero_lt_succ, decide_True]
@[simp] theorem mod_two_pos_mod_two_eq_one : x % 2 ^ j % 2 = 1 (0 < j) x % 2 = 1 := by
rw [mod_two_eq_one_iff_testBit_zero, testBit_mod_two_pow]
simp
/-! ### bitwise -/
theorem testBit_bitwise
(false_false_axiom : f false false = false) (x y i : Nat)
: (bitwise f x y).testBit i = f (x.testBit i) (y.testBit i) := by
induction i using Nat.strongInductionOn generalizing x y with
theorem testBit_bitwise (false_false_axiom : f false false = false) (x y i : Nat) :
(bitwise f x y).testBit i = f (x.testBit i) (y.testBit i) := by
induction i using Nat.strongRecOn generalizing x y with
| ind i hyp =>
unfold bitwise
if x_zero : x = 0 then
@@ -417,6 +458,11 @@ theorem and_pow_two_identity {x : Nat} (lt : x < 2^n) : x &&& 2^n-1 = x := by
rw [and_pow_two_is_mod]
apply Nat.mod_eq_of_lt lt
@[simp] theorem and_mod_two_eq_one : (a &&& b) % 2 = 1 a % 2 = 1 b % 2 = 1 := by
simp only [mod_two_eq_one_iff_testBit_zero]
rw [testBit_and]
simp
/-! ### lor -/
@[simp] theorem zero_or (x : Nat) : 0 ||| x = x := by
@@ -435,6 +481,11 @@ theorem and_pow_two_identity {x : Nat} (lt : x < 2^n) : x &&& 2^n-1 = x := by
theorem or_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
@[simp] theorem or_mod_two_eq_one : (a ||| b) % 2 = 1 a % 2 = 1 b % 2 = 1 := by
simp only [mod_two_eq_one_iff_testBit_zero]
rw [testBit_or]
simp
/-! ### xor -/
@[simp] theorem testBit_xor (x y i : Nat) :
@@ -444,6 +495,19 @@ theorem or_lt_two_pow {x y n : Nat} (left : x < 2^n) (right : y < 2^n) : x ||| y
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
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]
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]
@[simp] theorem xor_mod_two_eq_one : ((a ^^^ b) % 2 = 1) ¬ ((a % 2 = 1) (b % 2 = 1)) := by
simp only [mod_two_eq_one_iff_testBit_zero]
rw [testBit_xor]
simp
/-! ### Arithmetic -/
theorem testBit_mul_pow_two_add (a : Nat) {b i : Nat} (b_lt : b < 2^i) (j : Nat) :
@@ -505,6 +569,15 @@ theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^
@[simp] 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
@[simp] theorem decide_shiftRight_mod_two_eq_one :
decide (x >>> i % 2 = 1) = x.testBit i := by
simp only [testBit, one_and_eq_mod_two, mod_two_bne_zero]
exact (Bool.beq_eq_decide_eq _ _).symm
/-! ### le -/
theorem le_of_testBit {n m : Nat} (h : i, n.testBit i = true m.testBit i = true) : n m := by

View File

@@ -48,7 +48,7 @@ def div.inductionOn.{u}
decreasing_by apply div_rec_lemma; assumption
theorem div_le_self (n k : Nat) : n / k n := by
induction n using Nat.strongInductionOn with
induction n using Nat.strongRecOn with
| ind n ih =>
rw [div_eq]
-- Note: manual split to avoid Classical.em which is not yet defined
@@ -221,7 +221,7 @@ theorem le_div_iff_mul_le (k0 : 0 < k) : x ≤ y / k ↔ x * k ≤ y := by
induction y, k using mod.inductionOn generalizing x with
(rw [div_eq]; simp [h]; cases x with | zero => simp [zero_le] | succ x => ?_)
| base y k h =>
simp only [add_one, succ_mul, false_iff, Nat.not_le]
simp only [add_one, succ_mul, false_iff, Nat.not_le, Nat.succ_ne_zero]
refine Nat.lt_of_lt_of_le ?_ (Nat.le_add_left ..)
exact Nat.not_le.1 fun h' => h k0, h'
| ind y k h IH =>
@@ -334,7 +334,7 @@ theorem mul_mod_mul_left (z x y : Nat) : (z * x) % (z * y) = z * (x % y) :=
else if z0 : z = 0 then by
rw [z0, Nat.zero_mul, Nat.zero_mul, Nat.zero_mul, mod_zero]
else by
induction x using Nat.strongInductionOn with
induction x using Nat.strongRecOn with
| _ n IH =>
have y0 : y > 0 := Nat.pos_of_ne_zero y0
have z0 : z > 0 := Nat.pos_of_ne_zero z0

View File

@@ -75,7 +75,7 @@ theorem gcd_rec (m n : Nat) : gcd m n = gcd (n % m) m :=
@[elab_as_elim] theorem gcd.induction {P : Nat Nat Prop} (m n : Nat)
(H0 : n, P 0 n) (H1 : m n, 0 < m P (n % m) m P m n) : P m n :=
Nat.strongInductionOn (motive := fun m => n, P m n) m
Nat.strongRecOn (motive := fun m => n, P m n) m
(fun
| 0, _ => H0
| _+1, IH => fun _ => H1 _ _ (succ_pos _) (IH _ (mod_lt _ (succ_pos _)) _) )

View File

@@ -27,6 +27,11 @@ namespace Nat
fun n, h, w => by cases n with | zero => simp at h | succ n => exact n, w,
fun n, w => n + 1, by simp, w
@[simp] theorem exists_eq_add_one : ( n, a = n + 1) 0 < a :=
fun n, h => by omega, fun h => a - 1, by omega
@[simp] theorem exists_add_one_eq : ( n, n + 1 = a) 0 < a :=
fun n, h => by omega, fun h => a - 1, by omega
/-! ## add -/
protected theorem add_add_add_comm (a b c d : Nat) : (a + b) + (c + d) = (a + c) + (b + d) := by
@@ -50,6 +55,11 @@ protected theorem add_eq_zero_iff : n + m = 0 ↔ n = 0 ∧ m = 0 :=
@[simp] protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n m = k :=
Nat.add_right_cancel, fun | rfl => rfl
@[simp] protected theorem add_left_eq_self {a b : Nat} : a + b = b a = 0 := by omega
@[simp] protected theorem add_right_eq_self {a b : Nat} : a + b = a b = 0 := by omega
@[simp] protected theorem self_eq_add_right {a b : Nat} : a = a + b b = 0 := by omega
@[simp] protected theorem self_eq_add_left {a b : Nat} : a = b + a b = 0 := by omega
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m n + k m k :=
Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _
@@ -147,17 +157,9 @@ protected theorem sub_le_iff_le_add' {a b c : Nat} : a - b ≤ c ↔ a ≤ b + c
protected theorem le_sub_iff_add_le {n : Nat} (h : k m) : n m - k n + k m :=
Nat.add_le_of_le_sub h, Nat.le_sub_of_add_le
@[deprecated Nat.le_sub_iff_add_le (since := "2024-02-19")]
protected theorem add_le_to_le_sub (n : Nat) (h : m k) : n + m k n k - m :=
(Nat.le_sub_iff_add_le h).symm
protected theorem add_le_of_le_sub' {n k m : Nat} (h : m k) : n k - m m + n k :=
Nat.add_comm .. Nat.add_le_of_le_sub h
@[deprecated Nat.add_le_of_le_sub' (since := "2024-02-19")]
protected theorem add_le_of_le_sub_left {n k m : Nat} (h : m k) : n k - m m + n k :=
Nat.add_le_of_le_sub' h
protected theorem le_sub_of_add_le' {n k m : Nat} : m + n k n k - m :=
Nat.add_comm .. Nat.le_sub_of_add_le
@@ -419,14 +421,6 @@ protected theorem mul_min_mul_left (a b c : Nat) : min (a * b) (a * c) = a * min
/-! ### mul -/
@[deprecated Nat.mul_le_mul_left (since := "2024-02-19")]
protected theorem mul_le_mul_of_nonneg_left {a b c : Nat} : a b c * a c * b :=
Nat.mul_le_mul_left c
@[deprecated Nat.mul_le_mul_right (since := "2024-02-19")]
protected theorem mul_le_mul_of_nonneg_right {a b c : Nat} : a b a * c b * c :=
Nat.mul_le_mul_right c
protected theorem mul_right_comm (n m k : Nat) : n * m * k = n * k * m := by
rw [Nat.mul_assoc, Nat.mul_comm m, Nat.mul_assoc]
@@ -539,6 +533,11 @@ theorem mod_two_eq_zero_or_one (n : Nat) : n % 2 = 0 n % 2 = 1 :=
| 0, _ => .inl rfl
| 1, _ => .inr rfl
@[simp] theorem mod_two_bne_zero : ((a % 2) != 0) = (a % 2 == 1) := by
cases mod_two_eq_zero_or_one a <;> simp_all
@[simp] theorem mod_two_bne_one : ((a % 2) != 1) = (a % 2 == 0) := by
cases mod_two_eq_zero_or_one a <;> simp_all
theorem le_of_mod_lt {a b : Nat} (h : a % b < a) : b a :=
Nat.not_lt.1 fun hf => (ne_of_lt h).elim (Nat.mod_eq_of_lt hf)
@@ -649,6 +648,16 @@ protected theorem one_le_two_pow : 1 ≤ 2 ^ n :=
else
Nat.le_of_lt (Nat.one_lt_two_pow h)
@[simp] theorem one_mod_two_pow_eq_one : 1 % 2 ^ n = 1 0 < n := by
cases n with
| zero => simp
| succ n =>
rw [mod_eq_of_lt (a := 1) (Nat.one_lt_two_pow (by omega))]
simp
@[simp] theorem one_mod_two_pow (h : 0 < n) : 1 % 2 ^ n = 1 :=
one_mod_two_pow_eq_one.mpr h
protected theorem pow_pos (h : 0 < a) : 0 < a^n :=
match n with
| 0 => Nat.zero_lt_one
@@ -700,6 +709,36 @@ protected theorem pow_lt_pow_iff_right {a n m : Nat} (h : 1 < a) :
· intro w
exact Nat.pow_lt_pow_of_lt h w
@[simp]
protected theorem pow_pred_mul {x w : Nat} (h : 0 < w) :
x ^ (w - 1) * x = x ^ w := by
simp [ Nat.pow_succ, succ_eq_add_one, Nat.sub_add_cancel h]
protected theorem pow_pred_lt_pow {x w : Nat} (h₁ : 1 < x) (h₂ : 0 < w) :
x ^ (w - 1) < x ^ w :=
Nat.pow_lt_pow_of_lt h₁ (by omega)
protected theorem two_pow_pred_lt_two_pow {w : Nat} (h : 0 < w) :
2 ^ (w - 1) < 2 ^ w :=
Nat.pow_pred_lt_pow (by omega) h
@[simp]
protected theorem two_pow_pred_add_two_pow_pred (h : 0 < w) :
2 ^ (w - 1) + 2 ^ (w - 1) = 2 ^ w := by
rw [ Nat.pow_pred_mul h]
omega
@[simp]
protected theorem two_pow_sub_two_pow_pred (h : 0 < w) :
2 ^ w - 2 ^ (w - 1) = 2 ^ (w - 1) := by
simp [ Nat.two_pow_pred_add_two_pow_pred h]
@[simp]
protected theorem two_pow_pred_mod_two_pow (h : 0 < w) :
2 ^ (w - 1) % 2 ^ w = 2 ^ (w - 1) := by
rw [mod_eq_of_lt]
apply Nat.pow_pred_lt_pow (by omega) h
/-! ### log2 -/
@[simp]

View File

@@ -73,4 +73,10 @@ theorem mod_pow_succ {x b k : Nat} :
x % b ^ (k + 1) = x % b ^ k + b ^ k * ((x / b ^ k) % b) := by
rw [Nat.pow_succ, Nat.mod_mul]
@[simp] theorem two_pow_mod_two_eq_zero (n : Nat) : 2 ^ n % 2 = 0 0 < n := by
cases n <;> simp [Nat.pow_succ]
@[simp] theorem two_pow_mod_two_eq_one (n : Nat) : 2 ^ n % 2 = 1 n = 0 := by
cases n <;> simp [Nat.pow_succ]
end Nat

View File

@@ -19,7 +19,7 @@ theorem eq_of_eq_some {α : Type u} : ∀ {x y : Option α}, (∀z, x = some z
theorem eq_none_of_isNone {α : Type u} : {o : Option α}, o.isNone o = none
| none, _ => rfl
instance : Membership α (Option α) := fun a b => b = some a
instance : Membership α (Option α) := fun b a => b = some a
@[simp] theorem mem_def {a : α} {b : Option α} : a b b = some a := .rfl

View File

@@ -87,6 +87,9 @@ theorem eq_some_iff_get_eq : o = some a ↔ ∃ h : o.isSome, o.get h = a := by
theorem eq_some_of_isSome : {o : Option α} (h : o.isSome), o = some (o.get h)
| some _, _ => rfl
theorem isSome_iff_ne_none : o.isSome o none := by
cases o <;> simp
theorem not_isSome_iff_eq_none : ¬o.isSome o = none := by
cases o <;> simp
@@ -159,7 +162,7 @@ theorem map_some : f <$> some a = some (f a) := rfl
theorem map_eq_some : f <$> x = some b a, x = some a f a = b := map_eq_some'
@[simp] theorem map_eq_none' : x.map f = none x = none := by
cases x <;> simp only [map_none', map_some', eq_self_iff_true]
cases x <;> simp [map_none', map_some', eq_self_iff_true]
theorem isSome_map {x : Option α} : (f <$> x).isSome = x.isSome := by
cases x <;> simp
@@ -178,8 +181,19 @@ theorem map_eq_bind {x : Option α} : x.map f = x.bind (some ∘ f) := by
theorem map_congr {x : Option α} (h : a, a x f a = g a) : x.map f = x.map g := by
cases x <;> simp only [map_none', map_some', h, mem_def]
@[simp] theorem map_id' : Option.map (@id α) = id := map_id
@[simp] theorem map_id'' {x : Option α} : (x.map fun a => a) = x := congrFun map_id x
@[simp] theorem map_id_fun {α : Type u} : Option.map (id : α α) = id := by
funext; simp [map_id]
theorem map_id' {x : Option α} : (x.map fun a => a) = x := congrFun map_id x
@[simp] theorem map_id_fun' {α : Type u} : Option.map (fun (a : α) => a) = id := by
funext; simp [map_id']
theorem get_map {f : α β} {o : Option α} {h : (o.map f).isSome} :
(o.map f).get h = f (o.get (by simpa using h)) := by
cases o with
| none => simp at h
| some a => simp
@[simp] theorem map_map (h : β γ) (g : α β) (x : Option α) :
(x.map g).map h = x.map (h g) := by
@@ -238,6 +252,15 @@ theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) :
@[simp] theorem guard_eq_some [DecidablePred p] : guard p a = some b a = b p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[simp] theorem guard_isSome [DecidablePred p] : (Option.guard p a).isSome p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[simp] theorem guard_eq_none [DecidablePred p] : Option.guard p a = none ¬ p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[simp] theorem guard_pos [DecidablePred p] (h : p a) : Option.guard p a = some a := by
simp [Option.guard, h]
theorem liftOrGet_eq_or_eq {f : α α α} (h : a b, f a b = a f a b = b) :
o₁ o₂, liftOrGet f o₁ o₂ = o₁ liftOrGet f o₁ o₂ = o₂
| none, none => .inl rfl
@@ -261,7 +284,7 @@ theorem liftOrGet_eq_or_eq {f : ααα} (h : ∀ a b, f a b = a f
@[simp] theorem getD_map (f : α β) (x : α) (o : Option α) :
(o.map f).getD (f x) = f (getD o x) := by cases o <;> rfl
section
section choice
attribute [local instance] Classical.propDecidable
@@ -277,7 +300,7 @@ theorem choice_eq {α : Type _} [Subsingleton α] (a : α) : choice α = some a
theorem choice_isSome_iff_nonempty {α : Type _} : (choice α).isSome Nonempty α :=
fun h => (choice α).get h, fun h => by simp only [choice, dif_pos h, isSome_some]
end
end choice
@[simp] theorem toList_some (a : α) : (a : Option α).toList = [a] := rfl
@@ -298,7 +321,7 @@ theorem or_eq_bif : or o o' = bif o.isSome then o else o' := by
@[simp] theorem or_eq_none : or o o' = none o = none o' = none := by
cases o <;> simp
theorem or_eq_some : or o o' = some a o = some a (o = none o' = some a) := by
@[simp] theorem or_eq_some : or o o' = some a o = some a (o = none o' = some a) := by
cases o <;> simp
theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
@@ -333,3 +356,43 @@ theorem or_of_isSome {o o' : Option α} (h : o.isSome) : o.or o' = o := by
theorem or_of_isNone {o o' : Option α} (h : o.isNone) : o.or o' = o' := by
match o, h with
| none, _ => simp
section ite
@[simp] theorem isSome_dite {p : Prop} [Decidable p] {b : p β} :
(if h : p then some (b h) else none).isSome = true p := by
split <;> simpa
@[simp] theorem isSome_ite {p : Prop} [Decidable p] :
(if p then some b else none).isSome = true p := by
split <;> simpa
@[simp] theorem isSome_dite' {p : Prop} [Decidable p] {b : ¬ p β} :
(if h : p then none else some (b h)).isSome = true ¬ p := by
split <;> simpa
@[simp] theorem isSome_ite' {p : Prop} [Decidable p] :
(if p then none else some b).isSome = true ¬ p := by
split <;> simpa
@[simp] theorem get_dite {p : Prop} [Decidable p] (b : p β) (w) :
(if h : p then some (b h) else none).get w = b (by simpa using w) := by
split
· simp
· exfalso
simp at w
contradiction
@[simp] theorem get_ite {p : Prop} [Decidable p] (h) :
(if p then some b else none).get h = b := by
simpa using get_dite (p := p) (fun _ => b) (by simpa using h)
@[simp] theorem get_dite' {p : Prop} [Decidable p] (b : ¬ p β) (w) :
(if h : p then none else some (b h)).get w = b (by simpa using w) := by
split
· exfalso
simp at w
contradiction
· simp
@[simp] theorem get_ite' {p : Prop} [Decidable p] (h) :
(if p then none else some b).get h = b := by
simpa using get_dite' (p := p) (fun _ => b) (by simpa using h)
end ite
end Option

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

@@ -0,0 +1,12 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Core
deriving instance DecidableEq for PLift
instance [Subsingleton α] : Subsingleton (PLift α) where
allEq := fun a b => congrArg PLift.up (Subsingleton.elim a b)

View File

@@ -15,7 +15,7 @@ structure Range where
step : Nat := 1
instance : Membership Nat Range where
mem i r := r.start i i < r.stop
mem r i := r.start i i < r.stop
namespace Range
universe u v

View File

@@ -227,7 +227,7 @@ Examples:
* `"abc".front = 'a'`
* `"".front = (default : Char)`
-/
def front (s : String) : Char :=
@[inline] def front (s : String) : Char :=
get s 0
/--
@@ -237,7 +237,7 @@ Examples:
* `"abc".back = 'c'`
* `"".back = (default : Char)`
-/
def back (s : String) : Char :=
@[inline] def back (s : String) : Char :=
get s (prev s s.endPos)
/--
@@ -374,7 +374,7 @@ Examples:
* `"abba".posOf 'z' = none`
* `"L∃∀N".posOf '∀' = some ⟨4⟩`
-/
def revPosOf (s : String) (c : Char) : Option Pos :=
@[inline] def revPosOf (s : String) (c : Char) : Option Pos :=
revPosOfAux s c s.endPos
def findAux (s : String) (p : Char Bool) (stopPos : Pos) (pos : Pos) : Pos :=
@@ -398,7 +398,7 @@ def revFindAux (s : String) (p : Char → Bool) (pos : Pos) : Option Pos :=
else revFindAux s p pos
termination_by pos.1
def revFind (s : String) (p : Char Bool) : Option Pos :=
@[inline] def revFind (s : String) (p : Char Bool) : Option Pos :=
revFindAux s p s.endPos
abbrev Pos.min (p₁ p₂ : Pos) : Pos :=
@@ -505,7 +505,7 @@ The default separator is `" "`. The separators are not included in the returned
"ababacabac".splitOn "aba" = ["", "bac", "c"]
```
-/
def splitOn (s : String) (sep : String := " ") : List String :=
@[inline] def splitOn (s : String) (sep : String := " ") : List String :=
if sep == "" then [s] else splitOnAux s sep 0 0 0 []
instance : Inhabited String := ""
@@ -515,16 +515,16 @@ instance : Append String := ⟨String.append⟩
@[deprecated push (since := "2024-04-06")]
def str : String Char String := push
def pushn (s : String) (c : Char) (n : Nat) : String :=
@[inline] def pushn (s : String) (c : Char) (n : Nat) : String :=
n.repeat (fun s => s.push c) s
def isEmpty (s : String) : Bool :=
@[inline] def isEmpty (s : String) : Bool :=
s.endPos == 0
def join (l : List String) : String :=
@[inline] def join (l : List String) : String :=
l.foldl (fun r s => r ++ s) ""
def singleton (c : Char) : String :=
@[inline] def singleton (c : Char) : String :=
"".push c
def intercalate (s : String) : List String String
@@ -558,10 +558,10 @@ structure Iterator where
`Iterator.next` when `Iterator.atEnd` is true. If the position is not valid, then the
current character is `(default : Char)`, similar to `String.get` on an invalid position. -/
i : Pos
deriving DecidableEq
deriving DecidableEq, Inhabited
/-- Creates an iterator at the beginning of a string. -/
def mkIterator (s : String) : Iterator :=
@[inline] def mkIterator (s : String) : Iterator :=
s, 0
@[inherit_doc mkIterator]
@@ -575,66 +575,74 @@ theorem Iterator.sizeOf_eq (i : String.Iterator) : sizeOf i = i.1.utf8ByteSize -
rfl
namespace Iterator
@[inherit_doc Iterator.s]
@[inline, inherit_doc Iterator.s]
def toString := Iterator.s
/-- Number of bytes remaining in the iterator. -/
def remainingBytes : Iterator Nat
@[inline] def remainingBytes : Iterator Nat
| s, i => s.endPos.byteIdx - i.byteIdx
@[inherit_doc Iterator.i]
@[inline, inherit_doc Iterator.i]
def pos := Iterator.i
/-- The character at the current position.
On an invalid position, returns `(default : Char)`. -/
def curr : Iterator Char
@[inline] def curr : Iterator Char
| s, i => get s i
/-- Moves the iterator's position forward by one character, unconditionally.
It is only valid to call this function if the iterator is not at the end of the string, *i.e.*
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
def next : Iterator Iterator
@[inline] def next : Iterator Iterator
| s, i => s, s.next i
/-- Decreases the iterator's position.
If the position is zero, this function is the identity. -/
def prev : Iterator Iterator
@[inline] def prev : Iterator Iterator
| s, i => s, s.prev i
/-- True if the iterator is past the string's last character. -/
def atEnd : Iterator Bool
@[inline] def atEnd : Iterator Bool
| s, i => i.byteIdx s.endPos.byteIdx
/-- True if the iterator is not past the string's last character. -/
def hasNext : Iterator Bool
@[inline] def hasNext : Iterator Bool
| s, i => i.byteIdx < s.endPos.byteIdx
/-- True if the position is not zero. -/
def hasPrev : Iterator Bool
@[inline] def hasPrev : Iterator Bool
| _, i => i.byteIdx > 0
@[inline] def curr' (it : Iterator) (h : it.hasNext) : Char :=
match it with
| s, i => get' s i (by simpa only [hasNext, endPos, decide_eq_true_eq, String.atEnd, ge_iff_le, Nat.not_le] using h)
@[inline] def next' (it : Iterator) (h : it.hasNext) : Iterator :=
match it with
| s, i => s, s.next' i (by simpa only [hasNext, endPos, decide_eq_true_eq, String.atEnd, ge_iff_le, Nat.not_le] using h)
/-- Replaces the current character in the string.
Does nothing if the iterator is at the end of the string. If the iterator contains the only
reference to its string, this function will mutate the string in-place instead of allocating a new
one. -/
def setCurr : Iterator Char Iterator
@[inline] def setCurr : Iterator Char Iterator
| s, i, c => s.set i c, i
/-- Moves the iterator's position to the end of the string.
Note that `i.toEnd.atEnd` is always `true`. -/
def toEnd : Iterator Iterator
@[inline] def toEnd : Iterator Iterator
| s, _ => s, s.endPos
/-- Extracts the substring between the positions of two iterators.
Returns the empty string if the iterators are for different strings, or if the position of the first
iterator is past the position of the second iterator. -/
def extract : Iterator Iterator String
@[inline] def extract : Iterator Iterator String
| s₁, b, s₂, e =>
if s₁ s₂ || b > e then ""
else s₁.extract b e
@@ -648,7 +656,7 @@ def forward : Iterator → Nat → Iterator
| it, n+1 => forward it.next n
/-- The remaining characters in an iterator, as a string. -/
def remainingToString : Iterator String
@[inline] def remainingToString : Iterator String
| s, i => s.extract i s.endPos
@[inherit_doc forward]
@@ -673,7 +681,7 @@ def offsetOfPosAux (s : String) (pos : Pos) (i : Pos) (offset : Nat) : Nat :=
offsetOfPosAux s pos (s.next i) (offset+1)
termination_by s.endPos.1 - i.1
def offsetOfPos (s : String) (pos : Pos) : Nat :=
@[inline] def offsetOfPos (s : String) (pos : Pos) : Nat :=
offsetOfPosAux s pos 0 0
@[specialize] def foldlAux {α : Type u} (f : α Char α) (s : String) (stopPos : Pos) (i : Pos) (a : α) : α :=
@@ -714,7 +722,7 @@ termination_by stopPos.1 - i.1
@[inline] def all (s : String) (p : Char Bool) : Bool :=
!s.any (fun c => !p c)
def contains (s : String) (c : Char) : Bool :=
@[inline] def contains (s : String) (c : Char) : Bool :=
s.any (fun a => a == c)
theorem utf8SetAux_of_gt (c' : Char) : (cs : List Char) {i p : Pos}, i > p utf8SetAux c' cs i p = cs
@@ -770,7 +778,7 @@ termination_by s.endPos.1 - i.1
@[inline] def map (f : Char Char) (s : String) : String :=
mapAux f 0 s
def isNat (s : String) : Bool :=
@[inline] def isNat (s : String) : Bool :=
!s.isEmpty && s.all (·.isDigit)
def toNat? (s : String) : Option Nat :=
@@ -940,7 +948,7 @@ def splitOn (s : Substring) (sep : String := " ") : List Substring :=
@[inline] def all (s : Substring) (p : Char Bool) : Bool :=
!s.any (fun c => !p c)
def contains (s : Substring) (c : Char) : Bool :=
@[inline] def contains (s : Substring) (c : Char) : Bool :=
s.any (fun a => a == c)
@[specialize] def takeWhileAux (s : String) (stopPos : String.Pos) (p : Char Bool) (i : String.Pos) : String.Pos :=
@@ -995,7 +1003,7 @@ termination_by i.1
let e := takeRightWhileAux s b Char.isWhitespace e
s, b, e
def isNat (s : Substring) : Bool :=
@[inline] def isNat (s : Substring) : Bool :=
s.all fun c => c.isDigit
def toNat? (s : Substring) : Option Nat :=
@@ -1017,43 +1025,43 @@ end Substring
namespace String
def drop (s : String) (n : Nat) : String :=
@[inline] def drop (s : String) (n : Nat) : String :=
(s.toSubstring.drop n).toString
def dropRight (s : String) (n : Nat) : String :=
@[inline] def dropRight (s : String) (n : Nat) : String :=
(s.toSubstring.dropRight n).toString
def take (s : String) (n : Nat) : String :=
@[inline] def take (s : String) (n : Nat) : String :=
(s.toSubstring.take n).toString
def takeRight (s : String) (n : Nat) : String :=
@[inline] def takeRight (s : String) (n : Nat) : String :=
(s.toSubstring.takeRight n).toString
def takeWhile (s : String) (p : Char Bool) : String :=
@[inline] def takeWhile (s : String) (p : Char Bool) : String :=
(s.toSubstring.takeWhile p).toString
def dropWhile (s : String) (p : Char Bool) : String :=
@[inline] def dropWhile (s : String) (p : Char Bool) : String :=
(s.toSubstring.dropWhile p).toString
def takeRightWhile (s : String) (p : Char Bool) : String :=
@[inline] def takeRightWhile (s : String) (p : Char Bool) : String :=
(s.toSubstring.takeRightWhile p).toString
def dropRightWhile (s : String) (p : Char Bool) : String :=
@[inline] def dropRightWhile (s : String) (p : Char Bool) : String :=
(s.toSubstring.dropRightWhile p).toString
def startsWith (s pre : String) : Bool :=
@[inline] def startsWith (s pre : String) : Bool :=
s.toSubstring.take pre.length == pre.toSubstring
def endsWith (s post : String) : Bool :=
@[inline] def endsWith (s post : String) : Bool :=
s.toSubstring.takeRight post.length == post.toSubstring
def trimRight (s : String) : String :=
@[inline] def trimRight (s : String) : String :=
s.toSubstring.trimRight.toString
def trimLeft (s : String) : String :=
@[inline] def trimLeft (s : String) : String :=
s.toSubstring.trimLeft.toString
def trim (s : String) : String :=
@[inline] def trim (s : String) : String :=
s.toSubstring.trim.toString
@[inline] def nextWhile (s : String) (p : Char Bool) (i : String.Pos) : String.Pos :=
@@ -1062,23 +1070,23 @@ def trim (s : String) : String :=
@[inline] def nextUntil (s : String) (p : Char Bool) (i : String.Pos) : String.Pos :=
nextWhile s (fun c => !p c) i
def toUpper (s : String) : String :=
@[inline] def toUpper (s : String) : String :=
s.map Char.toUpper
def toLower (s : String) : String :=
@[inline] def toLower (s : String) : String :=
s.map Char.toLower
def capitalize (s : String) :=
@[inline] def capitalize (s : String) :=
s.set 0 <| s.get 0 |>.toUpper
def decapitalize (s : String) :=
@[inline] def decapitalize (s : String) :=
s.set 0 <| s.get 0 |>.toLower
end String
namespace Char
protected def toString (c : Char) : String :=
@[inline] protected def toString (c : Char) : String :=
String.singleton c
@[simp] theorem length_toString (c : Char) : c.toString.length = 1 := rfl

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

@@ -0,0 +1,12 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Core
deriving instance DecidableEq for ULift
instance [Subsingleton α] : Subsingleton (ULift α) where
allEq := fun a b => congrArg ULift.up (Subsingleton.elim a b)

View File

@@ -184,7 +184,7 @@ instance [GetElem? cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
@[simp] 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| apply Fin.val_lt_of_le; get_elem_tactic_trivial; done)
| `(tactic| get_elem_tactic_trivial) => `(tactic| (with_reducible apply Fin.val_lt_of_le); get_elem_tactic_trivial; done)
end Fin

View File

@@ -75,7 +75,7 @@ See #2572.
opaque Internal.hasLLVMBackend (u : Unit) : Bool
/-- Valid identifier names -/
def isGreek (c : Char) : Bool :=
@[inline] def isGreek (c : Char) : Bool :=
0x391 c.val && c.val 0x3dd
def isLetterLike (c : Char) : Bool :=
@@ -86,7 +86,7 @@ def isLetterLike (c : Char) : Bool :=
(0x2100 c.val && c.val 0x214f) || -- Letter like block
(0x1d49c c.val && c.val 0x1d59f) -- Latin letters, Script, Double-struck, Fractur
def isNumericSubscript (c : Char) : Bool :=
@[inline] def isNumericSubscript (c : Char) : Bool :=
0x2080 c.val && c.val 0x2089
def isSubScriptAlnum (c : Char) : Bool :=
@@ -94,16 +94,16 @@ def isSubScriptAlnum (c : Char) : Bool :=
(0x2090 c.val && c.val 0x209c) ||
(0x1d62 c.val && c.val 0x1d6a)
def isIdFirst (c : Char) : Bool :=
@[inline] def isIdFirst (c : Char) : Bool :=
c.isAlpha || c = '_' || isLetterLike c
def isIdRest (c : Char) : Bool :=
@[inline] def isIdRest (c : Char) : Bool :=
c.isAlphanum || c = '_' || c = '\'' || c == '!' || c == '?' || isLetterLike c || isSubScriptAlnum c
def idBeginEscape := '«'
def idEndEscape := '»'
def isIdBeginEscape (c : Char) : Bool := c = idBeginEscape
def isIdEndEscape (c : Char) : Bool := c = idEndEscape
@[inline] def isIdBeginEscape (c : Char) : Bool := c = idBeginEscape
@[inline] def isIdEndEscape (c : Char) : Bool := c = idEndEscape
namespace Name
def getRoot : Name Name
@@ -388,9 +388,9 @@ def getSubstring? (stx : Syntax) (withLeading := true) (withTrailing := true) :
partial def setTailInfoAux (info : SourceInfo) : Syntax Option Syntax
| atom _ val => some <| atom info val
| ident _ rawVal val pre => some <| ident info rawVal val pre
| node info k args =>
| node info' k args =>
match updateLast args (setTailInfoAux info) args.size with
| some args => some <| node info k args
| some args => some <| node info' k args
| none => none
| _ => none

View File

@@ -104,6 +104,11 @@ structure Config where
That is, given a local context containing entry `x : t := e`, the free variable `x` reduces to `e`.
-/
zetaDelta : Bool := false
/--
When `index` (default : `true`) is `false`, `simp` will only use the root symbol
to find candidate `simp` theorems. It approximates Lean 3 `simp` behavior.
-/
index : Bool := true
deriving Inhabited, BEq
end DSimp

View File

@@ -336,7 +336,7 @@ macro_rules | `($x == $y) => `(binrel_no_prop% BEq.beq $x $y)
@[inherit_doc] infixl:30 " || " => or
@[inherit_doc] notation:max "!" b:40 => not b
@[inherit_doc] infix:50 "" => Membership.mem
@[inherit_doc] notation:50 a:50 "" b:50 => Membership.mem b a
/-- `a ∉ b` is negated elementhood. It is notation for `¬ (a ∈ b)`. -/
notation:50 a:50 "" b:50 => ¬ (a b)

View File

@@ -300,6 +300,8 @@ theorem normalize_sat {s x v} (w : s.sat' x v) :
· split
· simp
· dsimp [Constraint.sat'] at w
simp only [IntList.gcd_eq_zero] at h
simp only [IntList.dot_eq_zero_of_left_eq_zero h] at w
simp_all
· split
· exact w

View File

@@ -116,7 +116,7 @@ theorem ofNat_max (a b : Nat) : ((max a b : Nat) : Int) = max (a : Int) (b : Int
split <;> rfl
theorem ofNat_natAbs (a : Int) : (a.natAbs : Int) = if 0 a then a else -a := by
rw [Int.natAbs]
rw [Int.natAbs.eq_def]
split <;> rename_i n
· simp only [Int.ofNat_eq_coe]
rw [if_pos (Int.ofNat_nonneg n)]

View File

@@ -352,7 +352,6 @@ attribute [simp] Int.zero_dvd
theorem gcd_dvd_dot_left (xs ys : IntList) : (xs.gcd : Int) dot xs ys :=
Int.dvd_of_emod_eq_zero (dot_mod_gcd_left xs ys)
@[simp]
theorem dot_eq_zero_of_left_eq_zero {xs ys : IntList} (h : x, x xs x = 0) : dot xs ys = 0 := by
induction xs generalizing ys with
| nil => rfl
@@ -363,6 +362,8 @@ theorem dot_eq_zero_of_left_eq_zero {xs ys : IntList} (h : ∀ x, x ∈ xs → x
rw [dot_cons₂, h x (List.mem_cons_self _ _), ih (fun x m => h x (List.mem_cons_of_mem _ m)),
Int.zero_mul, Int.add_zero]
@[simp] theorem nil_dot (xs : IntList) : dot [] xs = 0 := rfl
theorem dot_sdiv_left (xs ys : IntList) {d : Int} (h : d xs.gcd) :
dot (xs.sdiv d) ys = (dot xs ys) / d := by
induction xs generalizing ys with

View File

@@ -1515,7 +1515,7 @@ of the elements of the container.
-/
class Membership (α : outParam (Type u)) (γ : Type v) where
/-- The membership relation `a ∈ s : Prop` where `a : α`, `s : γ`. -/
mem : α γ Prop
mem : γ α Prop
set_option bootstrap.genMatcherCode false in
/--

View File

@@ -169,12 +169,23 @@ theorem if_true_right [h : Decidable p] :
@[simp] theorem ite_not (p : Prop) [Decidable p] (x y : α) : ite (¬p) x y = ite p y x :=
dite_not (fun _ => x) (fun _ => y)
@[simp] theorem ite_true_same (p q : Prop) [h : Decidable p] : (if p then p else q) = (¬p q) := by
@[simp] theorem ite_then_self (p q : Prop) [h : Decidable p] : (if p then p else q) = (¬p q) := by
cases h <;> (rename_i g; simp [g])
@[simp] theorem ite_false_same (p q : Prop) [h : Decidable p] : (if p then q else p) = (p q) := by
@[simp] theorem ite_else_self (p q : Prop) [h : Decidable p] : (if p then q else p) = (p q) := by
cases h <;> (rename_i g; simp [g])
@[simp] theorem ite_then_not_self (p : Prop) [Decidable p] (q : Prop) : (if p then ¬p else q) ¬p q := by
split <;> simp_all
@[simp] theorem ite_else_not_self (p : Prop) [Decidable p] (q : Prop) : (if p then q else ¬p) p q := by
split <;> simp_all
@[deprecated ite_then_self (since := "2024-08-28")]
theorem ite_true_same (p q : Prop) [Decidable p] : (if p then p else q) = (¬p q) := ite_then_self p q
@[deprecated ite_else_self (since := "2024-08-28")]
theorem ite_false_same (p q : Prop) [Decidable p] : (if p then q else p) = (p q) := ite_else_self p q
/-! ## exists and forall -/
section quantifiers
@@ -326,6 +337,9 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
@[simp] theorem exists_or_eq_left' (y : α) (p : α Prop) : x : α, y = x p x := y, .inl rfl
@[simp] theorem exists_or_eq_right' (y : α) (p : α Prop) : x : α, p x y = x := y, .inr rfl
@[simp] theorem exists_prop' (p : Prop) : ( _ : α, p) Nonempty α p :=
fun a, h => a, h, fun a, h => a, h
@[simp] theorem exists_prop : ( _h : a, b) a b :=
fun hp, hq => hp, hq, fun hp, hq => hp, hq
@@ -355,6 +369,11 @@ theorem forall_prop_of_false {p : Prop} {q : p → Prop} (hn : ¬p) : (∀ h' :
end quantifiers
/-! ## Nonempty -/
@[simp] theorem nonempty_prop (p : Prop) : Nonempty p p :=
fun h => h, fun h => h
/-! ## decidable -/
@[simp] theorem Decidable.not_not [Decidable p] : ¬¬p p := of_not_not, not_not_intro
@@ -390,7 +409,7 @@ else isTrue fun h2 => absurd h2 h
theorem decide_eq_true_iff (p : Prop) [Decidable p] : (decide p = true) p := by simp
@[simp] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
@[simp, boolToPropSimps] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
decide p = decide q (p q) :=
fun h => by rw [ decide_eq_true_iff p, h, decide_eq_true_iff], fun h => by simp [h]
@@ -403,7 +422,7 @@ theorem Decidable.not_imp_symm [Decidable a] (h : ¬a → b) (hb : ¬b) : a :=
theorem Decidable.not_imp_comm [Decidable a] [Decidable b] : (¬a b) (¬b a) :=
not_imp_symm, not_imp_symm
@[simp] theorem Decidable.not_imp_self [Decidable a] : (¬a a) a := by
theorem Decidable.not_imp_self [Decidable a] : (¬a a) a := by
have := @imp_not_self (¬a); rwa [not_not] at this
theorem Decidable.or_iff_not_imp_left [Decidable a] : a b (¬a b) :=
@@ -486,7 +505,7 @@ theorem Decidable.imp_iff_right_iff [Decidable a] : (a → b ↔ b) ↔ a b
(fun h => (Decidable.em a).imp_right fun ha' => h.mp fun ha => (ha' ha).elim)
(fun ab => ab.elim imp_iff_right fun hb => iff_of_true (fun _ => hb) hb)
theorem Decidable.imp_iff_left_iff [Decidable a] : (b a b) a b :=
theorem Decidable.imp_iff_left_iff [Decidable a] : (b a b) a b :=
propext (@Iff.comm (a b) b) (@Decidable.imp_iff_right_iff a b _)
theorem Decidable.and_or_imp [Decidable a] : a b (a c) a b c :=
@@ -573,12 +592,66 @@ theorem decide_ite (u : Prop) [du : Decidable u] (p q : Prop)
decide (ite u p q) = ite u (decide p) (decide q) := by
cases du <;> simp [*]
/- Confluence for `ite_true_same` and `decide_ite`. -/
@[simp] theorem ite_true_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
(if p then decide p else b) = (decide p || b) := by
cases h <;> (rename_i pt; simp [pt])
/- Confluence for `ite_then_self` and `decide_ite`. -/
@[simp] theorem ite_then_decide_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
(@ite _ p h (decide p) q) = (decide p || q) := by
split <;> simp_all
/- Confluence for `ite_false_same` and `decide_ite`. -/
@[simp] theorem ite_false_decide_same (p : Prop) [h : Decidable p] (b : Bool) :
(if p then b else decide p) = (decide p && b) := by
cases h <;> (rename_i pt; simp [pt])
/- Confluence for `ite_else_self` and `decide_ite`. -/
@[simp] theorem ite_else_decide_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
(@ite _ p h q (decide p)) = (decide p && q) := by
split <;> simp_all
@[deprecated ite_then_decide_self]
theorem ite_true_decide_same (p : Prop) [Decidable p] (b : Bool) :
(if p then decide p else b) = (decide p || b) := ite_then_decide_self p b
@[deprecated ite_false_decide_same]
theorem ite_false_decide_same (p : Prop) [Decidable p] (b : Bool) :
(if p then b else decide p) = (decide p && b) := ite_else_decide_self p b
@[simp] theorem ite_then_decide_not_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
(@ite _ p h (!decide p) q) = (!decide p && q) := by
split <;> simp_all
@[simp] theorem ite_else_decide_not_self (p : Prop) [h : Decidable p] {w : Decidable p} (q : Bool) :
(@ite _ p h q (!decide p)) = (!decide p || q) := by
split <;> simp_all
attribute [local simp] Decidable.imp_iff_left_iff
@[simp] theorem dite_eq_then (p : Prop) [Decidable p] {x : α} {y : ¬ p α} : (if h : p then x else y h) = x h : ¬ p, y h = x := by
split <;> simp_all
@[simp] theorem dite_eq_else (p : Prop) [Decidable p] {x : p α} {y : α} : (if h : p then x h else y) = y h : p, x h = y := by
split <;> simp_all
@[simp] theorem dite_iff_then (p : Prop) [Decidable p] {x : Prop} {y : ¬ p Prop} : ((if h : p then x else y h) x) h : ¬ p, y h x := by
split <;> simp_all
@[simp] theorem dite_iff_else (p : Prop) [Decidable p] {x : p Prop} {y : Prop} : ((if h : p then x h else y) y) h : p, x h y := by
split <;> simp_all
@[simp] theorem ite_eq_then (p : Prop) [Decidable p] (x y : α) : (if p then x else y) = x ¬ p y = x := by
split <;> simp_all
@[simp] theorem ite_eq_else (p : Prop) [Decidable p] (x y : α) : (if p then x else y) = y p x = y := by
split <;> simp_all
@[simp] theorem ite_iff_then (p : Prop) [Decidable p] (x y : Prop) : ((if p then x else y) x) ¬ p y = x := by
split <;> simp_all
@[simp] theorem ite_iff_else (p : Prop) [Decidable p] (x y : Prop) : ((if p then x else y) y) p x = y := by
split <;> simp_all
@[simp] theorem dite_then_false (p : Prop) [Decidable p] {x : ¬ p Prop} : (if h : p then False else x h) h : ¬ p, x h := by
split <;> simp_all
@[simp] theorem dite_else_false (p : Prop) [Decidable p] {x : p Prop} : (if h : p then x h else False) h : p, x h := by
split <;> simp_all
@[simp] theorem dite_then_true (p : Prop) [Decidable p] {x : ¬ p Prop} : (if h : p then True else x h) h : ¬ p, x h := by
split <;> simp_all
@[simp] theorem dite_else_true (p : Prop) [Decidable p] {x : p Prop} : (if h : p then x h else True) h : p, x h := by
split <;> simp_all

View File

@@ -244,7 +244,7 @@ instance : Std.Associative (· || ·) := ⟨Bool.or_assoc⟩
@[simp] theorem decide_not [g : Decidable p] [h : Decidable (Not p)] : decide (Not p) = !(decide p) := by
cases g <;> (rename_i gp; simp [gp]; rfl)
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by simp
theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by simp
@[simp] theorem heq_eq_eq (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq

View File

@@ -447,6 +447,7 @@ see there for more information.
@[extern "lean_io_remove_dir"] opaque removeDir : @& FilePath IO Unit
@[extern "lean_io_create_dir"] opaque createDir : @& FilePath IO Unit
/--
Moves a file or directory `old` to the new location `new`.
@@ -455,6 +456,16 @@ see there for more information.
-/
@[extern "lean_io_rename"] opaque rename (old new : @& FilePath) : IO Unit
/--
Creates a temporary file in the most secure manner possible. There are no race conditions in the
files creation. The file is readable and writable only by the creating user ID. Additionally
on UNIX style platforms the file is executable by nobody. The function returns both a `Handle`
to the already opened file as well as its `FilePath`.
Note that it is the caller's job to remove the file after use.
-/
@[extern "lean_io_create_tempfile"] opaque createTempFile : IO (Handle × FilePath)
end FS
@[extern "lean_io_getenv"] opaque getEnv (var : @& String) : BaseIO (Option String)
@@ -467,6 +478,17 @@ namespace FS
def withFile (fn : FilePath) (mode : Mode) (f : Handle IO α) : IO α :=
Handle.mk fn mode >>= f
/--
Like `createTempFile` but also takes care of removing the file after usage.
-/
def withTempFile [Monad m] [MonadFinally m] [MonadLiftT IO m] (f : Handle FilePath m α) :
m α := do
let (handle, path) createTempFile
try
f handle path
finally
removeFile path
def Handle.putStrLn (h : Handle) (s : String) : IO Unit :=
h.putStr (s.push '\n')

View File

@@ -552,9 +552,9 @@ The `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
non-dependent hypotheses. It has many variants:
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.
If an `hᵢ` is a defined constant `f`, then the equational lemmas associated with
`f` are used. This provides a convenient way to unfold `f`.
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.-
- If an `hᵢ` is a defined constant `f`, then `f` is unfolded. If `f` has equational lemmas associated
with it (and is not a projection or a `reducible` definition), these are used to rewrite with `f`.
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
attribute `[simp]` and all hypotheses.
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
@@ -679,9 +679,9 @@ syntax (name := delta) "delta" (ppSpace colGt ident)+ (location)? : tactic
* `unfold id` unfolds definition `id`.
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
For non-recursive definitions, this tactic is identical to `delta`.
For definitions by pattern matching, it uses "equation lemmas" which are
autogenerated for each match arm.
For non-recursive definitions, this tactic is identical to `delta`. For recursive definitions,
it uses the "unfolding lemma" `id.eq_def`, which is generated for each recursive definition,
to unfold according to the recursive definition given by the user.
-/
syntax (name := unfold) "unfold" (ppSpace colGt ident)+ (location)? : tactic

View File

@@ -190,21 +190,36 @@ def lt_wfRel : WellFoundedRelation Nat where
| Or.inl e => subst e; assumption
| Or.inr e => exact Acc.inv ih e
protected noncomputable def strongInductionOn
@[elab_as_elim] protected noncomputable def strongRecOn
{motive : Nat Sort u}
(n : Nat)
(ind : n, ( m, m < n motive m) motive n) : motive n :=
Nat.lt_wfRel.wf.fix ind n
@[deprecated Nat.strongRecOn (since := "2024-08-27")]
protected noncomputable def strongInductionOn
{motive : Nat Sort u}
(n : Nat)
(ind : n, ( m, m < n motive m) motive n) : motive n :=
Nat.strongRecOn n ind
@[elab_as_elim] protected noncomputable def caseStrongRecOn
{motive : Nat Sort u}
(a : Nat)
(zero : motive 0)
(ind : n, ( m, m n motive m) motive (succ n)) : motive a :=
Nat.strongRecOn a fun n =>
match n with
| 0 => fun _ => zero
| n+1 => fun h₁ => ind n (λ _ h₂ => h₁ _ (lt_succ_of_le h₂))
@[deprecated Nat.caseStrongRecOn (since := "2024-08-27")]
protected noncomputable def caseStrongInductionOn
{motive : Nat Sort u}
(a : Nat)
(zero : motive 0)
(ind : n, ( m, m n motive m) motive (succ n)) : motive a :=
Nat.strongInductionOn a fun n =>
match n with
| 0 => fun _ => zero
| n+1 => fun h₁ => ind n (λ _ h₂ => h₁ _ (lt_succ_of_le h₂))
Nat.caseStrongRecOn a zero ind
end Nat

View File

@@ -26,7 +26,7 @@ macro "clean_wf" : tactic =>
`(tactic| simp
(config := { unfoldPartialApp := true, zetaDelta := true, failIfUnchanged := false })
only [invImage, InvImage, Prod.lex, sizeOfWFRel, measure, Nat.lt_wfRel,
WellFoundedRelation.rel, sizeOf_nat])
WellFoundedRelation.rel, sizeOf_nat, reduceCtorEq])
/-- Extensible helper tactic for `decreasing_tactic`. This handles the "base case"
reasoning after applying lexicographic order lemmas.

View File

@@ -18,7 +18,6 @@ import Lean.Data.Name
import Lean.Data.NameMap
import Lean.Data.OpenDecl
import Lean.Data.Options
import Lean.Data.Parsec
import Lean.Data.PersistentArray
import Lean.Data.PersistentHashMap
import Lean.Data.PersistentHashSet

View File

@@ -6,23 +6,22 @@ Authors: Gabriel Ebner, Marc Huisinga
-/
prelude
import Lean.Data.Json.Basic
import Lean.Data.Parsec
import Lean.Data.RBMap
import Std.Internal.Parsec
open Std.Internal.Parsec
open Std.Internal.Parsec.String
namespace Lean.Json.Parser
open Lean.Parsec
open Lean.Parsec.String
@[inline]
def hexChar : Parser Nat := do
let c any
if '0' c c '9' then
pure $ c.val.toNat - '0'.val.toNat
else if 'a' c c 'f' then
pure $ c.val.toNat - 'a'.val.toNat + 10
else if 'A' c c 'F' then
pure $ c.val.toNat - 'A'.val.toNat + 10
if '0' <= c && c <= '9' then
pure $ (c.val - '0'.val).toNat
else if 'a' <= c && c <= 'f' then
pure $ (c.val - 'a'.val + 10).toNat
else if 'A' <= c && c <= 'F' then
pure $ (c.val - 'A'.val + 10).toNat
else
fail "invalid hex character"
@@ -44,31 +43,46 @@ def escapedChar : Parser Char := do
partial def strCore (acc : String) : Parser String := do
let c peek!
if c = '"' then -- "
if c == '"' then
skip
return acc
else
let c ← any
if c = '\\' then
if c == '\\' then
strCore (acc.push (← escapedChar))
-- as to whether c.val > 0xffff should be split up and encoded with multiple \u,
-- the JSON standard is not definite: both directly printing the character
-- and encoding it with multiple \u is allowed. we choose the former.
else if 0x0020 c.val c.val 0x10ffff then
else if 0x0020 <= c.val && c.val <= 0x10ffff then
strCore (acc.push c)
else
fail "unexpected character in string"
def str : Parser String := strCore ""
@[inline] def str : Parser String := strCore ""
partial def natCore (acc digits : Nat) : Parser (Nat × Nat) := do
let some c peek? | return (acc, digits)
if '0' c c '9' then
skip
let acc' := 10*acc + (c.val.toNat - '0'.val.toNat)
natCore acc' (digits+1)
partial def natCore (acc : Nat) : Parser Nat := do
if ← isEof then
return acc
else
let c ← peek!
if '0' <= c && c <= '9' then
skip
let acc' := 10*acc + (c.val - '0'.val).toNat
natCore acc'
else
return acc
partial def natCoreNumDigits (acc digits : Nat) : Parser (Nat × Nat) := do
if ← isEof then
return (acc, digits)
else
let c ← peek!
if '0' <= c && c <= '9' then
skip
let acc' := 10*acc + (c.val - '0'.val).toNat
natCoreNumDigits acc' (digits+1)
else
return (acc, digits)
@[inline]
def lookahead (p : Char → Prop) (desc : String) [DecidablePred p] : Parser Unit := do
@@ -80,129 +94,152 @@ def lookahead (p : Char → Prop) (desc : String) [DecidablePred p] : Parser Uni
@[inline]
def natNonZero : Parser Nat := do
lookahead (fun c => '1' c c '9') "1-9"
let (n, _) natCore 0 0
return n
lookahead (fun c => '1' <= c && c <= '9') "1-9"
natCore 0
@[inline]
def natNumDigits : Parser (Nat × Nat) := do
lookahead (fun c => '0' c c '9') "digit"
natCore 0 0
lookahead (fun c => '0' <= c && c <= '9') "digit"
natCoreNumDigits 0 0
@[inline]
def natMaybeZero : Parser Nat := do
let (n, _) natNumDigits
return n
lookahead (fun c => '0' <= c && c <= '9') "0-9"
natCore 0
def num : Parser JsonNumber := do
@[inline]
def numSign : Parser Int := do
let c ← peek!
let sign if c = '-' then
let sign ← if c == '-' then
skip
pure (-1 : Int)
return (-1 : Int)
else
pure 1
return 1
@[inline]
def nat : Parser Nat := do
let c ← peek!
let res if c = '0' then
if c == '0' then
skip
pure 0
return 0
else
natNonZero
let c? peek?
let res : JsonNumber if c? = some '.' then
skip
let (n, d) natNumDigits
if d > USize.size then fail "too many decimals"
let mantissa' := sign * (res * (10^d : Nat) + n)
let exponent' := d
pure <| JsonNumber.mk mantissa' exponent'
@[inline]
def numWithDecimals : Parser JsonNumber := do
let sign ← numSign
let whole ← nat
if ← isEof then
pure <| JsonNumber.fromInt (sign * whole)
else
pure <| JsonNumber.fromInt (sign * res)
let c? peek?
if c? = some 'e' c? = some 'E' then
skip
let c ← peek!
if c = '-' then
if c == '.' then
skip
let n natMaybeZero
return res.shiftr n
let (n, d) ← natNumDigits
if d > USize.size then fail "too many decimals"
let mantissa' := sign * (whole * (10^d : Nat) + n)
let exponent' := d
pure <| JsonNumber.mk mantissa' exponent'
else
if c = '+' then skip
let n natMaybeZero
if n > USize.size then fail "exp too large"
return res.shiftl n
else
return res
pure <| JsonNumber.fromInt (sign * whole)
partial def arrayCore (anyCore : Parser Json) (acc : Array Json) : Parser (Array Json) := do
let hd anyCore
let acc' := acc.push hd
let c any
if c = ']' then
ws
return acc'
else if c = ',' then
ws
arrayCore anyCore acc'
@[inline]
def exponent (value : JsonNumber) : Parser JsonNumber := do
if ← isEof then
return value
else
fail "unexpected character in array"
partial def objectCore (anyCore : Parser Json) : Parser (RBNode String (fun _ => Json)) := do
lookahead (fun c => c = '"') "\""; skip; -- "
let k strCore ""; ws
lookahead (fun c => c = ':') ":"; skip; ws
let v anyCore
let c any
if c = '}' then
ws
return RBNode.singleton k v
else if c = ',' then
ws
let kvs objectCore anyCore
return kvs.insert compare k v
else
fail "unexpected character in object"
partial def anyCore : Parser Json := do
let c peek!
if c = '[' then
skip; ws
let c ← peek!
if c = ']' then
skip; ws
return Json.arr (Array.mkEmpty 0)
if c == 'e' || c == 'E' then
skip
let c ← peek!
if c == '-' then
skip
let n ← natMaybeZero
return value.shiftr n
else
if c = '+' then skip
let n ← natMaybeZero
if n > USize.size then fail "exp too large"
return value.shiftl n
else
let a arrayCore anyCore (Array.mkEmpty 4)
return Json.arr a
else if c = '{' then
skip; ws
let c peek!
if c = '}' then
skip; ws
return Json.obj (RBNode.leaf)
else
let kvs objectCore anyCore
return Json.obj kvs
else if c = '\"' then
skip
let s ← strCore ""
ws
return Json.str s
else if c = 'f' then
skipString "false"; ws
return Json.bool false
else if c = 't' then
skipString "true"; ws
return Json.bool true
else if c = 'n' then
skipString "null"; ws
return Json.null
else if c = '-' ('0' ≤ c ∧ c ≤ '9') then
let n ← num
ws
return Json.num n
else
fail "unexpected input"
return value
def num : Parser JsonNumber := do
let res : JsonNumber ← numWithDecimals
exponent res
mutual
partial def arrayCore (acc : Array Json) : Parser (Array Json) := do
let hd ← anyCore
let acc' := acc.push hd
let c ← any
if c == ']' then
ws
return acc'
else if c == ',' then
ws
arrayCore acc'
else
fail "unexpected character in array"
partial def objectCore (kvs : RBNode String (fun _ => Json)) : Parser (RBNode String (fun _ => Json)) := do
lookahead (fun c => c == '"') "\""; skip;
let k str; ws
lookahead (fun c => c == ':') ":"; skip; ws
let v anyCore
let c any
if c == '}' then
ws
return kvs.insert compare k v
else if c == ',' then
ws
objectCore (kvs.insert compare k v)
else
fail "unexpected character in object"
partial def anyCore : Parser Json := do
let c peek!
if c == '[' then
skip; ws
let c peek!
if c == ']' then
skip; ws
return Json.arr (Array.mkEmpty 0)
else
let a arrayCore (Array.mkEmpty 4)
return Json.arr a
else if c == '{' then
skip; ws
let c peek!
if c == '}' then
skip; ws
return Json.obj (RBNode.leaf)
else
let kvs objectCore RBNode.leaf
return Json.obj kvs
else if c == '\"' then
skip
let s ← str
ws
return Json.str s
else if c == 'f' then
skipString "false"; ws
return Json.bool false
else if c == 't' then
skipString "true"; ws
return Json.bool true
else if c == 'n' then
skipString "null"; ws
return Json.null
else if c == '-' || ('0' <= c && c <= '9') then
let n ← num
ws
return Json.num n
else
fail "unexpected input"
end
def any : Parser Json := do
ws
@@ -215,9 +252,7 @@ end Json.Parser
namespace Json
def parse (s : String) : Except String Lean.Json :=
match Json.Parser.any s.mkIterator with
| Parsec.ParseResult.success _ res => Except.ok res
| Parsec.ParseResult.error it err => Except.error s!"offset {repr it.i.byteIdx}: {err}"
Parser.run Json.Parser.any s
end Json

View File

@@ -6,6 +6,7 @@ Authors: Gabriel Ebner, Marc Huisinga, Wojciech Nawrocki
prelude
import Lean.Data.Format
import Lean.Data.Json.Basic
import Init.Data.List.Impl
namespace Lean
namespace Json

View File

@@ -22,27 +22,23 @@ reduce the size of the resulting JSON. -/
/--
Identifier of a reference.
-/
-- Names are represented by strings to avoid having to parse them to `Name`,
-- which is relatively expensive. Most uses of these names only need equality, anyways.
inductive RefIdent where
/-- Named identifier. These are used in all references that are globally available. -/
| const (moduleName : Name) (identName : Name) : RefIdent
| const (moduleName : String) (identName : String) : RefIdent
/-- Unnamed identifier. These are used for all local references. -/
| fvar (moduleName : Name) (id : FVarId) : RefIdent
| fvar (moduleName : String) (id : String) : RefIdent
deriving BEq, Hashable, Inhabited
namespace RefIdent
instance : ToJson FVarId where
toJson id := toJson id.name
instance : FromJson FVarId where
fromJson? s := return fromJson? s
/-- Shortened representation of `RefIdent` for more compact serialization. -/
inductive RefIdentJsonRepr
/-- Shortened representation of `RefIdent.const` for more compact serialization. -/
| c (m n : Name)
| c (m n : String)
/-- Shortened representation of `RefIdent.fvar` for more compact serialization. -/
| f (m : Name) (i : FVarId)
| f (m : String) (i : String)
deriving FromJson, ToJson
/-- Converts `id` to its compact serialization representation. -/
@@ -74,7 +70,7 @@ end RefIdent
/-- Information about the declaration surrounding a reference. -/
structure RefInfo.ParentDecl where
/-- Name of the declaration surrounding a reference. -/
name : Name
name : String
/-- Range of the declaration surrounding a reference. -/
range : Lsp.Range
/-- Selection range of the declaration surrounding a reference. -/
@@ -104,7 +100,7 @@ instance : ToJson RefInfo where
let rangeToList (r : Lsp.Range) : List Nat :=
[r.start.line, r.start.character, r.end.line, r.end.character]
let parentDeclToList (d : RefInfo.ParentDecl) : List Json :=
let name := d.name.toString |> toJson
let name := d.name |> toJson
let range := rangeToList d.range |>.map toJson
let selectionRange := rangeToList d.selectionRange |>.map toJson
[name] ++ range ++ selectionRange
@@ -118,34 +114,42 @@ instance : ToJson RefInfo where
]
instance : FromJson RefInfo where
-- This implementation is optimized to prevent redundant intermediate allocations.
fromJson? j := do
let toRange : List Nat Except String Lsp.Range
| [sLine, sChar, eLine, eChar] => pure sLine, sChar, eLine, eChar
| l => throw s!"Expected list of length 4, not {l.length}"
let toParentDecl (a : Array Json) : Except String RefInfo.ParentDecl := do
let name := String.toName <| fromJson? a[0]!
let range a[1:5].toArray.toList |>.mapM fromJson?
let range toRange range
let selectionRange a[5:].toArray.toList |>.mapM fromJson?
let selectionRange toRange selectionRange
let toRange (a : Array Json) (i : Nat) : Except String Lsp.Range :=
if h : a.size < i + 4 then
throw s!"Expected list of length 4, not {a.size}"
else
return {
start := {
line := fromJson? a[i]
character := fromJson? a[i+1]
}
«end» := {
line := fromJson? a[i+2]
character := fromJson? a[i+3]
}
}
let toParentDecl (a : Array Json) (i : Nat) : Except String RefInfo.ParentDecl := do
let name fromJson? a[i]!
let range toRange a (i + 1)
let selectionRange toRange a (i + 5)
return name, range, selectionRange
let toLocation (l : List Json) : Except String RefInfo.Location := do
let l := l.toArray
if l.size != 4 && l.size != 13 then
let toLocation (a : Array Json) : Except String RefInfo.Location := do
if a.size != 4 && a.size != 13 then
.error "Expected list of length 4 or 13, not {l.size}"
let range l[:4].toArray.toList |>.mapM fromJson?
let range toRange range
if l.size == 13 then
let parentDecl toParentDecl l[4:].toArray
let range toRange a 0
if a.size == 13 then
let parentDecl toParentDecl a 4
return range, parentDecl
else
return range, none
let definition? j.getObjValAs? (Option $ List Json) "definition"
let definition? j.getObjValAs? (Option $ Array Json) "definition"
let definition? match definition? with
| none => pure none
| some list => some <$> toLocation list
let usages j.getObjValAs? (Array $ List Json) "usages"
| some array => some <$> toLocation array
let usages j.getObjValAs? (Array $ Array Json) "usages"
let usages usages.mapM toLocation
pure { definition?, usages }

View File

@@ -4,18 +4,19 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Dany Fabian
-/
prelude
import Lean.Data.Parsec
import Std.Internal.Parsec
import Lean.Data.Xml.Basic
open System
open Lean
namespace Lean
namespace Xml
namespace Parser
open Std.Internal.Parsec
open Std.Internal.Parsec.String
open Lean.Parsec
open Lean.Parsec.String
namespace Parser
abbrev LeanChar := Char
@@ -481,8 +482,6 @@ def document : Parser Element := prolog *> element <* many Misc <* eof
end Parser
def parse (s : String) : Except String Element :=
match Xml.Parser.document s.mkIterator with
| .success _ res => Except.ok res
| .error it err => Except.error s!"offset {it.i.byteIdx.repr}: {err}\n{(it.prevn 10).extract it}"
Parser.run Xml.Parser.document s
end Xml

View File

@@ -17,7 +17,7 @@ register_builtin_option autoImplicit : Bool := {
register_builtin_option relaxedAutoImplicit : Bool := {
defValue := true
descr := "When \"relaxed\" mode is enabled, any atomic nonempty identifier is eligible for auto bound implicit locals (see optin `autoBoundImplicitLocal`."
descr := "When \"relaxed\" mode is enabled, any atomic nonempty identifier is eligible for auto bound implicit locals (see option `autoImplicit`)."
}

View File

@@ -176,7 +176,8 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
let mut binderIds := binderIds
let mut binderIdsIniSize := binderIds.size
let mut modifiedVarDecls := false
for varDecl in varDecls do
-- Go through declarations in reverse to respect shadowing
for varDecl in varDecls.reverse do
let (ids, ty?, explicit') match varDecl with
| `(bracketedBinderF|($ids* $[: $ty?]? $(annot?)?)) =>
if annot?.isSome then
@@ -208,7 +209,7 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
`(bracketedBinderF| ($id $[: $ty?]?))
else
`(bracketedBinderF| {$id $[: $ty?]?})
for id in ids do
for id in ids.reverse do
if let some idx := binderIds.findIdx? fun binderId => binderId.raw.isIdent && binderId.raw.getId == id.raw.getId then
binderIds := binderIds.eraseIdx idx
modifiedVarDecls := true
@@ -216,7 +217,7 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
else
varDeclsNew := varDeclsNew.push ( mkBinder id explicit')
if modifiedVarDecls then
modifyScope fun scope => { scope with varDecls := varDeclsNew }
modifyScope fun scope => { scope with varDecls := varDeclsNew.reverse }
if binderIds.size != binderIdsIniSize then
binderIds.mapM fun binderId =>
if explicit then
@@ -228,15 +229,14 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
@[builtin_command_elab «variable»] def elabVariable : CommandElab
| `(variable $binders*) => do
let binders binders.concatMapM replaceBinderAnnotation
-- Try to elaborate `binders` for sanity checking
runTermElabM fun _ => Term.withSynthesize <| Term.withAutoBoundImplicit <|
Term.elabBinders binders fun _ => pure ()
-- Remark: if we want to produce error messages when variables shadow existing ones, here is the place to do it.
for binder in binders do
let binders replaceBinderAnnotation binder
-- Remark: if we want to produce error messages when variables shadow existing ones, here is the place to do it.
for binder in binders do
let varUIds getBracketedBinderIds binder |>.mapM (withFreshMacroScope MonadQuotation.addMacroScope)
modifyScope fun scope => { scope with varDecls := scope.varDecls.push binder, varUIds := scope.varUIds ++ varUIds }
let varUIds ( getBracketedBinderIds binder) |>.mapM (withFreshMacroScope MonadQuotation.addMacroScope)
modifyScope fun scope => { scope with varDecls := scope.varDecls.push binder, varUIds := scope.varUIds ++ varUIds }
| _ => throwUnsupportedSyntax
open Meta
@@ -504,12 +504,60 @@ def elabRunMeta : CommandElab := fun stx =>
@[builtin_command_elab Lean.Parser.Command.include] def elabInclude : CommandElab
| `(Lean.Parser.Command.include| include $ids*) => do
let vars := ( getScope).varDecls.concatMap getBracketedBinderIds
let sc getScope
let vars sc.varDecls.concatMapM getBracketedBinderIds
let mut uids := #[]
for id in ids do
unless vars.contains id.getId do
if let some idx := vars.findIdx? (· == id.getId) then
uids := uids.push sc.varUIds[idx]!
else
throwError "invalid 'include', variable '{id}' has not been declared in the current scope"
modifyScope fun sc =>
{ sc with includedVars := sc.includedVars ++ ids.toList.map (·.getId) }
modifyScope fun sc => { sc with
includedVars := sc.includedVars ++ uids.toList
omittedVars := sc.omittedVars.filter (!uids.contains ·) }
| _ => throwUnsupportedSyntax
@[builtin_command_elab Lean.Parser.Command.omit] def elabOmit : CommandElab
| `(Lean.Parser.Command.omit| omit $omits*) => do
-- TODO: this really shouldn't have to re-elaborate section vars... they should come
-- pre-elaborated
let omittedVars runTermElabM fun vars => do
Term.synthesizeSyntheticMVarsNoPostponing
-- We don't want to store messages produced when elaborating `(getVarDecls s)` because they have already been saved when we elaborated the `variable`(s) command.
-- So, we use `Core.resetMessageLog`.
Core.resetMessageLog
-- resolve each omit to variable user name or type pattern
let elaboratedOmits : Array (Sum Name Expr) omits.mapM fun
| `(ident| $id:ident) => pure <| Sum.inl id.getId
| `(Lean.Parser.Term.instBinder| [$id : $_]) => pure <| Sum.inl id.getId
| `(Lean.Parser.Term.instBinder| [$ty]) =>
Sum.inr <$> Term.withoutErrToSorry (Term.elabTermAndSynthesize ty none)
| _ => throwUnsupportedSyntax
-- check that each omit is actually used in the end
let mut omitsUsed := omits.map fun _ => false
let mut omittedVars := #[]
let mut revSectionFVars : Std.HashMap FVarId Name := {}
for (uid, var) in ( read).sectionFVars do
revSectionFVars := revSectionFVars.insert var.fvarId! uid
for var in vars do
let ldecl var.fvarId!.getDecl
if let some idx := ( elaboratedOmits.findIdxM? fun
| .inl id => return ldecl.userName == id
| .inr ty => do
let mctx getMCtx
isDefEq ty ldecl.type <* setMCtx mctx) then
if let some uid := revSectionFVars[var.fvarId!]? then
omittedVars := omittedVars.push uid
omitsUsed := omitsUsed.set! idx true
else
throwError "invalid 'omit', '{ldecl.userName}' has not been declared in the current scope"
for o in omits, used in omitsUsed do
unless used do
throwError "'{o}' did not match any variables in the current scope"
return omittedVars
modifyScope fun sc => { sc with
omittedVars := sc.omittedVars ++ omittedVars.toList
includedVars := sc.includedVars.filter (!omittedVars.contains ·) }
| _ => throwUnsupportedSyntax
@[builtin_command_elab Parser.Command.exit] def elabExit : CommandElab := fun _ =>

View File

@@ -51,8 +51,6 @@ structure Scope where
even if they do not work with binders per se.
-/
varDecls : Array (TSyntax ``Parser.Term.bracketedBinder) := #[]
/-- `include`d section variable names -/
includedVars : List Name := []
/--
Globally unique internal identifiers for the `varDecls`.
There is one identifier per variable introduced by the binders
@@ -62,6 +60,10 @@ structure Scope where
that capture these variables.
-/
varUIds : Array Name := #[]
/-- `include`d section variable names (from `varUIds`) -/
includedVars : List Name := []
/-- `omit`ted section variable names (from `varUIds`) -/
omittedVars : List Name := []
/--
If true (default: false), all declarations that fail to compile
automatically receive the `noncomputable` modifier.
@@ -555,19 +557,21 @@ private def mkMetaContext : Meta.Context := {
open Lean.Parser.Term in
/-- Return identifier names in the given bracketed binder. -/
def getBracketedBinderIds : Syntax Array Name
| `(bracketedBinderF|($ids* $[: $ty?]? $(_annot?)?)) => ids.map Syntax.getId
| `(bracketedBinderF|{$ids* $[: $ty?]?}) => ids.map Syntax.getId
| `(bracketedBinderF|[$id : $_]) => #[id.getId]
| `(bracketedBinderF|[$_]) => #[Name.anonymous]
| _ => #[]
def getBracketedBinderIds : Syntax CommandElabM (Array Name)
| `(bracketedBinderF|($ids* $[: $ty?]? $(_annot?)?)) => return ids.map Syntax.getId
| `(bracketedBinderF|{$ids* $[: $ty?]?}) => return ids.map Syntax.getId
| `(bracketedBinderF|$ids* : $_) => return ids.map Syntax.getId
| `(bracketedBinderF|[$id : $_]) => return #[id.getId]
| `(bracketedBinderF|[$_]) => return #[Name.anonymous]
| _ => throwUnsupportedSyntax
private def mkTermContext (ctx : Context) (s : State) : Term.Context := Id.run do
private def mkTermContext (ctx : Context) (s : State) : CommandElabM Term.Context := do
let scope := s.scopes.head!
let mut sectionVars := {}
for id in scope.varDecls.concatMap getBracketedBinderIds, uid in scope.varUIds do
for id in ( scope.varDecls.concatMapM getBracketedBinderIds), uid in scope.varUIds do
sectionVars := sectionVars.insert id uid
{ macroStack := ctx.macroStack
return {
macroStack := ctx.macroStack
sectionVars := sectionVars
isNoncomputableSection := scope.isNoncomputable
tacticCache? := ctx.tacticCache? }
@@ -607,7 +611,7 @@ def liftTermElabM (x : TermElabM α) : CommandElabM α := do
-- make sure `observing` below also catches runtime exceptions (like we do by default in
-- `CommandElabM`)
let _ := MonadAlwaysExcept.except (m := TermElabM)
let x : MetaM _ := (observing (try x finally Meta.reportDiag)).run (mkTermContext ctx s) { levelNames := scope.levelNames }
let x : MetaM _ := (observing (try x finally Meta.reportDiag)).run ( mkTermContext ctx s) { levelNames := scope.levelNames }
let x : CoreM _ := x.run mkMetaContext {}
let ((ea, _), _) runCore x
MonadExcept.ofExcept ea
@@ -704,7 +708,7 @@ def expandDeclId (declId : Syntax) (modifiers : Modifiers) : CommandElabM Expand
let currNamespace getCurrNamespace
let currLevelNames getLevelNames
let r Elab.expandDeclId currNamespace currLevelNames declId modifiers
for id in ( getScope).varDecls.concatMap getBracketedBinderIds do
for id in ( ( getScope).varDecls.concatMapM getBracketedBinderIds) do
if id == r.shortName then
throwError "invalid declaration name '{r.shortName}', there is a section variable with the same name"
return r

View File

@@ -163,7 +163,7 @@ def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List S
let diff := Diff.diff (expected.split (· == '\n')).toArray (res.split (· == '\n')).toArray
Diff.linesToString diff
else res
logErrorAt tk m!"❌ Docstring on `#guard_msgs` does not match generated message:\n\n{feedback}"
logErrorAt tk m!" Docstring on `#guard_msgs` does not match generated message:\n\n{feedback}"
pushInfoLeaf (.ofCustomInfo { stx := getRef, value := Dynamic.mk (GuardMsgFailure.mk res) })
| _ => throwUnsupportedSyntax

View File

@@ -336,30 +336,42 @@ def instantiateMVarsProfiling (e : Expr) : MetaM Expr := do
/--
Runs `k` with a restricted local context where only section variables from `vars` are included that
* are directly referenced in any `headers`,
* are included in `includedVars` (via the `include` command),
* are included in `sc.includedVars` (via the `include` command),
* are directly referenced in any variable included by these rules, OR
* are instance-implicit variables that only reference section variables included by these rules.
* are instance-implicit variables that only reference section variables included by these rules AND
are not listed in `sc.omittedVars` (via `omit`; note that `omit` also subtracts from
`sc.includedVars`).
-/
private def withHeaderSecVars {α} (vars : Array Expr) (includedVars : List Name) (headers : Array DefViewElabHeader)
private def withHeaderSecVars {α} (vars : Array Expr) (sc : Command.Scope) (headers : Array DefViewElabHeader)
(k : Array Expr TermElabM α) : TermElabM α := do
let (_, used) collectUsed.run {}
let mut revSectionFVars : Std.HashMap FVarId Name := {}
for (uid, var) in ( read).sectionFVars do
revSectionFVars := revSectionFVars.insert var.fvarId! uid
let (_, used) collectUsed revSectionFVars |>.run {}
let (lctx, localInsts, vars) removeUnused vars used
withLCtx lctx localInsts <| k vars
where
collectUsed : StateRefT CollectFVars.State MetaM Unit := do
collectUsed revSectionFVars : StateRefT CollectFVars.State MetaM Unit := do
-- directly referenced in headers
headers.forM (·.type.collectFVars)
-- included by `include`
vars.forM fun var => do
let ldecl getFVarLocalDecl var
if includedVars.contains ldecl.userName then
modify (·.add ldecl.fvarId)
for var in vars do
if let some uid := revSectionFVars[var.fvarId!]? then
if sc.includedVars.contains uid then
modify (·.add var.fvarId!)
-- transitively referenced
get >>= (·.addDependencies) >>= set
for var in ( get).fvarIds do
if let some uid := revSectionFVars[var]? then
if sc.omittedVars.contains uid then
throwError "cannot omit referenced section variable '{Expr.fvar var}'"
-- instances (`addDependencies` unnecessary as by definition they may only reference variables
-- already included)
vars.forM fun var => do
for var in vars do
let ldecl getFVarLocalDecl var
if let some uid := revSectionFVars[var.fvarId!]? then
if sc.omittedVars.contains uid then
continue
let st get
if ldecl.binderInfo.isInstImplicit && ( getFVars ldecl.type).all st.fvarSet.contains then
modify (·.add ldecl.fvarId)
@@ -371,7 +383,12 @@ register_builtin_option deprecated.oldSectionVars : Bool := {
descr := "re-enable deprecated behavior of including exactly the section variables used in a declaration"
}
private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr) (includedVars : List Name) : TermElabM (Array Expr) :=
register_builtin_option linter.unusedSectionVars : Bool := {
defValue := true
descr := "enable the 'unused section variables in theorem body' linter"
}
private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr) (sc : Command.Scope) : TermElabM (Array Expr) :=
headers.mapM fun header => do
let mut reusableResult? := none
if let some snap := header.bodySnap? then
@@ -386,7 +403,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
withReuseContext header.value do
withDeclName header.declName <| withLevelNames header.levelNames do
let valStx liftMacroM <| declValToTerm header.value
(if header.kind.isTheorem && !deprecated.oldSectionVars.get ( getOptions) then withHeaderSecVars vars includedVars #[header] else fun x => x #[]) fun vars => do
(if header.kind.isTheorem && !deprecated.oldSectionVars.get ( getOptions) then withHeaderSecVars vars sc #[header] else fun x => x #[]) fun vars => do
forallBoundedTelescope header.type header.numParams fun xs type => do
-- Add new info nodes for new fvars. The server will detect all fvars of a binder by the binder's source location.
for i in [0:header.binderIds.size] do
@@ -399,18 +416,25 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
-- leads to more section variables being included than necessary
let val instantiateMVarsProfiling val
let val mkLambdaFVars xs val
unless header.type.hasSorry || val.hasSorry do
for var in vars do
unless header.type.containsFVar var.fvarId! ||
val.containsFVar var.fvarId! ||
( vars.anyM (fun v => return ( v.fvarId!.getType).containsFVar var.fvarId!)) do
let varDecl var.fvarId!.getDecl
let var := if varDecl.userName.hasMacroScopes && varDecl.binderInfo.isInstImplicit then
m!"[{varDecl.type}]".group
if linter.unusedSectionVars.get ( getOptions) && !header.type.hasSorry && !val.hasSorry then
let unusedVars vars.filterMapM fun var => do
let varDecl var.fvarId!.getDecl
return if sc.includedVars.contains varDecl.userName ||
header.type.containsFVar var.fvarId! || val.containsFVar var.fvarId! ||
( vars.anyM (fun v => return ( v.fvarId!.getType).containsFVar var.fvarId!)) then
none
else
if varDecl.userName.hasMacroScopes && varDecl.binderInfo.isInstImplicit then
some m!"[{varDecl.type}]"
else
var
logWarningAt header.ref m!"included section variable '{var}' is not used in \
'{header.declName}', consider excluding it"
some m!"{var}"
if unusedVars.size > 0 then
Linter.logLint linter.unusedSectionVars header.ref
m!"automatically included section variable(s) unused in theorem '{header.declName}':\
\n {MessageData.joinSep unusedVars.toList "\n "}\
\nconsider restructuring your `variable` declarations so that the variables are not \
in scope or explicitly omit them:\
\n omit {MessageData.joinSep unusedVars.toList " "} in theorem ..."
return val
if let some snap := header.bodySnap? then
snap.new.resolve <| some {
@@ -962,7 +986,7 @@ partial def checkForHiddenUnivLevels (allUserLevelNames : List Name) (preDefs :
for preDef in preDefs do
checkPreDef preDef
def elabMutualDef (vars : Array Expr) (includedVars : List Name) (views : Array DefView) : TermElabM Unit :=
def elabMutualDef (vars : Array Expr) (sc : Command.Scope) (views : Array DefView) : TermElabM Unit :=
if isExample views then
withoutModifyingEnv do
-- save correct environment in info tree
@@ -983,7 +1007,7 @@ where
addLocalVarInfo view.declId funFVar
let values
try
let values elabFunValues headers vars includedVars
let values elabFunValues headers vars sc
Term.synthesizeSyntheticMVarsNoPostponing
values.mapM (instantiateMVarsProfiling ·)
catch ex =>
@@ -993,7 +1017,7 @@ where
let letRecsToLift getLetRecsToLift
let letRecsToLift letRecsToLift.mapM instantiateMVarsAtLetRecToLift
checkLetRecsToLiftTypes funFVars letRecsToLift
(if headers.all (·.kind.isTheorem) && !deprecated.oldSectionVars.get ( getOptions) then withHeaderSecVars vars includedVars headers else withUsed vars headers values letRecsToLift) fun vars => do
(if headers.all (·.kind.isTheorem) && !deprecated.oldSectionVars.get ( getOptions) then withHeaderSecVars vars sc headers else withUsed vars headers values letRecsToLift) fun vars => do
let preDefs MutualClosure.main vars headers funFVars values letRecsToLift
for preDef in preDefs do
trace[Elab.definition] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
@@ -1064,8 +1088,8 @@ def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
if let some snap := snap? then
-- no non-fatal diagnostics at this point
snap.new.resolve <| .ofTyped { defs, diagnostics := .empty : DefsParsedSnapshot }
let includedVars := ( getScope).includedVars
runTermElabM fun vars => Term.elabMutualDef vars includedVars views
let sc getScope
runTermElabM fun vars => Term.elabMutualDef vars sc views
builtin_initialize
registerTraceClass `Elab.definition.mkClosure

View File

@@ -10,3 +10,5 @@ import Lean.Elab.PreDefinition.Main
import Lean.Elab.PreDefinition.MkInhabitant
import Lean.Elab.PreDefinition.WF
import Lean.Elab.PreDefinition.Eqns
import Lean.Elab.PreDefinition.Nonrec.Eqns
import Lean.Elab.PreDefinition.EqUnfold

View File

@@ -12,6 +12,7 @@ import Lean.Util.NumApps
import Lean.PrettyPrinter
import Lean.Meta.AbstractNestedProofs
import Lean.Meta.ForEachExpr
import Lean.Meta.Eqns
import Lean.Elab.RecAppSyntax
import Lean.Elab.DefView
import Lean.Elab.PreDefinition.TerminationHint
@@ -153,6 +154,7 @@ private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List N
if compile && shouldGenCodeFor preDef then
discard <| compileDecl decl
if applyAttrAfterCompilation then
generateEagerEqns preDef.declName
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
def addAndCompileNonRec (preDef : PreDefinition) (all : List Name := [preDef.declName]) : TermElabM Unit := do

View File

@@ -0,0 +1,62 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
prelude
import Lean.Meta.Eqns
import Lean.Meta.Tactic.Util
import Lean.Meta.Tactic.Rfl
import Lean.Meta.Tactic.Intro
import Lean.Meta.Tactic.Apply
namespace Lean.Meta
/-- Try to close goal using `rfl` with smart unfolding turned off. -/
def tryURefl (mvarId : MVarId) : MetaM Bool :=
withOptions (smartUnfolding.set · false) do
try mvarId.refl; return true catch _ => return false
/--
Returns the "const unfold" theorem (`f.eq_unfold`) for the given declaration.
This is not extensible, and always builds on the unfold theorem (`f.eq_def`).
-/
def getConstUnfoldEqnFor? (declName : Name) : MetaM (Option Name) := do
let some unfoldEqnName getUnfoldEqnFor? (nonRec := true) declName | return none
let info getConstInfo unfoldEqnName
let type forallTelescope info.type fun xs eq => do
let some (_, lhs, rhs) := eq.eq? | throwError "Unexpected unfold theorem type {info.type}"
unless lhs.getAppFn.isConstOf declName do
throwError "Unexpected unfold theorem type {info.type}"
unless lhs.getAppArgs == xs do
throwError "Unexpected unfold theorem type {info.type}"
let type mkEq lhs.getAppFn ( mkLambdaFVars xs rhs)
return type
let value withNewMCtxDepth do
let main mkFreshExprSyntheticOpaqueMVar type
if ( tryURefl main.mvarId!) then -- try to make a rfl lemma if possible
instantiateMVars main
else forallTelescope info.type fun xs _eq => do
let mut proof mkConstWithLevelParams unfoldEqnName
proof := mkAppN proof xs
for x in xs.reverse do
proof mkLambdaFVars #[x] proof
proof mkAppM ``funext #[proof]
return proof
let name := .str declName eqUnfoldThmSuffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return some name
builtin_initialize
registerReservedNameAction fun name => do
let .str p s := name | return false
unless ( getEnv).isSafeDefinition p do return false
if s == eqUnfoldThmSuffix then
return ( MetaM.run' <| getConstUnfoldEqnFor? p).isSome
return false
end Lean.Meta

View File

@@ -43,15 +43,6 @@ def expandRHS? (mvarId : MVarId) : MetaM (Option MVarId) := do
let (true, rhs') := expand false rhs | return none
return some ( mvarId.replaceTargetDefEq ( mkEq lhs rhs'))
def funext? (mvarId : MVarId) : MetaM (Option MVarId) := do
let target mvarId.getType'
let some (_, _, rhs) := target.eq? | return none
unless rhs.isLambda do return none
commitWhenSome? do
let [mvarId] mvarId.apply ( mkConstWithFreshMVarLevels ``funext) | return none
let (_, mvarId) mvarId.intro1
return some mvarId
def simpMatch? (mvarId : MVarId) : MetaM (Option MVarId) := do
let mvarId' Split.simpMatchTarget mvarId
if mvarId != mvarId' then return some mvarId' else return none
@@ -60,7 +51,8 @@ def simpIf? (mvarId : MVarId) : MetaM (Option MVarId) := do
let mvarId' simpIfTarget mvarId (useDecide := true)
if mvarId != mvarId' then return some mvarId' else return none
private def findMatchToSplit? (env : Environment) (e : Expr) (declNames : Array Name) (exceptionSet : ExprSet) : Option Expr :=
private def findMatchToSplit? (deepRecursiveSplit : Bool) (env : Environment) (e : Expr)
(declNames : Array Name) (exceptionSet : ExprSet) : Option Expr :=
e.findExt? fun e => Id.run do
if e.hasLooseBVars || exceptionSet.contains e then
return Expr.FindStep.visit
@@ -75,7 +67,14 @@ private def findMatchToSplit? (env : Environment) (e : Expr) (declNames : Array
break
unless hasFVarDiscr do
return Expr.FindStep.visit
-- At least one alternative must contain a `declNames` application with loose bound variables.
-- For non-recursive functions (`declNames` empty), we split here
if declNames.isEmpty then
return Expr.FindStep.found
-- For recursive functions, the “new” behavior is to likewise split
if deepRecursiveSplit then
return Expr.FindStep.found
-- Else, the “old” behavior is split only when at least one alternative contains a `declNames`
-- application with loose bound variables.
for i in [info.getFirstAltPos : info.getFirstAltPos + info.numAlts] do
let alt := args[i]!
if Option.isSome <| alt.find? fun e => declNames.any e.isAppOf && e.hasLooseBVars then
@@ -92,7 +91,8 @@ private def findMatchToSplit? (env : Environment) (e : Expr) (declNames : Array
partial def splitMatch? (mvarId : MVarId) (declNames : Array Name) : MetaM (Option (List MVarId)) := commitWhenSome? do
let target mvarId.getType'
let rec go (badCases : ExprSet) : MetaM (Option (List MVarId)) := do
if let some e := findMatchToSplit? ( getEnv) target declNames badCases then
if let some e := findMatchToSplit? (eqns.deepRecursiveSplit.get ( getOptions)) ( getEnv)
target declNames badCases then
try
Meta.Split.splitMatch mvarId e
catch _ =>
@@ -102,9 +102,6 @@ partial def splitMatch? (mvarId : MVarId) (declNames : Array Name) : MetaM (Opti
return none
go {}
structure Context where
declNames : Array Name
private def lhsDependsOn (type : Expr) (fvarId : FVarId) : MetaM Bool :=
forallTelescope type fun _ type => do
if let some (_, lhs, _) matchEq? type then
@@ -228,25 +225,16 @@ private def shouldUseSimpMatch (e : Expr) : MetaM Bool := do
throwThe Unit ()
return ( (find e).run) matches .error _
partial def mkEqnTypes (tryRefl : Bool) (declNames : Array Name) (mvarId : MVarId) : MetaM (Array Expr) := do
let (_, eqnTypes) go mvarId |>.run { declNames } |>.run #[]
partial def mkEqnTypes (declNames : Array Name) (mvarId : MVarId) : MetaM (Array Expr) := do
let (_, eqnTypes) go mvarId |>.run #[]
return eqnTypes
where
go (mvarId : MVarId) : ReaderT Context (StateRefT (Array Expr) MetaM) Unit := do
go (mvarId : MVarId) : StateRefT (Array Expr) MetaM Unit := do
trace[Elab.definition.eqns] "mkEqnTypes step\n{MessageData.ofGoal mvarId}"
if tryRefl then
if ( tryURefl mvarId) then
saveEqn mvarId
return ()
if let some mvarId expandRHS? mvarId then
return ( go mvarId)
-- The following `funext?` was producing an overapplied `lhs`. Possible refinement: only do it
-- if we want to apply `splitMatch` on the body of the lambda
/- if let some mvarId ← funext? mvarId then
return (← go mvarId) -/
if ( shouldUseSimpMatch ( mvarId.getType')) then
if let some mvarId simpMatch? mvarId then
return ( go mvarId)
@@ -346,9 +334,6 @@ partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
let rec go (mvarId : MVarId) : MetaM Unit := do
if ( tryEqns mvarId) then
return ()
-- Remark: we removed funext? from `mkEqnTypes`
-- else if let some mvarId ← funext? mvarId then
-- go mvarId
if ( shouldUseSimpMatch ( mvarId.getType')) then
if let some mvarId simpMatch? mvarId then

View File

@@ -0,0 +1,108 @@
/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Meta.Tactic.Rewrite
import Lean.Meta.Tactic.Split
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Eqns
import Lean.Meta.ArgsPacker.Basic
import Init.Data.Array.Basic
namespace Lean.Elab.Nonrec
open Meta
open Eqns
/--
Simple, coarse-grained equation theorem for nonrecursive definitions.
-/
private def mkSimpleEqThm (declName : Name) (suffix := Name.mkSimple unfoldThmSuffix) : MetaM (Option Name) := do
if let some (.defnInfo info) := ( getEnv).find? declName then
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
let type mkForallFVars xs ( mkEq lhs body)
let value mkLambdaFVars xs ( mkEqRefl lhs)
let name := declName ++ suffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return some name
else
return none
private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
trace[Elab.definition.eqns] "proving: {type}"
withNewMCtxDepth do
let main mkFreshExprSyntheticOpaqueMVar type
let (_, mvarId) main.mvarId!.intros
let rec go (mvarId : MVarId) : MetaM Unit := do
trace[Elab.definition.eqns] "step\n{MessageData.ofGoal mvarId}"
if withAtLeastTransparency .all (tryURefl mvarId) then
return ()
else if ( tryContradiction mvarId) then
return ()
else if let some mvarId simpMatch? mvarId then
go mvarId
else if let some mvarId simpIf? mvarId then
go mvarId
else if let some mvarId whnfReducibleLHS? mvarId then
go mvarId
else match ( simpTargetStar mvarId { config.dsimp := false } (simprocs := {})).1 with
| TacticResultCNM.closed => return ()
| TacticResultCNM.modified mvarId => go mvarId
| TacticResultCNM.noChange =>
if let some mvarIds casesOnStuckLHS? mvarId then
mvarIds.forM go
else if let some mvarIds splitTarget? mvarId then
mvarIds.forM go
else
throwError "failed to generate equational theorem for '{declName}'\n{MessageData.ofGoal mvarId}"
-- Try rfl before deltaLHS to avoid `id` checkpoints in the proof, which would make
-- the lemma ineligible for dsimp
unless withAtLeastTransparency .all (tryURefl mvarId) do
go ( deltaLHS mvarId)
instantiateMVars main
def mkEqns (declName : Name) (info : DefinitionVal) : MetaM (Array Name) :=
withOptions (tactic.hygienic.set · false) do
let baseName := declName
let eqnTypes withNewMCtxDepth <| lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
let us := info.levelParams.map mkLevelParam
let target mkEq (mkAppN (Lean.mkConst declName us) xs) body
let goal mkFreshExprSyntheticOpaqueMVar target
withReducible do
mkEqnTypes #[] goal.mvarId!
let mut thmNames := #[]
for i in [: eqnTypes.size] do
let type := eqnTypes[i]!
trace[Elab.definition.eqns] "eqnType[{i}]: {eqnTypes[i]!}"
let name := (Name.str baseName eqnThmSuffixBase).appendIndexAfter (i+1)
thmNames := thmNames.push name
let value mkProof declName type
let (type, value) removeUnusedEqnHypotheses type value
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
}
return thmNames
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
if ( isRecursiveDefinition declName) then
return none
if let some (.defnInfo info) := ( getEnv).find? declName then
if eqns.nonrecursive.get ( getOptions) then
mkEqns declName info
else
let o mkSimpleEqThm declName
return o.map (#[·])
else
return none
builtin_initialize
registerGetEqnsFn getEqnsFor?
end Lean.Elab.Nonrec

View File

@@ -64,7 +64,7 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
let us := info.levelParams.map mkLevelParam
let target mkEq (mkAppN (Lean.mkConst info.declName us) xs) body
let goal mkFreshExprSyntheticOpaqueMVar target
mkEqnTypes (tryRefl := true) info.declNames goal.mvarId!
mkEqnTypes info.declNames goal.mvarId!
let baseName := info.declName
let mut thmNames := #[]
for i in [: eqnTypes.size] do

View File

@@ -65,7 +65,7 @@ where
go (fvars : Array Expr) (vals : Array Expr) : M α := do
if !(vals.all fun val => val.isLambda) then
k fvars vals
else if !( vals.allM fun val => return val.bindingName! == vals[0]!.bindingName! && val.binderInfo == vals[0]!.binderInfo && ( isDefEq val.bindingDomain! vals[0]!.bindingDomain!)) then
else if !( vals.allM fun val=> isDefEq val.bindingDomain! vals[0]!.bindingDomain!) then
k fvars vals
else
withLocalDecl vals[0]!.bindingName! vals[0]!.binderInfo vals[0]!.bindingDomain! fun x =>
@@ -193,6 +193,7 @@ def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Opti
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
addSmartUnfoldingDef preDef recArgPos
markAsRecursive preDef.declName
generateEagerEqns preDef.declName
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation

View File

@@ -9,17 +9,17 @@ import Lean.Elab.Tactic.Basic
namespace Lean.Elab.WF
register_builtin_option cleanDecreasingByGoal : Bool := {
defValue := true
descr := "Cleans up internal implementation details in the proof goals presented by \
`decreasing_by`, using the `clean_wf` tactic. Can be disabled for debugging \
purposes. Please report an issue if you have to disable this option."
register_builtin_option debug.rawDecreasingByGoal : Bool := {
defValue := false
descr := "Shows the raw `decreasing_by` goal including internal implementation detail \
intead of cleaning it up with the `clean_wf` tactic. Can be enabled for debugging \
purposes. Please report an issue if you have to use this option for other reasons."
}
open Lean Elab Tactic
def applyCleanWfTactic : TacticM Unit := do
if cleanDecreasingByGoal.get ( getOptions) then
unless debug.rawDecreasingByGoal.get ( getOptions) do
Tactic.evalTactic ( `(tactic| all_goals clean_wf))
end Lean.Elab.WF

View File

@@ -81,7 +81,7 @@ def mkEqns (declName : Name) (info : EqnInfo) : MetaM (Array Name) :=
let target mkEq (mkAppN (Lean.mkConst declName us) xs) body
let goal mkFreshExprSyntheticOpaqueMVar target
withReducible do
mkEqnTypes (tryRefl := false) info.declNames goal.mvarId!
mkEqnTypes info.declNames goal.mvarId!
let mut thmNames := #[]
for i in [: eqnTypes.size] do
let type := eqnTypes[i]!

View File

@@ -36,7 +36,7 @@ where
go (fvars : Array Expr) (vals : Array Expr) : TermElabM α := do
if !(vals.all fun val => val.isLambda) then
k fvars vals
else if !( vals.allM fun val => return val.bindingName! == vals[0]!.bindingName! && val.binderInfo == vals[0]!.binderInfo && ( isDefEq val.bindingDomain! vals[0]!.bindingDomain!)) then
else if !( vals.allM fun val => isDefEq val.bindingDomain! vals[0]!.bindingDomain!) then
k fvars vals
else
withLocalDecl vals[0]!.bindingName! vals[0]!.binderInfo vals[0]!.bindingDomain! fun x =>
@@ -145,6 +145,7 @@ def wfRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option Termi
registerEqnsInfo preDefs preDefNonRec.declName fixedPrefixSize argsPacker
for preDef in preDefs do
markAsRecursive preDef.declName
generateEagerEqns preDef.declName
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
-- Unless the user asks for something else, mark the definition as irreducible
unless preDef.modifiers.attrs.any fun a =>

View File

@@ -134,7 +134,7 @@ private def printAxiomsOf (constName : Name) : CommandElabM Unit := do
| _ => throwUnsupportedSyntax
private def printEqnsOf (constName : Name) : CommandElabM Unit := do
let some eqns liftTermElabM <| Meta.getEqnsFor? constName (nonRec := true) |
let some eqns liftTermElabM <| Meta.getEqnsFor? constName |
logInfo m!"'{constName}' does not have equations"
let mut m := m!"equations:"
for eq in eqns do

View File

@@ -42,3 +42,4 @@ import Lean.Elab.Tactic.Rfl
import Lean.Elab.Tactic.Rewrites
import Lean.Elab.Tactic.DiscrTreeKey
import Lean.Elab.Tactic.BVDecide
import Lean.Elab.Tactic.BoolToPropSimps

View File

@@ -4,10 +4,75 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
prelude
import Lean.Elab.Tactic.BVDecide.Bitblast
import Lean.Elab.Tactic.BVDecide.LRAT
import Lean.Elab.Tactic.BVDecide.External
import Lean.Elab.Tactic.BVDecide.Frontend
/-!
This directory implements the `bv_decide` tactic as a verified bitblaster with subterm sharing.
It makes use of proof by reflection and `ofReduceBool`, thus adding the Lean compiler to the trusted
code base.
This directory offers three different SAT tactics for proving goals involving `BitVec` and `Bool`:
1. `bv_decide` takes the goal, hands it over to a SAT solver and verifies the generated LRAT
UNSAT proof to prove the goal.
2. `bv_check file.lrat` can prove the same things as `bv_decide`. However instead of
dynamically handing the goal to a SAT solver to obtain an LRAT proof, the LRAT proof is read from
`file.lrat`. This allows users that do not have a SAT solver installed to verify proofs.
3. `bv_decide?` offers a code action to turn a `bv_decide` invocation automatically into a
`bv_check` one.
There are also some options to influence the behavior of `bv_decide` and friends:
- `sat.solver`: the name of the SAT solver used by `bv_decide`. It goes through 3 steps to determine
which solver to use:
1. If sat.solver is set to something != "" it will use that.
2. If sat.solver is set to "" it will check if there is a cadical binary next to the executing
program. Usually that program is going to be `lean` itself and we do ship a `cadical` next to it.
3. If that does not succeed try to call `cadical` from PATH.
- `sat.timeout`: The timeout for waiting for the SAT solver in seconds, default 10.
- `sat.trimProofs`: Whether to run the trimming algorithm on LRAT proofs, default true.
- `sat.binaryProofs`: Whether to use the binary LRAT proof format, default true.
- `trace.Meta.Tactic.bv` and `trace.Meta.Tactic.sat` for inspecting the inner workings of `bv_decide`.
- `debug.skipKernelTC`: may be set to true to disable actually checking the LRAT proof.
`bv_decide` will still run bitblasting + SAT solving so this option essentially trusts the SAT
solver.
## Architecture
`bv_decide` roughly runs through the following steps:
1. Apply `false_or_by_contra` to start a proof by contradiction.
2. Apply the `bv_normalize` and `seval` simp set to all hypotheses. This has two effects:
1. It applies a subset of the rewrite rules from [Bitwuzla](https://github.com/bitwuzla/bitwuzla)
for simplification of the expressions.
2. It turns all hypotheses that might be of interest for the remainder of the tactic into the form
`x = true` where `x` is a mixture of `Bool` and fixed width `BitVec` expressions.
3. Use proof by reflection to reduce the proof to showing that an SMTLIB-syntax-like value that
represents the conjunction of all relevant assumptions is UNSAT.
4. Use a verified bitblasting algorithm to turn that expression into an AIG.
The bitblasting algorithms are collected from various other bitblasters, including Bitwuzla and
Z3 and verified using Lean's `BitVec` theory.
5. Turn the AIG into a CNF.
6. Run CaDiCal on the CNF to obtain an LRAT proof that the CNF is UNSAT. If CaDiCal returns SAT
instead the tactic aborts here and presents a counterexample.
7. Use an LRAT checker with a soundness proof in Lean to show that the LRAT proof is correct.
8. Chain all the proofs so far to demonstrate that the original goal holds.
## Axioms
`bv_decide` makes use of proof by reflection and `ofReduceBool`, thus adding the Lean compiler to
the trusted code base.
## Adding a new primitive
`bv_decide` knows two kinds of primitives:
1. The ones that can be reduced to already existing ones.
2. The ones that cannot.
For the first kind the steps to adding them are very simple, go to `Std.Tactic.BVDecide.Normalize`
and add the reduction lemma into the `bv_normalize` simp set. Don't forget to add a test!
For the second kind more steps are involved:
1. Add a new constructor to `BVExpr`/`BVPred`
2. Add a bitblasting algorithm for the new constructor to `Std.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl`.
3. Verify that algorithm in `Std.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas`.
4. Integrate it with either the expression or predicate bitblaster and use the proof above to verify it.
5. Add simplification lemmas for the primitive to `bv_normalize` in `Std.Tactic.BVDecide.Normalize`.
If there are mutliple ways to write the primitive (e.g. with TC based notation and without) you
should normalize for one notation here.
6. Add the reflection code to `Lean.Elab.Tactic.BVDecide.Frontend.BVDecide`
7. Add a test!
-/

View File

@@ -1,26 +0,0 @@
/-
Copyright (c) 2024 FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
prelude
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Add
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Append
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Eq
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Extract
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.GetLsb
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Mul
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Not
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Replicate
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.RotateLeft
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.RotateRight
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.ShiftLeft
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.ShiftRight
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.SignExtend
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.Ult
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Impl.Operations.ZeroExtend
/-!
This directory contains the implementations of bitblasters for all basic operations on `BVExpr`
and `BVPred`.
-/

View File

@@ -1,26 +0,0 @@
/-
Copyright (c) 2024 FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henrik Böving
-/
prelude
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Add
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Append
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Eq
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Extract
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.GetLsb
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Mul
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Not
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Replicate
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.RotateLeft
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.RotateRight
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.ShiftLeft
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.ShiftRight
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.SignExtend
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.Ult
import Lean.Elab.Tactic.BVDecide.Bitblast.BVExpr.Circuit.Lemmas.Operations.ZeroExtend
/-!
This directory contains the verification of bitblasters for all basic operations on `BVExpr` and
`BVPred`.
-/

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