Compare commits

..

247 Commits

Author SHA1 Message Date
Kim Morrison
0432399d5b Merge remote-tracking branch 'origin/master' into array_swap_docstring 2024-07-31 13:31:24 +10:00
Leonardo de Moura
afe0b5a013 perf: precise cache for foldConsts (#4871)
It addresses a performance issue at
https://github.com/leanprover/LNSym/blob/proof_size_expt/Proofs/SHA512/Experiments/Sym20.lean
2024-07-30 18:35:45 +00:00
Kim Morrison
3841caa192 chore: correct doc-string for Array.swap! 2024-07-30 22:38:08 +10:00
Kim Morrison
90dab5e267 chore: fix naming of List.Subset lemmas (#4868) 2024-07-30 09:21:23 +00:00
Kim Morrison
6a904f2c85 chore: upstream List.eraseIdx lemmas (#4865) 2024-07-30 06:59:45 +00:00
Kim Morrison
2c396d6424 chore: upstream List.pairwise_iff_getElem (#4866) 2024-07-30 06:55:29 +00:00
Kim Morrison
69f86d6478 chore: split Init.Data.List.Lemmas (#4863)
Init.Data.List.Lemmas had reached 5000 lines: splitting into
function-specific files.
2024-07-30 03:17:34 +00:00
Joachim Breitner
4ea55687a5 refactor: deriving DecidableEq to use termination_by structural (#4826)
now that we support structural mutual recursion, I expect that every
`DecidableEq` instance be implemented using structural recursion, so
let's be explicit about it.
2024-07-29 21:24:05 +00:00
Kyle Miller
69c71f6476 fix: make elabAsElim aware of explicit motive arguments (#4817)
Some eliminators (such as `False.rec`) have an explicit motive argument.
The `elabAsElim` elaborator assumed that all motives are implicit.

If the explicit motive argument is `_`, then it uses the elab-as-elim
procedure, and otherwise it falls back to the standard app elaborator.

Furthermore, if an explicit elaborator is not provided, it falls back to
treating the elaborator as being implicit, which is convenient for
writing `h.rec` rather than `h.rec _`. Rationale: for `False.rec`, this
simulates it having an implicit motive, and also motives are generally
not going to be available in the expected type.

Closes #4347
2024-07-29 19:18:47 +00:00
Kyle Miller
7f128b39e7 feat: more than one optional argument can be omitted while pretty printing (#4854)
Before, the delaborator was conservative about omitting optional
arguments, only omitting the very last one. Now it can omit arbitrarily
long sequences of optional arguments from the end.

For simplicity of implementation, every optional argument is delaborated
and then potentially discarded. It could save state and lazily
delaborate, but we're running under the hypothesis that most optional
arguments are for very simple values (like `true`, `false`, or a numeric
literal), so it is unlikely that efficiency gains, if any, are worth it.
In particular, in the future structure constructors will have optional
arguments, but `unexpandStructureInstance` assumes none of the optional
fields are omitted.

Closes #4812
2024-07-29 19:02:39 +00:00
Kim Morrison
a845a007ac chore: fix universe in PSigma.exists (#4862) 2024-07-29 12:30:31 +00:00
Sebastian Ullrich
abf4206e9c chore: CI: fix msys2 2024-07-29 10:25:59 +02:00
Kim Morrison
83ad82162f feat: upstream more List lemmas (#4856) 2024-07-28 23:23:59 +00:00
arthur-adjedj
93ac635a89 chore: fix parenthesizing in test 2024-07-29 08:58:49 +10:00
Kim Morrison
642c28cdbb chore: update stage0 2024-07-29 08:58:49 +10:00
arthur-adjedj
a04f3cab5a fix: reduction behaviour of derived BEq instances
fix: forgot an assignation
2024-07-29 08:58:49 +10:00
arthur-adjedj
86af04cc08 fix: handle dependent fields when deriving BEq 2024-07-29 08:58:49 +10:00
arthur-adjedj
7253ef8751 chore: inverse rhs construction order in BEq's handler 2024-07-29 08:58:49 +10:00
Joachim Breitner
f830fc9f4d refactor: IndPredBelow: use apply_assumption (#4841)
when transforming the `match` statements in `IndPredBelow`, given a
local variable `x : T`, we need to search for `hx : T.below x`.
Previously this was done using the custom `backwardsChaining` method,
although my hypothesis is that we don’t need to chain anything here, and
can use `apply_assumption`.
2024-07-28 17:22:54 +00:00
Joachim Breitner
671ce7afd3 fix: IndPred: track function's motive in a let binding, use withoutProofIrrelevance, no chaining (#4839)
this improves support for structural recursion over inductive
*predicates* when there are reflexive arguments.

Consider
```lean
inductive F: Prop where
  | base
  | step (fn: Nat → F)

-- set_option trace.Meta.IndPredBelow.search true
set_option pp.proofs true

def F.asdf1 : (f : F) → True
  | base => trivial
  | step f => F.asdf1 (f 0)
termination_by structural f => f`
```

Previously the search for the right induction hypothesis would fail with
```
could not solve using backwards chaining x✝¹ : F
x✝ : x✝¹.below
f : Nat → F
a✝¹ : ∀ (a : Nat), (f a).below
a✝ : Nat → True
⊢ True
```

The backchaining process will try to use `a✝ : Nat → True`, but then has
no idea what to use for `Nat`.

There are three steps here to fix this.

1. We let-bind the function's type before the whole process. Now the
   goal is

   ```
   funType : F → Prop := fun x => True
   x✝ : x✝¹.below
   f : Nat → F
   a✝¹ : ∀ (a : Nat), (f a).below
   a✝ : ∀ (a : Nat), funType (f a)
   ⊢ funType (f 0)
   ```
2. Instead of using the general purpose backchaining proof search, which
is more
powerful than we need here (we need on recursive search and no
backtracking),
   we have a custom search that looks for local assumptions that 
   provide evidence of `funType`, and extracts the arguments from that
   “type” application to construct the recursive call.

   Above, it will thus unify `f a =?= f 0`.

3. In order to make progress here, we also turn on use
`withoutProofIrrelevance`,
because else `isDefEq` is happy to say “they are equal” without actually
looking
   at the terms and thus assigning `?a := 0`.

This idea of let-binding the function's motive may also be useful for
the other recursion compilers, as it may simplify the FunInd
construction. This is to be investigated.

fixes #4751
2024-07-28 17:22:27 +00:00
Kim Morrison
87c92a3f87 feat: upstream more List operations (#4855)
Upstreams `tail`, `findIdx`, `indexOf`, `countP`, `count`, `range'` from
Batteries.
2024-07-28 04:52:21 +00:00
Ikko Eltociear Ashimine
15bf41cd67 chore: update Topological.lean (#4853)
minor fix
2024-07-27 18:14:42 +00:00
Kyle Miller
906bc583c5 fix: handle unimported builtin names for location links (#4780)
The function `locationLinksFromDecl` could throw an error if the name it
is provided doesn't exist in the environment, which is possible if for
example an elaborator is a builtin.

Closes #3789
2024-07-27 17:39:39 +00:00
Kim Morrison
ea43ebd54a chore: cleanups for Mathlib.Init (#4852)
It is convenient to adjust some attributes here, to allow easier cleanup
in `Mathlib.Init`.
2024-07-27 07:37:17 +00:00
Siddharth
bb9c9bd99f feat: bitVec shiftLeft recurrences for bitblasting (#4571)
```lean 
@[simp]
theorem shiftLeftRec_zero (x : BitVec w₁) (y : BitVec w₂) :
    shiftLeftRec x y 0 = x <<< (y &&& twoPow w₂ 0)  := by
  simp [shiftLeftRec]

@[simp]
theorem shiftLeftRec_succ (x : BitVec w₁) (y : BitVec w₂) :
    shiftLeftRec x y (n + 1) =
      (shiftLeftRec x y n) <<< (y &&& twoPow w₂ (n + 1)) := by
  simp [shiftLeftRec]

theorem shiftLeftRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) (hn : n + 1 ≤ w₂) :
  shiftLeftRec x y n = x <<< (y.truncate (n + 1)).zeroExtend w₂ := by
```

These theorems are used for bitblasting shiftLeft in LeanSAT.

---------

Co-authored-by: Alex Keizer <alex@keizer.dev>
Co-authored-by: Kim Morrison <scott@tqft.net>
Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-27 06:36:52 +00:00
Mac Malone
fe5894f2f6 feat: lake: CLI options to control output & failure log levels (#4847)
Adds the `--log-level=<lv>` CLI option for controlling the minimum log
level Lake should output. For instance, `--log-level=error` will only
print errors (not warnings or info).

Also, adds the parallel `--fail-level` CLI option to control what the
minimum log level of build failures is. The existing `--iofail` and
`--wfail` options are equivalent to `--fail-level=info` and
`--fail-level=warning` , respectively.

Closes #4805,
2024-07-27 06:16:59 +00:00
Mac Malone
3ecbf4ae2d doc: lake: require @ git in README (#4849)
Demonstrates `require @ git` in Lake's README and tweaks related
documentation.
2024-07-27 02:44:50 +00:00
Sebastian Ullrich
9b342efb84 fix: calling programs with spaces on Windows (#4515)
This entire API has been carefully optimized for maximum pain output

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2024-07-26 17:35:05 +00:00
Sebastian Ullrich
c02aa98c6a doc: triage 2024-07-26 18:24:06 +02:00
Kim Morrison
18ba5f24e1 chore: correct List.Subset lemma names (#4843) 2024-07-26 11:36:28 +00:00
Kim Morrison
cbe39dc4bb chore: fix List deprecations (#4842) 2024-07-26 11:32:18 +00:00
Joachim Breitner
a5b8d5b486 chore: upon nightly release, trigger nightly_bump_toolchain on mathlib4 (#4838)
as discussed at
https://leanprover.zulipchat.com/#narrow/stream/428973-nightly-testing/topic/Bumping.20more.20often/near/453976634
2024-07-26 06:51:19 +00:00
Kim Morrison
895391b73f feat: List.IsPrefix/IsSuffix is decidable (#4837) 2024-07-26 05:17:41 +00:00
Kim Morrison
e280de00b6 feat: gaps/cleanup in List lemmas (#4835) 2024-07-26 05:00:50 +00:00
Kim Morrison
8c87a90cea chore: upstream IsPrefix/IsSuffix/IsInfix (#4836)
Further lemmas to follow; this is the basic material from Batteries.
2024-07-26 04:35:36 +00:00
Joachim Breitner
54c22efca1 fix: structural recursion: do not check for brecOn too early (#4831)
Due to nested recursion, we do two passes of `getRecArgInfo`: One on
each argument in isolation, to see which inductive types are around
(e.g. `Tree` and `List`), and
then we later refine/replace this result with the data for the nested
type former (the implicit `ListTree`).

If we have nested recursion through a non-recursive data type like
`Array` or `Prod` then arguemnts of these types should survive the first
phase, so that we can still use them when looking for, say, `Array
Tree`.

This was helpfully reported by @arthur-adjedj.
2024-07-25 15:25:34 +00:00
Joachim Breitner
d4f2db9559 chore: report github actions failure on zulip (#4830)
only the master branch
2024-07-25 11:15:33 +00:00
Joachim Breitner
39e0b41fe1 test: make #1697 test case Linux-Debug safe (#4829) 2024-07-25 10:26:01 +00:00
Marc Huisinga
84f8871c3f fix: filter duplicate subexpressions (#4786)
For every parenthesized expression `(foo)`, the InfoView produces an
interactive component both for `(foo)` itself and its subexpression
`foo` because the corresponding `TaggedText` in the language server is
duplicated as well. Both of these subexpressions have the same
subexpression position and so they are identical w.r.t. interactive
features.

Removing this duplication would help reduce the size of the DOM of the
InfoView and ensure that the UI for InfoView features is consistent for
`(foo)` and `foo` (e.g. hovers would always highlight `(foo)`, not
either `(foo)` or `foo` depending on whether the mouse cursor is on the
bracket or not). It would also help resolve a bug where selecting a
subexpression will yield selection highlighting both for `(foo)` and
`foo`, as we use the subexpression position to identify which terms to
highlight.

This PR adjusts the parenthesizer to move the corresponding info instead
of duplicating it.
2024-07-25 08:58:49 +00:00
Austin Letson
93fa9c8837 feat: create ci workflow on lake new/init (#4608)
Draft of adding ci workflow using lean-action on `lake new/init`

This PR is currently missing lake options for the user to control this
feature.

Closes #4606

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2024-07-25 01:23:54 +00:00
Sebastian Ullrich
0768ad4eb9 chore: CI: Jira sync 2024-07-24 19:52:55 +02:00
Kyle Miller
c545e7b0c9 fix: make sure anonymous dot notation works with pi-type-valued type synonyms (#4818)
When resolving anonymous dot notation (`.ident x y z`), it would reduce
the expected type to whnf. Now, it unfolds definitions step-by-step,
even if the type synonym is for a pi type like so
```lean
def Foo : Prop := ∀ a : Nat, a = a
protected theorem Foo.intro : Foo := sorry
example : Foo := .intro
```

Closes #4761
2024-07-24 17:09:42 +00:00
Sebastian Ullrich
7b3c64fc85 feat: trailing whitespace changes should not invalidate imports (#4580)
Thus, starting to type the first declaration after the imports should
not make them reload
2024-07-24 13:08:01 +00:00
Sebastian Ullrich
af0b563099 feat: respond to info view requests as soon as relevant tactic has finished execution (#4727)
After each tactic step, we save the info tree created by it together
with an appropriate info tree context that makes it stand-alone (which
we already did before to some degree, see `Info.updateContext?`). Then,
in the adjusted request handlers, we first search for a snapshot task
containing the required position, if so wait on it, and if it yielded an
info tree, use it to answer the request, or else continue searching and
waiting, falling back to the full info tree, which should be unchanged
by this PR.

The definition header does *not* report info trees early as in general
it is not stand-alone in the tactic sense but may contain e.g.
metavariables solved by the body in which case we do want to show the
ultimate state as before. This could be refined in the future in case
there are no unsolved mvars.

The adjusted request handlers are exactly the ones waited on together by
the info view, so they all have to be adjusted to have any effect on the
UX. Further request handlers may be adjusted in the future.

No new tests as "replies early" is not something we can test with our
current framework but the existing test suite did help in uncovering
functional regressions.
2024-07-24 13:02:13 +00:00
Sebastian Ullrich
af40e61811 chore: typo 2024-07-24 15:11:54 +02:00
Kim Morrison
1758b37a71 chore: List.filterMapM runs and returns left-to-right (#4820)
Closes #4676. Previously `List.filterMapM` was returning results
left-to-right, but evaluating right-to-left.
2024-07-24 09:00:10 +00:00
Joachim Breitner
3701bee777 test: test case for #4751 (#4819)
and tracing for `IndPredBelow.backwardsChaining`.
2024-07-24 08:14:25 +00:00
Sebastian Ullrich
6d971827e2 chore: CI: add back dropped check-stage3 2024-07-24 09:03:11 +02:00
Joachim Breitner
871c9b4164 test: update test output following stage0 update (#4815)
this is a consequenc of #4807 that only shows up once that change made
it to stage0, it seem.
2024-07-23 21:43:38 +00:00
Lean stage0 autoupdater
ee6737ab4d chore: update stage0 2024-07-23 16:14:57 +00:00
Joachim Breitner
7d60d8b563 feat: safer #eval, and #eval! (#4810)
previously, `#eval` would happily evaluate expressions that contain
`sorry`, either explicitly or because of failing tactics. In conjunction
with operations like array access this can lead to the lean process
crashing, which isn't particularly great.

So how `#eval` will refuse to run code that (transitively) depends on
the `sorry` axiom (using the same code as `#print axioms`).

If the user really wants to run it, they can use `#eval!`.

Closes #1697
2024-07-23 15:26:56 +00:00
Joachim Breitner
a4673e20a5 chore: release notes for mutual structural induction (#4808) 2024-07-23 07:40:29 +00:00
Marc Huisinga
b2ee8c240d doc: update quickstart guide (#4806)
This PR updates the screenshots and instructions in the quickstart guide
for the most recent Lean 4 VS Code extension version and makes a small
stylistic change suggested by @semorrison.
2024-07-23 07:31:21 +00:00
Markus Himmel
5d632a97b8 feat: more hash map lemmas (#4803) 2024-07-23 06:57:44 +00:00
Kyle Miller
5938dbbd14 fix: make elab_as_elim eagerly elaborate arguments for parameters appearing in the types of targets (#4800)
The `elab_as_elim` elaborator eagerly elaborates arguments that can help
with elaborating the motive, however it does not include the transitive
closure of parameters appearing in types of parameters appearing in ...
types of targets.

This leads to counter-intuitive behavior where arguments supplied to the
eliminator may unexpectedly have postponed elaboration, causing motives
to be type incorrect for under-applied eliminators such as the
following:

```lean
class IsEmpty (α : Sort u) : Prop where
  protected false : α → False

@[elab_as_elim]
def isEmptyElim [IsEmpty α] {p : α → Sort _} (a : α) : p a :=
  (IsEmpty.false a).elim

example {α : Type _} [IsEmpty α] :
  id (α → False) := isEmptyElim (α := α)
```

The issue is that when `isEmptyElim (α := α)` is computing its motive,
the value of the postponed argument `α` is still an unassignable
metavariable. With this PR, this argument is now among those that are
eagerly elaborated since it appears as the type of the target `a`.

This PR also contains some other fixes:
* When underapplied, does unification when instantiating foralls in the
expected type.
* When overapplied, type checks the generalized-and-reverted expected
type.
* When collecting targets, collects them in the correct order.

Adds trace class `trace.Elab.app.elab_as_elim`.

This is a followup to #4722, which added motive type checking but
exposed the eagerness issue.
2024-07-22 23:23:28 +00:00
grunweg
852add3e55 doc: document Command.Scope (#4748)
Also extends existing definition for `getScope`/`getScopes` and
clarifies that the `end` command is optional at the end of a file.

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
2024-07-22 21:55:37 +00:00
Joachim Breitner
20c857147c feat: unnecessary termination_by clauses cause warnings, not errors (#4809)
fixes #4804
2024-07-22 20:52:14 +00:00
Joachim Breitner
9f1eb479b0 feat: functional induction for mutual structural recursion (#4772) 2024-07-22 15:10:11 +00:00
Markus Himmel
92cca5ed1b chore: remove bif from hash map lemmas (#4791)
The original idea was to use `bif` in computation contexts and `if` in
propositional contexts, but this turned out to be really inconvenient in
practice.
2024-07-22 14:39:00 +00:00
Joachim Breitner
3a4d2cded3 refactor: Introduce PProdN module (#4807)
code to create nested `PProd`s, and project out, and related functions
were scattered in variuos places. This unifies them in
`Lean.Meta.PProdN`.

It also consistently avoids the terminal `True` or `PUnit`, for slightly
easier to read constructions.
2024-07-22 11:56:50 +00:00
Joachim Breitner
22ae04f3e7 refactor: FunInd overhaul (#4789)
This refactoring PR changes the structure of the `FunInd` module, with
the main purpose to make it easier to support mutual structural
recursion.

In particular the recursive calls are now longer recognized by their
terms (simple for well-founded recursion, `.app oldIH [arg, proof]`, but
tedious for structural recursion and even more so for mutual structural
recursion), but the type after replacing `oldIH` with `newIH`, where the
type will be simply and plainly `mkAppN motive args`).

We also no longer try to guess whether we deal with well-founded or
structural recursion but instead rely on the `EqnInfo` environment
extensions. The previous code tried to handle both variants, but they
differ too much, so having separate top-level functions is easier.

This also fuses the `foldCalls` and `collectIHs` traversals and
introduces a suitable monad for collecting the inductive hypotheses.
2024-07-21 14:46:52 +00:00
François G. Dorais
99f362979b feat: use usize for array types (#4802)
This is part 2 of 2 of #4801 (which closes #4654). That PR was split in
two to allow a stage0 update between declaring the `usize` functions and
using them where they are needed.
2024-07-21 12:26:04 +00:00
Lean stage0 autoupdater
3a309f7691 chore: update stage0 2024-07-21 11:05:36 +00:00
François G. Dorais
8f0631ab1f feat: usize for array types (#4801)
Add efficient `usize` functions for `Array`, `ByteArray`, `FloatArray`.

This is part 1 of 2 since there is a need to update stage0 between the
two parts. (See discussion below.)

Closes #4654
2024-07-21 10:23:49 +00:00
Markus Himmel
08acf5a136 fix: remove typeclass assumptions for Nodup.eraseP (#4790)
The `α` in the typeclass assumptions wasn't even the element type of the
list.
2024-07-21 07:51:42 +00:00
Markus Himmel
bdfaa00b1e doc: remove reference to HashMap.find? from Option docstring (#4782) 2024-07-21 07:51:06 +00:00
Leonardo de Moura
8ceb24a5e6 perf: Expr.replace (#4799)
use the kernel implementation.
2024-07-20 04:53:43 +00:00
Lean stage0 autoupdater
201749ccac chore: update stage0 2024-07-20 04:31:11 +00:00
Leonardo de Moura
d0bc4e4245 fix: replace_fn.cpp (#4798) 2024-07-19 21:20:43 -07:00
Lean stage0 autoupdater
c2117d75a6 chore: update stage0 2024-07-20 03:58:16 +00:00
Leonardo de Moura
3477b0e7f6 fix: for_each_fn.cpp (#4797) 2024-07-20 03:22:56 +00:00
Lean stage0 autoupdater
696f70bb4e chore: update stage0 2024-07-20 02:35:13 +00:00
Leonardo de Moura
726e162527 perf: kernel replace with precise cache (#4796)
Changes:

- We avoid the thread local storage.
- We use a hash map to ensure that cached values are not lost.
- We remove `check_system`. If this becomes an issue in the future we
should precompute the remaining amount of stack space, and use a cheaper
check.
- We add a `Expr.replaceImpl`, and will use it to implement
`Expr.replace` after update-stage0
2024-07-20 02:00:29 +00:00
Leonardo de Moura
de5e07c4d2 perf: find? and findExt? (#4795)
use the kernel implementation.
2024-07-20 01:13:54 +00:00
Lean stage0 autoupdater
327986e6fb chore: update stage0 2024-07-20 00:51:23 +00:00
Leonardo de Moura
6c33b9c57f perf: for_each with precise cache (#4794)
This commit also adds support for `find?` and `findExt?` using kernel
`for_each`.
We need to perform `update-stage0`.
2024-07-20 00:18:55 +00:00
Henrik Böving
d907771fdd feat: theory from LeanSAT (#4742)
Co-authored-by: Kim Morrison <scott.morrison@gmail.com>
2024-07-19 16:21:03 +00:00
Leonardo de Moura
5c3360200e fix: add term elaborator for Lean.Parser.Term.namedPattern (#4792)
closes #4662
2024-07-19 16:14:32 +00:00
Joachim Breitner
204d4839fa refactor: add numFixed to Structural.EqnInfo (#4788) 2024-07-19 10:21:43 +00:00
Joachim Breitner
e32f3e8140 refactor: IndGroupInst.brecOn (#4787)
this logic fits nicely within `IndGroupInst`.

Also makes `isAuxRecursorWithSuffix` recognize `brecOn_<n>`.
2024-07-19 10:20:50 +00:00
Sebastian Ullrich
7d2155943c doc: fix integer division example
Fixes #4785
2024-07-19 10:36:43 +02:00
Lean stage0 autoupdater
78c4d6daff chore: update stage0 2024-07-18 20:38:21 +00:00
Leonardo de Moura
5526ff6320 chore: Simp.Config.implicitDefEqProofs := true by default (#4784)
Motivation: unblock PR #4595
`Simp.Config.implicitDefEqProofs := false` is currently creating too
many issues in Mathlib.
2024-07-18 19:10:18 +00:00
Leonardo de Moura
bfca7ec72a fix: .eq_def theorem generation with messy universes (#4712)
closes #4673
2024-07-18 17:34:23 +00:00
Leonardo de Moura
9208b3585f chore: document replaceUnsafeM issue (#4783) 2024-07-18 16:26:20 +00:00
Leonardo de Moura
a94805ff71 perf: ensure Expr.replaceExpr preserve DAG structure in Exprs (#4779) 2024-07-18 02:24:15 +00:00
Lean stage0 autoupdater
4eb842560c chore: update stage0 2024-07-18 01:19:02 +00:00
Kyle Miller
490d16c80d fix: have elabAsElim check inferred motive for type correctness (#4722)
Declarations with `@[elab_as_elim]` could elaborate as type-incorrect
expressions. Reported by Jireh Loreaux [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/bug.20in.20revert/near/450522157).

(In principle the elabAsElim routine could revert fvars appearing in the
expected type that depend on the discriminants (if the discriminants are
fvars) to increase the likelihood of type correctness, but that's at the
cost of some complexity to both the elaborator and to the user.)
2024-07-17 20:48:03 +00:00
Leonardo de Moura
f60721bfbd feat: add some low level helper APIs (#4778) 2024-07-17 20:12:05 +00:00
Kyle Miller
a5ecdd0a17 feat: improve @[ext] error message when ext_iff generation fails (#4762)
Now it suggests using `@[ext (iff := false)]` to disable generating the
`ext_iff` lemma.

This PR also adjusts error messages and attribute documentation.
Additionally, to simplify the code now the `x` and `y` arguments can't
come in reverse order (this feature was was added in the refactor
#4543).

Closes #4758
2024-07-17 18:26:12 +00:00
Leonardo de Moura
be717f03ef fix: missing assignment validation at closeMainGoal (#4777)
This primitive is used by the `exact` tactic. This issue allowed users
to create loops in the metavariable assignment.

closes #4773
2024-07-17 18:25:02 +00:00
Leonardo de Moura
41b4914836 perf: Replacement.apply (#4776)
Avoid potentially expensive `e.replace` if it is not applicable.
2024-07-17 16:17:47 +00:00
Leonardo de Moura
933445608c chore: simplify shareCommon' (#4775) 2024-07-17 15:32:35 +00:00
Markus Himmel
8e396068e4 doc: mention linearity in hash map docstring (#4771) 2024-07-17 09:26:38 +00:00
Markus Himmel
c1df7564ce fix: resolve instances for HashMap via unification (#4759) 2024-07-17 08:02:22 +00:00
Markus Himmel
ba3565f441 chore: fix BEq argument order in hash map lemmas (#4732)
The previous argument order was a conscious choice, but I had missed
#3056.
2024-07-17 04:25:21 +00:00
Kim Morrison
af03af5037 feat: simprocs for #[1,2,3,4,5][2] (#4765)
None of these were working previously:

```
#check_simp #[1,2,3,4,5][2]  ~> 3
#check_simp #[1,2,3,4,5][2]? ~> some 3
#check_simp #[1,2,3,4,5][7]? ~> none
#check_simp #[][0]? ~> none
#check_simp #[1,2,3,4,5][2]! ~> 3
#check_simp #[1,2,3,4,5][7]! ~> (default : Nat)
#check_simp (#[] : Array Nat)[0]! ~> (default : Nat)
```
2024-07-17 03:05:17 +00:00
Leonardo de Moura
f6666fe266 chore: add missing withTraceNode (#4769)
Motivation: improve `trace.profiler`
2024-07-17 02:32:32 +00:00
Leonardo de Moura
c580684c22 perf: add ShareCommon.shareCommon' (#4767)
A more restrictive but efficient max sharing primitive.

**Motivation:** Some software verification proofs may contain
significant redundancy that can be eliminated using hash-consing (also
known as `shareCommon`). For example, [theorem
`sha512_block_armv8_test_4_sym`](460fe5d74c/Proofs/SHA512/SHA512Sym.lean (L29))
took a few seconds at [`addPreDefinitions`
](1a12f63f74/src/Lean/Elab/PreDefinition/Main.lean (L155))
and one second at `fixLevelParams` on a MacBook Pro (with M1 Pro). The
proof term initially had over 16 million subterms, but the redundancy
was indirectly and inefficiently eliminated using `Core.transform` at
`addPreDefinitions`. I tried to use `shareCommon` method to fix the
performance issue, but it was too inefficient. This PR introduces a new
`shareCommon'` method that, although less flexible (e.g., it uses only a
local cache and hash-consing table), is much more efficient. The new
procedure minimizes the number of RC operations and optimizes the
caching strategy. It is 20 times faster than the old `shareCommon`
procedure for theorem `sha512_block_armv8_test_4_sym`.
2024-07-17 01:33:54 +00:00
Joachim Breitner
1a12f63f74 refactor: move Synax.hasIdent, shake dependencies (#4766)
I noticed that a change to `Lean.PrettyPrinter.Delaborator.Builtins`
rebuilt more modules than I expected, so I moved a definition and
reduced some dependcies.

More reduction would be possible to move const-delaboration out of the
big `Lean.PrettyPrinter`, and import from `Lean.PrettyPrinter`
selectively.
2024-07-16 21:19:26 +00:00
Joachim Breitner
95b8095fa6 feat: PProd syntax (part 3) (#4756)
reworks #4730 based on feedback from @kmill:

 * Uses `×'` for PProd
 * No syntax for MProd for now
 * Angle brackets (without nesting) for the values
2024-07-16 21:06:04 +00:00
Kyle Miller
94cc8eb863 chore: add comment for why anonymous constructor notation isn't flattened during pretty printing (#4764) 2024-07-16 19:04:51 +00:00
Kim Morrison
1cf47bce5a chore: rename TC to Relation.TransGen (#4760)
This is barely used in Lean, and this rename is both more readable, and
consistent with further developments downstream.

See
[zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Relation.2ETransGen.20vs.2E.20TC.20from.20Init.2ECore/near/448941824)
discussion.
2024-07-16 17:06:49 +00:00
Leonardo de Moura
b73fe04710 feat: add Lean.Expr.numObjs (#4754)
Add helper function for computing the number of allocated
sub-expressions in a given expression. Note: Use this function primarily
for diagnosing performance issues.
2024-07-16 15:52:33 +00:00
Leonardo de Moura
f986a2e9ef chore: missing profileitM (#4753)
This PR addresses the absence of the `profileitM` function in two
auxiliary functions. The added `profileitM` instances are particularly
useful for diagnosing performance issues in declarations that contain
many repeated sub-terms.
2024-07-16 15:43:23 +00:00
Markus Himmel
1a9cbc96f1 chore: rename HashMap.remove to HashMap.erase (#4725)
The name `remove` was chosen because it is more popular in mainstream
programming languages, but being consistent with other Lean container
types (including `Lean.HashMap` and `Batteries.HashMap`) is more
important, so let's change the name while we still can.
2024-07-16 08:14:56 +00:00
Sebastian Ullrich
7aec6c9ae7 chore: temporarily remove test broken by #4746 2024-07-16 09:43:26 +02:00
Lean stage0 autoupdater
31de2494fb chore: update stage0 2024-07-15 21:53:05 +00:00
Lean stage0 autoupdater
d679591880 chore: update stage0 2024-07-15 21:29:34 +00:00
Sebastian Ullrich
f167cfba71 chore: exclude more symbols to get below Windows symbol limit 2024-07-15 23:19:04 +02:00
Joachim Breitner
180c6aaa5e feat: PProd and MProd syntax (part 2) (#4730)
the internal constructions for structural and well-founded recursion
use plenty of `PProd` and `MProd`, and reading these, deeply
nested and in prefix notation, is unnecessarily troublesome.

Therefore this introduces notations
```
a ×ₚ b   -- PProd a b
a ×ₘ b   -- MProd a b
()ₚ      -- PUnit.unit
(x,y,z)ₚ -- PProd.mk x (PProd.mk y z)
(x,y,z)ₘ -- MProd.mk x (MProd.mk y z)
```

(This is the post-stage0-part 2.)
2024-07-15 15:40:42 +00:00
Lean stage0 autoupdater
ab0241dac8 chore: update stage0 2024-07-15 15:02:32 +00:00
Joachim Breitner
dc65f03c41 feat: PProd and MProd syntax (part 1) (#4747)
the internal constructions for structural and well-founded recursion
use plenty of `PProd` and `MProd`, and reading these, deeply
nested and in prefix notation, is unnecessarily troublesome.

Therefore this introduces notations
```
a ×ₚ b   -- PProd a b
a ×ₘ b   -- MProd a b
()ₚ      -- PUnit.unit
(x,y,z)ₚ -- PProd.mk x (PProd.mk y z)
(x,y,z)ₘ -- MProd.mk x (MProd.mk y z)
```

(This is part 1, the rest will follow in #4730 after a stage0 update.)
2024-07-15 14:21:11 +00:00
Joachim Breitner
de96b6d8a7 feat: structural recursion over nested datatypes (#4733)
This now works:

```lean
inductive Tree where | node : List Tree → Tree

mutual
def Tree.size : Tree → Nat
  | node ts => list_size ts

def Tree.list_size : List Tree → Nat
  | [] => 0
  | t::ts => t.size + list_size ts
end
```

It is still out of scope to expect to be able to use nested recursion
(e.g. through `List.map` or `List.foldl`) here.

Depends on #4718.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-15 11:49:53 +00:00
Joachim Breitner
3ab2c714ec feat: infer mutual structural recursion (#4718)
the support for mutual structural recursion (new since #4575) is
extended so that Lean tries to infer it even without annotations.

* The error message when termination checking fails looks quite
different now. Maybe a bit better, maybe with more room for
improvements.
* If there are too many combinations (with an arbitrary cut-off) for a
given argument type, it will just give up and ask the user to use
`termination_by structural`.
* It is now legal to specify `termination_by structural` on not
necessarily all functions of a clique; this simply restricts the
combinations of arguments that Lean considers.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-15 09:34:06 +00:00
Joachim Breitner
f99427bd1a test: extend test for #4671 with nice example reported on zulip (#4740)
from
<https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.E2.9C.94.20Doubly-nested.20inductive/near/451204850>

I really like when I can respond to bug report with “will fix in -1
weeks”
2024-07-13 22:22:40 +00:00
Joachim Breitner
1118978cbb refactor: IndGroupInfo and IndGroupInst (#4738)
This adds the types
* `IndGroupInfo`, a variant of `InductiveVal` with information that
   applies to a whole group of mutual inductives and
* `IndGroupInst` which extends `IndGroupInfo` with levels and parameters
   to indicate a instantiation of the group.

One purpose of this abstraction is to make it clear when a fuction
operates on a group as a whole, rather than a specific inductive within
the group.

This is extracted from #4718 and #4733 to reduce PR size and improve
bisectability.
2024-07-13 08:30:09 +00:00
James Sully
4ea8c5ad8d doc: fix misplaced docstring for getThe (#4737) 2024-07-13 08:10:05 +00:00
Mac Malone
a6ae49c3ab feat: lake: cleaner release handling & related touchups (#4735)
Improves a number of elements related to Git checkouts, cloud releases,
and related error handling.

* On error, Lake now prints all top-level logs. Top-level logs are those
produced by Lake outside of the job monitor (e.g., when cloning
dependencies).
* When fetching a remote for a dependency, Lake now forcibly fetches
tags. This prevents potential errors caused by a repository recreating
tags already fetched.
* Tweaked Git error handling to hopefully be more informative.
* The builtin package facets `release`, `optRelease`, `extraDep` are now
caption in the same manner as other facets. Previously, they were
attempting to be too clever.
* `afterReleaseSync` and `afterReleaseAsync` now fetch `optRelease`
rather than `release`.
* Added support for optional jobs, whose failure does not cause the
whole build to failure (and made `optRelease` such a job).

Closes #4302.
2024-07-13 01:10:41 +00:00
Joachim Breitner
2ad6d397f8 refactor: use indVal.numNested or indVal.numTypeFormers where applicable (#4734)
follow-up to #4684
2024-07-12 22:07:25 +00:00
Joachim Breitner
891824bc51 feat: .below and .brecOn for nested inductive (#4658)
We now get `.below` and `.brecOn` definitions for nested inductives.

No surprises in the implementation: the kernel already gives us suitable
`.rec_1` etc. recursors, and our construction follows the structure of
this recursor.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-12 21:26:35 +00:00
Matthew Robert Ballard
f35c562ef8 feat: add #discr_tree_key command and discr_tree_key tactic (#4447)
Adds a command and tactic to print the `Array <| DiscrTree.Key` for
equalities helping the user to debug perceived `simp` failures.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-07-12 15:05:10 +00:00
Joachim Breitner
bcd8517307 feat: Meta.withErasedFVars (#4731)
this idiom shows up multiple times, is non-trivial (in the sense that
the `localInsts` has to be updated, and I am about to use it once more.
Hence time to abstract this out.
2024-07-12 14:58:04 +00:00
Kyle Miller
ce73bbe277 feat: detailed feedback on decide tactic failure (#4674)
When the `decide` tactic fails, it can try to give hints about the
failure:
- It tells you which `Decidable` instances it unfolded, by making use of
the diagnostics feature.
- If it encounters `Eq.rec`, it gives you a hint that one of these
instances was likely defined using tactics.
- If it encounters `Classical.choice`, it hints that you might have
classical instances in scope.
- During this, it tries to process `Decidable.rec`s and matchers to pin
blame on a particular instance that failed to reduce.

This idea comes from discussion with Heather Macbeth [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Decidable.20with.20structures/near/449409870).
2024-07-11 20:08:29 +00:00
Joachim Breitner
f0eab4b7b1 fix: nested structural recursion over reflexive data type (#4728)
this code
```
inductive N where
 | cons : (Nat -> N) -> N

mutual
def f : N -> Nat
 | .cons a => g (a 32) + 1
termination_by structural n => n
def g : N -> Nat
 | .cons a => f (a 42) + 1
termination_by structural  n => n
end
```
would break. When searching for the right `belowDict` we now have to,
evne after instantiating the paramters for a reflexive argument, again
search through a bunch of `PProd`s.

(Instead of searching we could pass down the index, but since we are
searching anyways in this function let's just re-use.)

Fixes: #4726
2024-07-11 15:25:48 +00:00
Kyle Miller
5f70c1ca64 fix: make matcher pretty printer sensitive to pp.explicit (#4724)
Matchers usually have implicit arguments, and even if they don't the
notation hides the name of the matcher function.

Now when hovering over `match` expressions you can see the actual
underlying matcher expression.
2024-07-11 01:49:49 +00:00
Kim Morrison
fce82eba40 feat: further theorems for List.erase (#4723) 2024-07-10 22:19:12 +00:00
grunweg
9d14e4423c chore: fix typo in doc-string (#4719)
Fix a typo "to at" in a doc-string.
2024-07-10 22:03:11 +00:00
Kim Morrison
0c7859a7dd feat: upstream more erase API (#4720)
This should complete leansat's requirements.
2024-07-10 20:26:51 +00:00
Joachim Breitner
c01e003b49 fix: mutual structural recursion: check that datatype parameters agree (#4715)
if will fail otherwise, but with a worse error message, and it's helpful
in later transformation to know that the parameters are the same for the
whole group.
2024-07-10 08:14:57 +00:00
Leonardo de Moura
ce8a130724 fix: deprecated warnings for overloaded symbols (#4713)
closes #4636
2024-07-10 04:06:25 +00:00
Kyle Miller
3c18d151a6 fix: make iff theorem generated by @[ext] preserve inst implicits (#4710)
Previously all arguments from the ext theorem were made implicit, but
now only default and strict implicits are made implicit.
2024-07-10 03:48:39 +00:00
Leonardo de Moura
0f48e926eb fix: decide tactic transparency (#4711)
closes #4644
2024-07-10 01:40:32 +00:00
Leonardo de Moura
850964999e fix: Repr instances for Int and Float (#4709)
closes #4677
2024-07-10 00:10:58 +00:00
Kim Morrison
57b8b32c72 chore: reorganise lemmas on list getters (#4708)
Just reordering lemmas, sorting into subsections.
2024-07-09 22:55:20 +00:00
Kim Morrison
bd2aefee01 feat: simp normal form tests for Pairwise and Nodup (#4707) 2024-07-09 22:26:27 +00:00
Kim Morrison
74dcd6c2a9 feat: lemmas for List.head and List.getLast (#4678) 2024-07-09 22:13:41 +00:00
Kyle Miller
23b893f778 doc: update release checklist for new release notes workflow (#4458)
This makes it reflect how we are writing release notes for 4.9.0,
including how to handle the `releases_drafts` folder and how and when to
update `RELEASES.md`.

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-07-09 21:44:15 +00:00
Kim Morrison
1e02c08111 feat: basic material on List.Pairwise and Nodup (#4706)
Upstreaming of basic material on `List.Pairwise` and `List.Nodup`. More
complete API to follow later, this is just a first approximation of what
leansat will need.
2024-07-09 21:39:08 +00:00
Kim Morrison
0f6a802314 feat: characterisations of List.Sublist (#4704) 2024-07-09 21:34:29 +00:00
Mario Carneiro
be197cd431 fix: prefer original module in const2ModIdx (#4652)
When a definition is redeclared, the original code would clobber the
value of `const2ModIdx` every time, meaning that a constant would be
attributed to a module which occurs later than the modules for constants
referencing this one. Preferring the original module ensures that these
module indexes are dependency-ordered. This originally came up as a bug
in `shake`, which assumes this property, see
[Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/check.20for.20unused.20imports.20doesn't.20stop/near/449139309).
2024-07-09 14:14:39 +00:00
Kim Morrison
f531f4e5db feat: chore upstream List.Sublist and API from Batteries (#4697)
I'll update `list_simp.lean` (simp normal form testing) and add missing
lemmas in follow-up PRs.

This just upstreams the material, and reorders the lemmas to match the
other sections.
2024-07-09 12:57:09 +00:00
Kim Morrison
8229b28cc9 feat: omega doesn't push coercion over multiplication unnecessarily (#4695) 2024-07-09 12:49:31 +00:00
Sebastian Ullrich
582d6e7f71 chore: CI: update download-artifact actions 2024-07-09 10:17:19 +02:00
Mac Malone
4daa29e71d feat: lake: require @ git (#4692)
Adds syntactic sugar specifying a git revision as a dependency version
in a `require` command. For example:

```
require "leanprover-community" / "proofwidgets" @ git "v0.0.39"
```
2024-07-09 02:50:50 +00:00
Kim Morrison
9124426c55 chore: upstream eq_iff_true_of_subsingleton (#4689) 2024-07-08 21:09:33 +00:00
Kyle Miller
cb0755bac0 chore: make use of ext_iff realization now that stage0 is updated (#4694)
This is a followup to #4543. This also adds "go to definition" for
generated lemmas.
2024-07-08 21:05:53 +00:00
Lean stage0 autoupdater
4b32d9b9a1 chore: update stage0 2024-07-08 20:22:24 +00:00
Kyle Miller
7602265923 feat: make @[ext] derive ext_iff theorems from user ext theorems (#4543)
This PR refactors the 'ext' attribute and implements the following
features:
- The 'local' and 'scoped' attribute kinds are now usable.
- The attribute realizes the `ext`/`ext_iff` lemmas when they do not
already exist, rather than always generating them. This is useful in
conjunction with `@[local ext]`.
- Adding `@[ext]` to a user ext lemma now realizes an `ext_iff` lemma as
well; formerly this was only for structures. The name of the generated
`ext_iff` theorem for a user `ext` theorem named `A.B.myext` is
`A.B.myext_iff`. If this process leads to an error, the user can write
`@[ext (iff := false)]` to disable this feature.

Breaking changes:
- Now the "x" and "y" term arguments to the realized `ext` and `ext_iff`
lemmas are implicit.
- Now the realized `ext` and `ext_iff` lemmas are protected.

Bootstrapping notes:
- There are a few `ext_iff` lemmas to address after the next stage0
update.

Closes https://github.com/leanprover/lean4/issues/3643

Suggested by Floris [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.22Missing.20Tactics.22.20list/near/446267660).
2024-07-08 19:37:56 +00:00
Lean stage0 autoupdater
6ba5704e00 chore: update stage0 2024-07-08 21:18:50 +02:00
Joachim Breitner
98ee789990 refactor: InductiveVal.numNested instead of .isNested
right now, in order to find out how many auxilary datatype are in a
mutual group of inductive with nested data type, one has to jump
through hoops like this:

```
private def numNestedInducts (indName : Name) : MetaM Nat := do
  let .inductInfo indVal ← getConstInfo indName | panic! "{indName} is an inductive"
  let .recInfo recVal ← getConstInfo (mkRecName indName) | panic! "{indName} has a recursor"
  return recVal.numMotives - indVal.all.lengt
```

The `InductiveVal` data structure already has `.isNested : Bool`, so it
seems to be a natural extension to beef that up to `.numNested: Nat`.

This touched kernel code.
2024-07-08 21:18:50 +02:00
Kim Morrison
e08a562c48 chore: add step to release checklist (#4693) 2024-07-08 18:58:18 +00:00
Kim Morrison
84c40d9999 chore: improve compatibility of tests/list_simp with Mathlib (#4691)
I'd like to be able to automatically re-test simp normal forms
post-Mathlib. This makes the file compatible with Mathlib.
2024-07-08 17:17:28 +00:00
Kim Morrison
aecebaab74 chore: upstream SMap.foldM (#4690) 2024-07-08 17:11:58 +00:00
Kim Morrison
3b3901b824 chore: forward and backward directions of not_exists (#4688)
These are added in Batteries.
2024-07-08 16:31:04 +00:00
Kim Morrison
811c1e3685 chore: upstream ToExpr FilePath and compile_time_search_path% (#4453) 2024-07-08 15:41:03 +00:00
Kim Morrison
27e85cc947 chore: adjust List.replicate simp lemmas (#4687) 2024-07-08 15:29:19 +00:00
Henrik Böving
9a852595c4 feat: Process.tryWait (#4660)
Reopen of #4659 due to "processing updates" bug.
2024-07-08 15:14:13 +00:00
Joachim Breitner
1311e36a98 feat: structural mutual recursion (#4575)
This adds support for mutual structural recursive functions.

For now this is opt-in: The functions must have a `termination_by
structural …` annotation (new since #4542) for this to work:

```lean
mutual
inductive A
  | self : A → A
  | other : B → A
  | empty
inductive B
  | self : B → B
  | other : A → B
  | empty
end

mutual
def A.size : A → Nat
  | .self a => a.size + 1
  | .other b => b.size + 1
  | .empty => 0
termination_by structural x => x
def B.size : B → Nat
  | .self b => b.size + 1
  | .other a => a.size + 1
  | .empty => 0
termination_by structural x => x
end
```

The recursive functions don’t have to be in a one-to-one relation to a
set of mutually recursive inductive data types. It is possible to ignore
some of the types:

```lean
def A.self_size : A → Nat
  | .self a => a.self_size + 1
  | .other _ => 0
  | .empty => 0
termination_by structural x => x
```

or have more than one function per argument type:

```lean
  def isEven : Nat → Prop
    | 0 => True
    | n+1 => ¬ isOdd n
  termination_by structural x => x

  def isOdd : Nat → Prop
    | 0 => False
    | n+1 => ¬ isEven n
  termination_by structural x => x
```


This does not include

 * Support for nested inductive data types or nested recursion
* Inferring mutual structural recursion in the absence of
`termination_by`.
 * Functional induction principles for these.
* Mutually recursive functions that live in different universes. This
may be possible,
maybe after beefing up the `.below` and `.brecOn` functions; we can look
into this some
   other time, maybe when there are concrete use cases.

---------

Co-authored-by: Richard Kiss <him@richardkiss.com>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-07-08 14:39:50 +00:00
Joachim Breitner
db7a01d126 chore: update comments in kernel/declaration.h (#4683)
This file has comments that recall the data type definitions in Lean.
Most of them were still using lean3 syntax, and at least one of them was
out of date (one field missing), so I updated them.

I took the liberty to shorten the comments from the original file, or
omit them if they don’t add much over the field names.
2024-07-08 14:39:43 +00:00
Sebastian Ullrich
4d2f2d7cc5 fix: calculate error suppression per snapshot (#4657)
Generalizes #3556 to not suppressing errors in tactic steps either when
the parse error is in a later step, as otherwise changes to the end of a
proof would affect (correctness or effectiveness of) incrementality of
preceding steps.

Fixes #4623, in combination with #4643
2024-07-08 09:54:43 +00:00
Sebastian Ullrich
f6265e25f4 chore: fix "max dynamic symbols" metric (#4669)
As we do not build multiple shared libraries on non-Windows anymore,
count the max exported symbols per static library instead.
Unfortunately, this still does seem to match the number on Windows.
2024-07-08 09:09:19 +00:00
Markus Himmel
d6c6e16254 fix: unorphan modules in Std.Data (#4679) 2024-07-08 07:57:56 +00:00
Markus Himmel
818b9d7de0 chore: update codeowners (#4681) 2024-07-08 07:57:54 +00:00
Markus Himmel
34e6579190 chore: Inhabited instances for Std.HashMap (#4682) 2024-07-08 07:57:51 +00:00
Markus Himmel
2b0ed751bd fix: unorphan modules in Init (#4680) 2024-07-08 07:57:50 +00:00
Kim Morrison
6ed26dcf8f chore: cleanup unused arguments (from linter) (#4621) 2024-07-07 21:20:06 +00:00
Joachim Breitner
955135b3f9 chore: bump actions/checkout and actions/upload-artifacts (#4664)
to reduce warnings on CI. Based on the changelogs/migrations guides I
found they should work as before.
2024-07-07 20:26:59 +00:00
Joachim Breitner
f36bbc8d56 fix: hasBadParamDep? to look at term, not type (#4672)
The previous check, looking only at the type of the parameter, was too
permissive and led to ill-typed terms later on.

This fixes #4671.

In some cases the previous code might have worked by accident, in this
sense this is a breaking change. Affected functions can be fixed by
reordering their parameters to that all the function parameters that
occur in the parameter of the inductive type of the parameter that the
function recurses on come first.
2024-07-07 16:00:00 +00:00
Kim Morrison
64eeba726a chore: make Antisymm a Prop (#4666)
As pointed out on
[zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.60Antisymm.60.20is.20in.20.60Type.60/near/449084812).
2024-07-07 12:31:35 +00:00
Sebastian Ullrich
4ed79472af fix: explicitly initialize Std in lean_initialize (#4668)
Fixes the stage 2 build, which runs with `prefer_native=true`
2024-07-06 13:17:30 +00:00
Sebastian Ullrich
55d09a39b4 fix: move Std from libleanshared to much smaller libInit_shared (#4661)
Fixes the Windows build. As libLean is by far the biggest component,
there is no need for a separate libStd_shared for now.

```
$ find build/release/stage1/lib/lean -name '*.a' -exec bash -c 'echo -n "{} " ; nm {} | grep " T " | wc -l' \;
build/release/stage1/lib/lean/libleanrt.a 497
build/release/stage1/lib/lean/libleancpp.a 1320
build/release/stage1/lib/lean/libInit.a 7476
build/release/stage1/lib/lean/libStd.a 1696
build/release/stage1/lib/lean/libLean.a 64339
build/release/stage1/lib/lean/libLake.a 5722
```
2024-07-06 11:43:09 +02:00
Kyle Miller
7de0c58dc1 fix: don't set pp.tagAppFns when pretty printing signatures (#4665)
In #3911, a refactor to share `MessageData` code between `ppConst` and
the signature pretty printer unintentionally caused the signature pretty
printer to use the `pp.tagAppFns` option. This causes, for example, `+`
in `a + b` to independently have its own hover information due to the
fact that `notation` app unexpanders use the head function's syntax as
the `ref` when constructing the notation syntax. This behavior of
`pp.tagAppFns` is intentional, and it is used by docgen, but it should
not be activated for signatures.

This affects `#check` and was reported by Kevin Buzzard [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/degraded.20hover.20experience.20on.20.23check/near/449380674).

This PR also makes sure the initial `ref` when applying app unexpanders
is `.missing`, rather than whatever random value might be present in the
`CoreM` context.
2024-07-05 23:02:39 +00:00
Joachim Breitner
04fefdd728 chore: restart-on-label: wait for 30s (#4663)
this doesn't work reliable when it cancels jobs. Maybe sleeping helps.
2024-07-05 19:50:14 +00:00
Lean stage0 autoupdater
75fe520562 chore: update stage0 2024-07-05 16:43:21 +00:00
Markus Himmel
3e0ea762b8 feat: Std.HashMap (#4583)
### Preliminary PRs:

- [x] #4597 
- [x] #4599
- [x] #4600
- [x] #4602
- [x] #4603
- [x] #4604
- [x] #4605
- [x] #4607
- [x] #4627
- [x] #4629 

### Quick overview over API/naming changes compared to `Lean.HashMap`
and `Batteries.HashMap`:
#### Lean

* `find?` -> `get?`/`getElem?`
* `find!` -> `get!`/`gtetElem!`
* `findD` -> `getD`
* `findEntry?` -> not implemented for now
* `insert'` -> `containsThenInsert` (order reversed in result)
* `insertIfNew` -> `getThenInsertIfNew?` (order reversed in result)
* `numBuckets` -> `Internal.numBuckets`
* `ofListWith` -> not implemented for now
* `Array.groupByKey` -> not implemented for now
* `merge` -> not implemented for now, but you can use `insertMany`

#### Batteries

* `modify` -> not implemented for now
* `mergeWith` -> not implemented for now
* `mergeWithM` -> not implemented for now
2024-07-05 10:14:20 +00:00
Joachim Breitner
5ce886cf96 refactor: Split Constructions module (#4656)
for better build paralleization and less rebuilding when editing one of
these files.
2024-07-05 08:25:44 +00:00
Joachim Breitner
5ad5c2cf04 fix: universe level in .below and .brecOn construction (#4651)
I made a mistake in #4517, fixed here, so about time to add a test.

I wonder if this generic level optimization should be moved into
`mkLevelMax'`, but not today.

fixes #4650
2024-07-04 18:19:43 +00:00
Markus Himmel
d4e141e233 feat: EquivBEq and LawfulHashable classes (#4607)
Split from #4583

There are two open questions, opinions appreciated:

- Should this material be part of `Init` or `Std`?
- Should the typeclasses be in the `Std` namespace?
2024-07-04 05:07:18 +00:00
Markus Himmel
05f78939f6 feat: additional lemmas for arrays (#4627)
Split from #4583
2024-07-04 05:06:34 +00:00
Leonardo de Moura
7b965f3f18 fix: improve synthAppInstances (#4646)
This is an auxiliary procedured used by `rw` and `apply` tactics. It
synthesizes pending type class instances.
The new test contains an example where it failed. The comment at
`synthAppInstances.step` explains why, and the fix.
2024-07-03 19:14:25 +00:00
Joachim Breitner
0594bc4e5a refactor: lambdaBoundedTelescope (#4642)
we have a `forallBoundedTelescope`, and for a long while I was
wondering why we also don't have `lambdaBoundedTelescope`, and every now
and then felt the need for it. So let's just add it.
2024-07-03 15:57:12 +00:00
Sebastian Ullrich
3fb7f632a5 fix: snapshot subtree was not restored on reuse (#4643)
This could lead to nested error messages and info trees vanishing on
edits (strictly) below them

Fixes the second issue in #4623
2024-07-03 15:27:15 +00:00
Joachim Breitner
f6deaa8fb2 chore: update stage0 2024-07-03 14:54:58 +02:00
Joachim Breitner
15a41ffc1c refactor: include declNames in Structural.EqnInfo
this is in preparation for #4575. Because we use the EqnInfo in
attributes, this should be followed by a stage0 update before the field
is used.
2024-07-03 14:54:58 +02:00
Joachim Breitner
cc5c95f377 chore: CI: restart-on-label: view run more often (#4640)
this job sometimes fails, maybe a race condition with the `gh run
cancel` not happenign quickly enough. Maybe more verbose output will
help understand this better.
2024-07-03 12:35:50 +00:00
Sebastian Ullrich
62c5bc5d0d chore: manual nightly trigger (#4638)
Now that nightly releases may fail more often because of more checks,
let's make it possible to retry them on a later commit
2024-07-03 08:19:07 +00:00
Kyle Miller
c2edae92c8 fix: make sure syntax nodes always run their formatters (#4631)
Now syntax nodes have their formatters run even if the parsers they wrap
are all arity zero. This fixes an issue where if `ppSpace` appears in a
`macro`/`elab` then it does not format with a space due to the fact that
macro argument processing wraps this as `group(ppSpace)`, and `ppSpace`
has arity zero.

Implementation note: the fix is to make the `visitArgs` formatter
combinator always visit the last child, even if it does not exist (in
which case the visited node will be `Syntax.missing`). To compensate,
parser combinators like many and optional need to be sure to keep track
of whether there any children. Only optional's needed to be modified.

Closes #4561
2024-07-03 07:45:34 +00:00
Lean stage0 autoupdater
7ef95cd30b chore: update stage0 2024-07-03 07:48:23 +00:00
Alok Singh
2cd2364974 chore: typo (#4635) 2024-07-03 05:14:09 +00:00
Leonardo de Moura
f5fd962a25 feat: safe exponentiation (#4637)
Summary:
- Adds configuration option `exponentiation.threshold`
- An expression `b^n` where `b` and `n` are literals is not reduced by
`whnf`, `simp`, and `isDefEq` if `n > exponentiation.threshold`.

Motivation: prevents system from becoming irresponsive and/or crashing
without memory.

TODO: improve support in the kernel. It is using a hard-coded limit for
now.
2024-07-03 05:12:53 +00:00
Markus Himmel
d72fcb6b2a feat: Option.or (#4600)
Split from #4583
2024-07-03 01:30:15 +00:00
Markus Himmel
a2a73e9611 feat: USize.and_toNat (#4629)
Split from #4583
2024-07-03 01:28:36 +00:00
Kyle Miller
3f2cf8bf27 fix: set default value of pp.instantiateMVars to true and make the option be effective (#4558)
Before, `pp.instantiateMVars` generally had no effect because most call
sites for the pretty printer instantiated metavariables first, but now
this functionality is entrusted upon the `pp.instantiateMVars` option.

This also has an effect in hovers, where metavariables can be unfolded
one assignment at a time. However, the goal state still sees all
metavariables instantiated due to the fact that the algorithm relies on
expression equality post-instantiation (see
`Lean.Widget.goalToInteractive`).

Closes #4406
2024-07-02 22:59:44 +00:00
Joachim Breitner
d39c4d6a1c chore: pr-release: use right tag name (#4632)
this improves upon #4598
2024-07-02 21:06:55 +00:00
Sebastian Ullrich
b28cfb9336 fix: Windows build (#4628) 2024-07-02 20:32:00 +00:00
Leonardo de Moura
6080e3dd5c fix: enforce isDefEqStuckEx at unstuckMVar procedure (#4596)
Closes #2736

See comment at `ExprDefEq.lean` for explanation.
Side effects:
- Improved error messages in two tests.
- Had to improve `getSuccesses` procedure at `App.lean`. It now
  discards candidates that contain postponed elaboration problems.
  If it is too disruptive for Mathlib, we should try to discard the
  ones that have postponed metavariables.
2024-07-02 13:42:47 +00:00
Markus Himmel
8959b2ca87 chore: make constructor-as-variable test more robust (#4625)
The test tended to fail every time the number of structures in `Init`
changes, which turns out to be quite often.
2024-07-02 11:44:46 +00:00
Kim Morrison
554e723433 chore: add 'since' dates to deprecated (#4617) 2024-07-02 04:30:09 +00:00
Kim Morrison
9cc1164305 chore: follow simpNF linter's advice (#4620)
We can run the `simpNF` environment linter from Batteries. Nearly all
its advice is good.
2024-07-02 04:30:00 +00:00
Kim Morrison
0c6f83eb6d chore: satisfy duplicate namespace linter (#4616) 2024-07-02 04:29:56 +00:00
Kim Morrison
1225b0f651 chore: delete deprecations from 2022 (#4618) 2024-07-02 03:47:33 +00:00
Kim Morrison
75e11ecf7c chore: defs that should be theorems (#4619) 2024-07-02 03:03:11 +00:00
Markus Himmel
4055aecba2 feat: additional lemmas for bounded integers (#4605)
Split from #4583
2024-07-02 02:03:13 +00:00
Markus Himmel
1681b2fa67 feat: additional lemmas for cond (#4604)
Split from #4583
2024-07-02 02:02:41 +00:00
Markus Himmel
c97f958ecf feat: getElem_congr (#4603)
Split from #4583

Rewriting the `i` in `xs[i]` usually fails with `motive is not type
correct`, but with this lemma it works.
2024-07-02 02:02:17 +00:00
Markus Himmel
e2dc85274b feat: additional lemmas for lists (#4602)
Split from #4583

`exists_of_set` appears in Batteries as `exists_of_set'`. The
`exists_of_set` version is unused in batteries and mathlib at least and
I would argue that the primed version (i.e., the one added in this PR)
is always better anyway.

`isEmpty_iff` appears in mathlib as `isEmpty_iff_eq_nil`.
2024-07-02 02:01:44 +00:00
Markus Himmel
e12999bcf6 feat: additional lemmas for Option (#4599)
Split from #4583

Mathlib has `isSome_map'` but calls it `isSome_map`.
2024-07-02 01:58:33 +00:00
Markus Himmel
7a0fe6f54c feat: Nat.and_le_(left|right) (#4597)
Split from #4583
2024-07-02 01:55:12 +00:00
Mario Carneiro
4a2210b7e6 fix: unresolve name avoiding locals (#4593)
Fixes #4591. The extra code already existed in the only other user of
`unresolveNameGlobal` (in the pretty printer), although I did not make
it use this function because it has some additional behavior around
universes and in pattern position.
2024-07-02 01:15:39 +00:00
Siddharth
e9d2f8f5f2 feat: mul recurrence theorems for LeanSAT (#4568)
This implements the recurrence theorems `getLsb_mul`, `mulRec_zero_eq`,
`mulRec_succ_eq` to allow bitblasting multiplication.

---------

Co-authored-by: Kim Morrison <scott@tqft.net>
2024-07-01 23:47:29 +00:00
Lean stage0 autoupdater
d5a45dfa8b chore: update stage0 2024-07-01 17:36:58 +00:00
Joachim Breitner
fb0c46a011 feat: termination_by structural (#4542)
This implements the `termination_by structural` syntax proposed in
#3909.

I went with `termination_by structural` over, say,
`termination_by (config := {method := .structural})` mainly because it
was
easier to get going (otherwise I’d have to look into how to define
recursive
parsers, as `Parser.config` depends on `term` and `termination_by` is
part of
term. But also because I find it more ergonomic and aesthetic as a user.
But syntax can still change.

The `termination_by?` syntax will no longer force well-founded
recursion,
and instead the inferred `termination_by structurally` annotation will
be shown
if structural termination is possible.

While I was it, this fixes #4546 the easy way (log errors about but
otherwise
ignore incomplete `termination_by` sets for mutual recursion). Maybe we
get
multiple replacements (#4551), but even then this this good behavior.

Involves a bit of shuffling around `TerimationHints` (now validated for
a
clique already by `PreDefinition.main`) and `TerminationArguments` (now
lifted
out of the `WF` namespace, and a bit simplified).

Fixes #3909

---------

Co-authored-by: Richard Kiss <him@richardkiss.com>
2024-07-01 16:51:30 +00:00
Joachim Breitner
0635b277ec fix: diagnostics: show kernel diags even if it is the only section (#4611) 2024-07-01 16:45:39 +00:00
Joachim Breitner
087054172c feat: omega error message: normalize constraint order (#4612)
using the order as it comes out of the `HashMap` led to annying test
suite output variations. Moreover, sorting by the canonical order leads
to messages that are probably easier to digest as a user.
2024-07-01 16:11:15 +00:00
Sebastian Ullrich
7f00767b1e fix: adapt kernel interruption to new cancellation system (#4584)
Kernel checks were not canceled on edit after #3014
2024-07-01 14:52:42 +00:00
Joachim Breitner
be54ccd246 chore: pr-release: adjust to new lakefile.lean syntax (#4598)
as per

f5c3f06aa7
2024-07-01 08:48:33 +00:00
Kim Morrison
4d0b7cf66c chore: begin development cycle for v4.11.0 (#4594) 2024-06-30 23:28:48 +00:00
Joachim Breitner
0629eebc09 chore: release triggers update of release.lean-lang.org (#4531) 2024-06-30 10:39:32 +00:00
Wojciech Nawrocki
9248ada3a8 feat: total ByteArray.toList/findIdx? (#4582)
This is to enable proving facts about these functions.
2024-06-30 07:09:08 +00:00
Kyle Miller
144a3d9463 fix: typo hearbeats -> heartbeats (#4590)
Reported [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/is.20.60trace.2Eprofiler.2EuseHeartbeats.60.20a.20thing.3F/near/447950838).
2024-06-30 07:07:11 +00:00
L
a7bbe7416b feat: upstream List.attach and Array.attach from Batteries (#4586)
Source material:

555ec79bc6/Batteries/Data/List/Init/Attach.lean

555ec79bc6/Batteries/Data/Array/Basic.lean (L133-L148)

Closes RFC #4414
2024-06-30 07:06:26 +00:00
Leonardo de Moura
f31d4dc128 chore: update stage0 2024-06-29 19:18:53 +02:00
Leonardo de Moura
fb97275dcb feat: add Simp.Config.implicitDefEqProofs
This commit does **not** implement this feature.
2024-06-29 19:18:53 +02:00
Leni Aniva
d4d7c72365 fix: Add linking of -lStd back into nix build flags on darwin (#4587)
Adds linkage to `Std` so the build behaviour on darwin is in line with
linux

I'm not sure why linking with `Std` is needed. I deleted it in the
previous patch https://github.com/leanprover/lean4/pull/3811 and Lean
still builds and runs. @tydeu mentioned this issue so I created this PR.
2024-06-29 08:12:57 +00:00
Mac Malone
93c9ae7c20 feat: lake: reservoir require (#4495)
Adds a new type of `require` which fetches package metadata from a
registry API endpoint (i.e., Reservoir) and then clones a Git package
using the information provided. To require such a dependency, the new
syntax is:

```lean
require <scope> / <pkg-name> [@ "git#<rev>"] -- e.g., require "leanprover" / "doc-gen4"
```

Or in TOML:

```toml
[[require]]
name = "<pkg-name>"
scope = "<scope>"
rev = "<rev>"
```

Unlike with Git dependencies, Lake can make use of the richer
information provided by the registry to determine the default branch of
the package. This means for repositories of packages like `doc-gen4`
which have a default branch that is not `master`, Lake will now use said
default branch (e.g., in `doc-gen4`'s case, `main`).

Lake also supports configuring the registry endpoint via an environment
variable: `RESERVIOR_API_URL`. Thus, any server providing a similar
interface to Reservoir can be used as the registry. Further
configuration options paralleling those of Cargo's [Alternative
Registries](https://doc.rust-lang.org/cargo/reference/registries.html)
and [Source
Replacement](https://doc.rust-lang.org/cargo/reference/source-replacement.html)
will come in the future.

Updated and split from #3174.
2024-06-29 01:40:54 +00:00
Leni Aniva
b8dd51500f fix: nix: add platform dependent flag to lib target (#3811)
Closes #3810
2024-06-28 10:40:11 +00:00
Kim Morrison
bd091f119b chore: fix bv_omega regression since v4.9.0 (#4579)
This example, reported from LNSym, started failing when we changed the
definition of `Fin.sub` in
https://github.com/leanprover/lean4/pull/4421.

When we use the new definition, `omega` produces a proof term that the
kernel is very slow on.

To work around this for now, I've removed `BitVec.toNat_sub` from the
`bv_toNat` simp set,
and replaced it with `BitVec.toNat_sub'` which uses the old definition
for subtraction.

This is only a workaround, and I would like to understand why the term
chokes the kernel.

```
example
    (n : Nat)
    (addr2 addr1 : BitVec 64)
    (h0 : n ≤ 18446744073709551616)
    (h1 : addr2 + 18446744073709551615#64 - addr1 ≤ BitVec.ofNat 64 (n - 1))
    (h2 : addr2 - addr1 ≤ addr2 + 18446744073709551615#64 - addr1) :
    n = 18446744073709551616 := by
  bv_omega
```
2024-06-28 01:20:08 +00:00
Leonardo de Moura
d8e719f9ab feat: add set_option debug.skipKernelTC true
The new option `set_option debug.skipKernelTC true` is meant for
temporarily working around kernel performance issues.
It compromises soundness because a buggy tactic may produce an invalid
proof, and the kernel will not catch it if the new option is set to true.
2024-06-28 00:55:47 +02:00
Leonardo de Moura
93d2ad5fa7 chore: update stage0 2024-06-28 00:55:47 +02:00
Leonardo de Moura
7b56eb20a0 feat: prepare for adding new option debug.skipKernelTC
Remark: I had to comment
```
if debug.skipKernelTC.get opts then
  addDeclWithoutChecking env decl
else
```
because the build was crashing when trying to compile Lake.
Going to perform `update-stage0` and try again.
2024-06-28 00:55:47 +02:00
Leonardo de Moura
30a922a7e9 feat: add option debug.byAsSorry true (#4576) 2024-06-27 18:29:26 +00:00
Mac Malone
294f7fbec5 fix: lake: computation of precompiled libs (#4566)
Addresses a few issues with precompile library computation. 

* Fixes a bug where Lake would always precompile the package of a
module.
* If a module is precompiled, it now precompiles its imports.
Previously, it would only do this if imported.

Closes #4565.
2024-06-27 15:08:52 +00:00
Sebastian Ullrich
f3cb8a6c2d fix: interrupt exception was swallowed by some tryCatchRuntimeEx uses (#4569)
This appears to have been a semantic merge conflict between #3940 and
#4129. The effect on the language server is that if two edits are
sufficiently close in time to create an interrupt, some elaboration
steps like `simp` may accidentally catch the exception when it is
triggered during their execution, which makes incrementality assume that
elaboration of the body was successful, which can lead to incorrect
reuse, presenting the interrupted state to the user with symptoms such
as "uses sorry" without accompanying errors and incorrect lints.
2024-06-27 10:03:22 +00:00
Kim Morrison
5c978a2e24 feat: remove Decidable instances from GetElem (#4560) 2024-06-27 02:09:29 +00:00
Leonardo de Moura
ee42c3ca56 fix: discrepancy in the elaborators for theorem, def, and example (#4482)
When the type of a definition or example is a proposition,
we should elaborate on them as we elaborate on theorems.
This is particularly important for examples that are often
used in educational material.

Recall that when elaborating theorem headers, we convert unassigned
universe metavariables into universe parameters. The motivation is
that the proof of a theorem should not influence its statement.
However, before this commit, this was not the case for definitions and
examples when their type was a proposition. This discrepancy often
confused users.

Additionally, we considered extending the above behavior whenever
the type of a definition is provided. That is, we would keep the
current behavior only if `: <type>` was omitted in a definition.
However, this proved to be too restrictive.
For example, the following instance in `Core.lean` would fail:
```
instance {α : Sort u} [Setoid α] : HasEquiv α :=
  ⟨Setoid.r⟩
```
and we would have to write instead:
```
instance {α : Sort u} [Setoid α] : HasEquiv.{u, 0} α :=
  ⟨Setoid.r⟩
```
There are other failures like this in the core, and we assume many more
in Mathlib.

closes #4398

@semorrison @jcommelin: what do you think?
2024-06-27 00:58:58 +00:00
Joachim Breitner
18c97926a1 refactor: extract withRecArgInfo from findRecArg (#4549)
this is  in preparation for #4542, and extracts from `findRecArg` the
functionality for trying one particular argument.

It also refactors the code a bit. In particular

 * It reports errors in the order of the parameters, not the order of
   in which they are tried (it tries non-indices first).
 * For every argument it will say why it wasn't tried, even if the
   reason is quite obviously (fixed prefix, or `Prop`-typed etc.)

Therefore there is some error message churn.
2024-06-26 11:10:57 +00:00
Joachim Breitner
ea22ef4485 refactor: port below and brecOn construction to Lean (#4517)
This ports the `.below` and `.brecOn` constructions to lean.

I kept them in the same file, as they were in the C code, because they
are
highly coupled and the constructions are very analogous.

For validation I developed this in a separate repository at
https://github.com/nomeata/lean-constructions/tree/fad715e
and checked that all declarations found in Lean and Mathlib are
equivalent, up to

    def canon (e : Expr) : CoreM Expr := do
      Core.transform (← Core.betaReduce e) (pre := fun
        | .const n ls  => return .done (.const n (ls.map (·.normalize)))
        | .sort l => return .done (.sort l.normalize)
        | _ => return .continue)

It was not feasible to make them completely equal, because the kernel's
type inference code seem to optimize level expressions a bit less
aggressively, and beta-reduces less in inference.

The private helper functions about `PProd` can later move into their own
file, used by these constructions as well as the structural recursion
module.
2024-06-26 11:10:39 +00:00
Leonardo de Moura
62b6e58789 fix: avoid unnecessary proof steps in simp (#4567)
closes #4534
2024-06-26 05:48:03 +00:00
Mac Malone
714dc6d2bb fix: lake: non-ident script names (#4564)
Fixes a bug where non-identifier script names could not be entered on
the CLI without French quotes. [Reported on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Running.20.60lake.60.20scripts.20from.20the.20command.20line/near/446944450).

Also adds support for string literal script names in a `lakefile.lean`.
2024-06-26 04:24:01 +00:00
Mac Malone
5e7d2c34dc fix: lake: exe bad import errors & related touchups (#4529)
Fixes some issues with the executable build and bad imports.

**Release notes:** 
* A bad import in an executable no longer prevents the executable's root
module from being built., This also fixes a problem where the location
of a transitive bad import would not been shown.
 * The root module of the executable now respects `nativeFacets`.

**Technical touchups:**

* Expanded and better documented `tests/badImport`.
* Use `ensureJob` in `recBuildDeps` to catch import errors instead of
individual `try ... catch` blocks.
2024-06-26 03:39:39 +00:00
Leonardo de Moura
fb6d29e260 fix: IndPredBelow should not add auxiliary declarations containing sorry (#4563)
Issue #4535 is being affected by a bug in the structural inductive
predicate termination checker (`IndPred.lean`). This module did not
exist in Lean 3, and it is buggy in Lean 4. In the given example, it
introduces an auxiliary declaration containing a `sorry`, and the fails.
This PR ensures this kind of declaration is not added to the
environment.

Closes #4535

TODO: we need a new maintainer for the `IndPred.lean`.
2024-06-25 20:57:32 +00:00
Leonardo de Moura
4964ce3ce8 fix: two functions with the same name in a where/let rec block (#4562)
closes #4547
2024-06-25 20:03:53 +00:00
Kyle Miller
230f335702 fix: block implicit lambda feature for type-free type ascription (#4536)
The implicit lambda feature is already blocked for type ascriptions, but
there is an oversight where it was not blocked for the `(x :)` type
ascription as well. Reported on
[Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.60refine.60.20with.20implicit.20variables/near/446327230).
2024-06-25 18:18:23 +00:00
Sebastian Ullrich
875e4b1904 fix: tactics in terms in tactic combinators breaking incrementality (#4554)
Fixes #4553
2024-06-25 08:59:38 +00:00
Kyle Miller
49249b9107 feat: introduce pp.maxSteps (#4556)
The `pp.maxSteps` option is a hard limit on the complexity of pretty
printer output, which is necessary to prevent the LSP from crashing when
there are accidental large terms. We're using the default value from the
corresponding Lean 3 option.

This PR also sets `pp.deepTerms` to `false` by default.
2024-06-24 19:19:45 +00:00
Kim Morrison
3b67e15827 feat: maximum?_eq_some_iff' (#4550)
Requested by @hargoniX.
2024-06-24 11:57:27 +00:00
1069 changed files with 26839 additions and 5478 deletions

View File

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

View File

@@ -9,6 +9,17 @@ on:
merge_group:
schedule:
- cron: '0 7 * * *' # 8AM CET/11PM PT
# for manual re-release of a nightly
workflow_dispatch:
inputs:
action:
description: 'Action'
required: true
default: 'release nightly'
type: choice
options:
- release nightly
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
@@ -41,11 +52,11 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
- name: Set Nightly
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
id: set-nightly
run: |
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
@@ -287,8 +298,8 @@ jobs:
uses: msys2/setup-msys2@v2
with:
msystem: clang64
# `:p` means prefix with appropriate msystem prefix
pacboy: "make python cmake:p clang:p ccache:p gmp:p git zip unzip diffutils binutils tree zstd:p tar"
# `:` means do not prefix with msystem
pacboy: "make: python: cmake clang ccache gmp git: zip: unzip: diffutils: binutils: tree: zstd tar:"
if: runner.os == 'Windows'
- name: Install Brew Packages
run: |
@@ -386,7 +397,7 @@ jobs:
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.release
with:
name: build-${{ matrix.name }}
@@ -415,7 +426,7 @@ jobs:
if: matrix.test-speedcenter
- name: Check Stage 3
run: |
make -C build -j$NPROC stage3
make -C build -j$NPROC check-stage3
if: matrix.test-speedcenter
- name: Test Speedcenter Benchmarks
run: |
@@ -444,12 +455,24 @@ jobs:
# mark as merely cancelled not failed if builds are cancelled
if: ${{ !cancelled() }}
steps:
- if: ${{ contains(needs.*.result, 'failure') && github.repository == 'leanprover/lean4' && github.ref_name == 'master' }}
uses: zulip/github-actions-zulip/send-message@v1
with:
api-key: ${{ secrets.ZULIP_BOT_KEY }}
email: "github-actions-bot@lean-fro.zulipchat.com"
organization-url: "https://lean-fro.zulipchat.com"
to: "infrastructure"
topic: "Github actions"
type: "stream"
content: |
A build of `${{ github.ref_name }}`, triggered by event `${{ github.event_name }}`, [failed](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
- if: contains(needs.*.result, 'failure')
uses: actions/github-script@v7
with:
script: |
core.setFailed('Some jobs failed')
# This job creates releases from tags
# (whether they are "unofficial" releases for experiments, or official releases when the tag is "v" followed by a semver string.)
# We do not attempt to automatically construct a changelog here:
@@ -459,7 +482,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Release
@@ -470,6 +493,11 @@ jobs:
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-rc') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update release.lean-lang.org
run: |
gh workflow -R leanprover/release-index run update-index.yml
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}
# This job creates nightly releases during the cron job.
# It is responsible for creating the tag, and automatically generating a changelog.
@@ -479,12 +507,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# needed for tagging
fetch-depth: 0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare Nightly Release
@@ -512,3 +540,13 @@ jobs:
repository: ${{ github.repository_owner }}/lean4-nightly
env:
GITHUB_TOKEN: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- name: Update release.lean-lang.org
run: |
gh workflow -R leanprover/release-index run update-index.yml
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}
- name: Update toolchain on mathlib4's nightly-testing branch
run: |
gh workflow -R leanprover-community/mathlib4 run nightly_bump_toolchain.yml
env:
GITHUB_TOKEN: ${{ secrets.MATHLIB4_BOT }}

34
.github/workflows/jira.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Jira sync
on:
issues:
types: [closed]
jobs:
jira-sync:
runs-on: ubuntu-latest
steps:
- name: Move Jira issue to Done
env:
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }}
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
run: |
issue_number=${{ github.event.issue.number }}
jira_issue_key=$(curl -s -u "${JIRA_USERNAME}:${JIRA_API_TOKEN}" \
-X GET -H "Content-Type: application/json" \
"${JIRA_BASE_URL}/rest/api/2/search?jql=summary~\"${issue_number}\"" | \
jq -r '.issues[0].key')
if [ -z "$jira_issue_key" ]; then
exit
fi
curl -s -u "${JIRA_USERNAME}:${JIRA_API_TOKEN}" \
-X POST -H "Content-Type: application/json" \
--data "{\"transition\": {\"id\": \"41\"}}" \
"${JIRA_BASE_URL}/rest/api/2/issue/${jira_issue_key}/transitions"
echo "Moved Jira issue ${jira_issue_key} to Done"

View File

@@ -50,7 +50,7 @@ jobs:
NIX_BUILD_ARGS: --print-build-logs --fallback
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}

View File

@@ -234,7 +234,7 @@ jobs:
# Checkout the Batteries repository with all branches
- name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }}
@@ -291,7 +291,7 @@ jobs:
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: leanprover-community/mathlib4
token: ${{ secrets.MATHLIB4_BOT }}
@@ -328,7 +328,7 @@ jobs:
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
git add lean-toolchain
sed -i "s/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \".\+\"/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
sed -i 's,require "leanprover-community" / "batteries" @ ".\+",require "leanprover-community" / "batteries" @ "git#nightly-testing-'"${MOST_RECENT_NIGHTLY}"'",' lakefile.lean
lake update batteries
git add lakefile.lean lake-manifest.json
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"

View File

@@ -20,10 +20,12 @@ jobs:
gh run view "$run_id"
echo "Cancelling (just in case)"
gh run cancel "$run_id" || echo "(failed)"
echo "Waiting for 10s"
sleep 10
echo "Waiting for 30s"
sleep 30
gh run view "$run_id"
echo "Rerunning"
gh run rerun "$run_id"
gh run view "$run_id"
shell: bash
env:
head_ref: ${{ github.head_ref }}

View File

@@ -23,7 +23,7 @@ jobs:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"

View File

@@ -42,4 +42,4 @@
/src/Lean/Elab/Tactic/Guard.lean @digama0
/src/Init/Guard.lean @digama0
/src/Lean/Server/CodeActions/ @digama0
/src/Std/ @TwoFX

View File

@@ -63,6 +63,20 @@ Because the change will be squashed, there is no need to polish the commit messa
Reviews and Feedback:
----
The lean4 repo is managed by the Lean FRO's *triage team* that aims to provide initial feedback on new bug reports, PRs, and RFCs weekly.
This feedback generally consists of prioritizing the ticket using one of the following categories:
* label `P-high`: We will work on this issue
* label `P-medium`: We may work on this issue if we find the time
* label `P-low`: We are not planning to work on this issue
* *closed*: This issue is already fixed, it is not an issue, or is not sufficiently compatible with our roadmap for the project and we will not work on it nor accept external contributions on it
For *bug reports*, the listed priority reflects our commitment to fixing the issue.
It is generally indicative but not necessarily identical to the priority an external contribution addressing this bug would receive.
For *PRs* and *RFCs*, the priority reflects our commitment to reviewing them and getting them to an acceptable state.
Accepted RFCs are marked with the label `RFC accepted` and afterwards assigned a new "implementation" priority as with bug reports.
General guidelines for interacting with reviews and feedback:
**Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time.
**Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism.

View File

@@ -8,13 +8,326 @@ This file contains work-in-progress notes for the upcoming release, as well as p
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
of each version.
v4.10.0
v4.11.0
----------
Development in progress.
v4.10.0
----------
Release candidate, release notes will be copied from branch `releases/v4.10.0` once completed.
v4.9.0
----------
Release candidate, release notes will be copied from branch `releases/v4.9.0` once completed.
### Language features, tactics, and metaprograms
* **Definition transparency**
* [#4053](https://github.com/leanprover/lean4/pull/4053) adds the `seal` and `unseal` commands, which make definitions locally be irreducible or semireducible.
* [#4061](https://github.com/leanprover/lean4/pull/4061) marks functions defined by well-founded recursion with `@[irreducible]` by default,
which should prevent the expensive and often unfruitful unfolding of such definitions (see breaking changes below).
* **Incrementality**
* [#3940](https://github.com/leanprover/lean4/pull/3940) extends incremental elaboration into various steps inside of declarations:
definition headers, bodies, and tactics.
![Recording 2024-05-10](https://github.com/leanprover/lean4/assets/109126/c9d67b6f-c131-4bc3-a0de-7d63eaf1bfc9).
* [250994](https://github.com/leanprover/lean4/commit/250994166ce036ab8644e459129f51ea79c1c2d2)
and [67338b](https://github.com/leanprover/lean4/commit/67338bac2333fa39a8656e8f90574784e4c23d3d)
add `@[incremental]` attribute to mark an elaborator as supporting incremental elaboration.
* [#4259](https://github.com/leanprover/lean4/pull/4259) improves resilience by ensuring incremental commands and tactics are reached only in supported ways.
* [#4268](https://github.com/leanprover/lean4/pull/4268) adds special handling for `:= by` so that stray tokens in tactic blocks do not inhibit incrementality.
* [#4308](https://github.com/leanprover/lean4/pull/4308) adds incremental `have` tactic.
* [#4340](https://github.com/leanprover/lean4/pull/4340) fixes incorrect info tree reuse.
* [#4364](https://github.com/leanprover/lean4/pull/4364) adds incrementality for careful command macros such as `set_option in theorem`, `theorem foo.bar`, and `lemma`.
* [#4395](https://github.com/leanprover/lean4/pull/4395) adds conservative fix for whitespace handling to avoid incremental reuse leading to goals in front of the text cursor being shown.
* [#4407](https://github.com/leanprover/lean4/pull/4407) fixes non-incremental commands in macros blocking further incremental reporting.
* [#4436](https://github.com/leanprover/lean4/pull/4436) fixes incremental reporting when there are nested tactics in terms.
* **Functional induction**
* [#4135](https://github.com/leanprover/lean4/pull/4135) ensures that the names used for functional induction are reserved.
* [#4327](https://github.com/leanprover/lean4/pull/4327) adds support for structural recursion on reflexive types.
For example,
```lean4
inductive Many (α : Type u) where
| none : Many α
| more : α → (Unit → Many α) → Many α
def Many.map {α β : Type u} (f : α → β) : Many α → Many β
| .none => .none
| .more x xs => .more (f x) (fun _ => (xs ()).map f)
#check Many.map.induct
/-
Many.map.induct {α β : Type u} (f : α → β) (motive : Many α → Prop)
(case1 : motive Many.none)
(case2 : ∀ (x : α) (xs : Unit → Many α), motive (xs ()) → motive (Many.more x xs)) :
∀ (a : Many α), motive a
-/
```
* [#3903](https://github.com/leanprover/lean4/pull/3903) makes the Lean frontend normalize all line endings to LF before processing.
This lets Lean be insensitive to CRLF vs LF line endings, improving the cross-platform experience and making Lake hashes be faithful to what Lean processes.
* [#4130](https://github.com/leanprover/lean4/pull/4130) makes the tactic framework be able to recover from runtime errors (for example, deterministic timeouts or maximum recursion depth errors).
* `split` tactic
* [#4211](https://github.com/leanprover/lean4/pull/4211) fixes `split at h` when `h` has forward dependencies.
* [#4349](https://github.com/leanprover/lean4/pull/4349) allows `split` for `if`-expressions to work on non-propositional goals.
* `apply` tactic
* [#3929](https://github.com/leanprover/lean4/pull/3929) makes error message for `apply` show implicit arguments in unification errors as needed.
Modifies `MessageData` type (see breaking changes below).
* `cases` tactic
* [#4224](https://github.com/leanprover/lean4/pull/4224) adds support for unification of offsets such as `x + 20000 = 20001` in `cases` tactic.
* `omega` tactic
* [#4073](https://github.com/leanprover/lean4/pull/4073) lets `omega` fall back to using classical `Decidable` instances when setting up contradiction proofs.
* [#4141](https://github.com/leanprover/lean4/pull/4141) and [#4184](https://github.com/leanprover/lean4/pull/4184) fix bugs.
* [#4264](https://github.com/leanprover/lean4/pull/4264) improves `omega` error message if no facts found in local context.
* [#4358](https://github.com/leanprover/lean4/pull/4358) improves expression matching in `omega` by using `match_expr`.
* `simp` tactic
* [#4176](https://github.com/leanprover/lean4/pull/4176) makes names of erased lemmas clickable.
* [#4208](https://github.com/leanprover/lean4/pull/4208) adds a pretty printer for discrimination tree keys.
* [#4202](https://github.com/leanprover/lean4/pull/4202) adds `Simp.Config.index` configuration option,
which controls whether to use the full discrimination tree when selecting candidate simp lemmas.
When `index := false`, only the head function is taken into account, like in Lean 3.
This feature can help users diagnose tricky simp failures or issues in code from libraries
developed using Lean 3 and then ported to Lean 4.
In the following example, it will report that `foo` is a problematic theorem.
```lean
opaque f : Nat → Nat → Nat
@[simp] theorem foo : f x (x, y).2 = y := by sorry
example : f a b ≤ b := by
set_option diagnostics true in
simp (config := { index := false })
/-
[simp] theorems with bad keys
foo, key: f _ (@Prod.mk _ _).2
-/
```
With the information above, users can annotate theorems such as `foo` using `no_index` for problematic subterms. Example:
```lean
opaque f : Nat → Nat → Nat
@[simp] theorem foo : f x (no_index (x, y).2) = y := by sorry
example : f a b ≤ b := by
simp -- `foo` is still applied with `index := true`
```
* [#4274](https://github.com/leanprover/lean4/pull/4274) prevents internal `match` equational theorems from appearing in simp trace.
* [#4177](https://github.com/leanprover/lean4/pull/4177) and [#4359](https://github.com/leanprover/lean4/pull/4359) make `simp` continue even if a simp lemma does not elaborate, if the tactic state is in recovery mode.
* [#4341](https://github.com/leanprover/lean4/pull/4341) fixes panic when applying `@[simp]` to malformed theorem syntax.
* [#4345](https://github.com/leanprover/lean4/pull/4345) fixes `simp` so that it does not use the forward version of a user-specified backward theorem.
* [#4352](https://github.com/leanprover/lean4/pull/4352) adds missing `dsimp` simplifications for fixed parameters of generated congruence theorems.
* [#4362](https://github.com/leanprover/lean4/pull/4362) improves trace messages for `simp` so that constants are hoverable.
* **Elaboration**
* [#4046](https://github.com/leanprover/lean4/pull/4046) makes subst notation (`he ▸ h`) try rewriting in both directions even when there is no expected type available.
* [#3328](https://github.com/leanprover/lean4/pull/3328) adds support for identifiers in autoparams (for example, `rfl` in `(h : x = y := by exact rfl)`).
* [#4096](https://github.com/leanprover/lean4/pull/4096) changes how the type in `let` and `have` is elaborated, requiring that any tactics in the type be evaluated before proceeding, improving performance.
* [#4215](https://github.com/leanprover/lean4/pull/4215) ensures the expression tree elaborator commits to the computed "max type" for the entire arithmetic expression.
* [#4267](https://github.com/leanprover/lean4/pull/4267) cases signature elaboration errors to show even if there are parse errors in the body.
* [#4368](https://github.com/leanprover/lean4/pull/4368) improves error messages when numeric literals fail to synthesize an `OfNat` instance,
including special messages warning when the expected type of the numeral can be a proposition.
* **Metaprogramming**
* [#4167](https://github.com/leanprover/lean4/pull/4167) adds `Lean.MVarId.revertAll` to revert all free variables.
* [#4169](https://github.com/leanprover/lean4/pull/4169) adds `Lean.MVarId.ensureNoMVar` to ensure the goal's target contains no expression metavariables.
* [#4180](https://github.com/leanprover/lean4/pull/4180) adds `cleanupAnnotations` parameter to `forallTelescope` methods.
* [#4307](https://github.com/leanprover/lean4/pull/4307) adds support for parser aliases in syntax quotations.
* Work toward implementing `grind` tactic
* [0a515e](https://github.com/leanprover/lean4/commit/0a515e2ec939519dafb4b99daa81d6bf3c411404)
and [#4164](https://github.com/leanprover/lean4/pull/4164)
add `grind_norm` and `grind_norm_proc` attributes and `@[grind_norm]` theorems.
* [#4170](https://github.com/leanprover/lean4/pull/4170), [#4221](https://github.com/leanprover/lean4/pull/4221),
and [#4249](https://github.com/leanprover/lean4/pull/4249) create `grind` preprocessor and core module.
* [#4235](https://github.com/leanprover/lean4/pull/4235) and [d6709e](https://github.com/leanprover/lean4/commit/d6709eb1576c5d40fc80462637dc041f970e4d9f)
add special `cases` tactic to `grind` along with `@[grind_cases]` attribute to mark types that this `cases` tactic should automatically apply to.
* [#4243](https://github.com/leanprover/lean4/pull/4243) adds special `injection?` tactic to `grind`.
* **Other fixes or improvements**
* [#4065](https://github.com/leanprover/lean4/pull/4065) fixes a bug in the `Nat.reduceLeDiff` simproc.
* [#3969](https://github.com/leanprover/lean4/pull/3969) makes deprecation warnings activate even for generalized field notation ("dot notation").
* [#4132](https://github.com/leanprover/lean4/pull/4132) fixes the `sorry` term so that it does not activate the implicit lambda feature
* [9803c5](https://github.com/leanprover/lean4/commit/9803c5dd63dc993628287d5f998525e74af03839)
and [47c8e3](https://github.com/leanprover/lean4/commit/47c8e340d65b01f4d9f011686e3dda0d4bb30a20)
move `cdot` and `calc` parsers to `Lean` namespace.
* [#4252](https://github.com/leanprover/lean4/pull/4252) fixes the `case` tactic so that it is usable in macros by having it erase macro scopes from the tag.
* [26b671](https://github.com/leanprover/lean4/commit/26b67184222e75529e1b166db050aaebee323d2d)
and [cc33c3](https://github.com/leanprover/lean4/commit/cc33c39cb022d8a3166b1e89677c78835ead1fc7)
extract `haveId` syntax.
* [#4335](https://github.com/leanprover/lean4/pull/4335) fixes bugs in partial `calc` tactic when there is mdata or metavariables.
* [#4329](https://github.com/leanprover/lean4/pull/4329) makes `termination_by?` report unused each unused parameter as `_`.
* **Docs:** [#4238](https://github.com/leanprover/lean4/pull/4238), [#4294](https://github.com/leanprover/lean4/pull/4294),
[#4338](https://github.com/leanprover/lean4/pull/4338).
### Language server, widgets, and IDE extensions
* [#4066](https://github.com/leanprover/lean4/pull/4066) fixes features like "Find References" when browsing core Lean sources.
* [#4254](https://github.com/leanprover/lean4/pull/4254) allows embedding user widgets in structured messages.
Companion PR is [vscode-lean4#449](https://github.com/leanprover/vscode-lean4/pull/449).
* [#4445](https://github.com/leanprover/lean4/pull/4445) makes watchdog more resilient against badly behaving clients.
### Library
* [#4059](https://github.com/leanprover/lean4/pull/4059) upstreams many `List` and `Array` operations and theorems from Batteries.
* [#4055](https://github.com/leanprover/lean4/pull/4055) removes the unused `Inhabited` instance for `Subtype`.
* [#3967](https://github.com/leanprover/lean4/pull/3967) adds dates in existing `@[deprecated]` attributes.
* [#4231](https://github.com/leanprover/lean4/pull/4231) adds boilerplate `Char`, `UInt`, and `Fin` theorems.
* [#4205](https://github.com/leanprover/lean4/pull/4205) fixes the `MonadStore` type classes to use `semiOutParam`.
* [#4350](https://github.com/leanprover/lean4/pull/4350) renames `IsLawfulSingleton` to `LawfulSingleton`.
* `Nat`
* [#4094](https://github.com/leanprover/lean4/pull/4094) swaps `Nat.zero_or` and `Nat.or_zero`.
* [#4098](https://github.com/leanprover/lean4/pull/4098) and [#4145](https://github.com/leanprover/lean4/pull/4145)
change the definition of `Nat.mod` so that `n % (m + n)` reduces when `n` is literal without relying on well-founded recursion,
which becomes irreducible by default in [#4061](https://github.com/leanprover/lean4/pull/4061).
* [#4188](https://github.com/leanprover/lean4/pull/4188) redefines `Nat.testBit` to be more performant.
* Theorems: [#4199](https://github.com/leanprover/lean4/pull/4199).
* `Array`
* [#4074](https://github.com/leanprover/lean4/pull/4074) improves the functional induction principle `Array.feraseIdx.induct`.
* `List`
* [#4172](https://github.com/leanprover/lean4/pull/4172) removes `@[simp]` from `List.length_pos`.
* `Option`
* [#4037](https://github.com/leanprover/lean4/pull/4037) adds theorems to simplify `Option`-valued dependent if-then-else.
* [#4314](https://github.com/leanprover/lean4/pull/4314) removes `@[simp]` from `Option.bind_eq_some`.
* `BitVec`
* Theorems: [#3920](https://github.com/leanprover/lean4/pull/3920), [#4095](https://github.com/leanprover/lean4/pull/4095),
[#4075](https://github.com/leanprover/lean4/pull/4075), [#4148](https://github.com/leanprover/lean4/pull/4148),
[#4165](https://github.com/leanprover/lean4/pull/4165), [#4178](https://github.com/leanprover/lean4/pull/4178),
[#4200](https://github.com/leanprover/lean4/pull/4200), [#4201](https://github.com/leanprover/lean4/pull/4201),
[#4298](https://github.com/leanprover/lean4/pull/4298), [#4299](https://github.com/leanprover/lean4/pull/4299),
[#4257](https://github.com/leanprover/lean4/pull/4257), [#4179](https://github.com/leanprover/lean4/pull/4179),
[#4321](https://github.com/leanprover/lean4/pull/4321), [#4187](https://github.com/leanprover/lean4/pull/4187).
* [#4193](https://github.com/leanprover/lean4/pull/4193) adds simprocs for reducing `x >>> i` and `x <<< i` where `i` is a bitvector literal.
* [#4194](https://github.com/leanprover/lean4/pull/4194) adds simprocs for reducing `(x <<< i) <<< j` and `(x >>> i) >>> j` where `i` and `j` are natural number literals.
* [#4229](https://github.com/leanprover/lean4/pull/4229) redefines `rotateLeft`/`rotateRight` to use modulo reduction of shift offset.
* [0d3051](https://github.com/leanprover/lean4/commit/0d30517dca094a07bcb462252f718e713b93ffba) makes `<num>#<term>` bitvector literal notation global.
* `Char`/`String`
* [#4143](https://github.com/leanprover/lean4/pull/4143) modifies `String.substrEq` to avoid linter warnings in downstream code.
* [#4233](https://github.com/leanprover/lean4/pull/4233) adds simprocs for `Char` and `String` inequalities.
* [#4348](https://github.com/leanprover/lean4/pull/4348) upstreams Mathlib lemmas.
* [#4354](https://github.com/leanprover/lean4/pull/4354) upstreams basic `String` lemmas.
* `HashMap`
* [#4248](https://github.com/leanprover/lean4/pull/4248) fixes implicitness of typeclass arguments in `HashMap.ofList`.
* `IO`
* [#4036](https://github.com/leanprover/lean4/pull/4036) adds `IO.Process.getCurrentDir` and `IO.Process.setCurrentDir` for adjusting the current process's working directory.
* **Cleanup:** [#4077](https://github.com/leanprover/lean4/pull/4077), [#4189](https://github.com/leanprover/lean4/pull/4189),
[#4304](https://github.com/leanprover/lean4/pull/4304).
* **Docs:** [#4001](https://github.com/leanprover/lean4/pull/4001), [#4166](https://github.com/leanprover/lean4/pull/4166),
[#4332](https://github.com/leanprover/lean4/pull/4332).
### Lean internals
* **Defeq and WHNF algorithms**
* [#4029](https://github.com/leanprover/lean4/pull/4029) remove unnecessary `checkpointDefEq`
* [#4206](https://github.com/leanprover/lean4/pull/4206) fixes `isReadOnlyOrSyntheticOpaque` to respect metavariable depth.
* [#4217](https://github.com/leanprover/lean4/pull/4217) fixes missing occurs check for delayed assignments.
* **Definition transparency**
* [#4052](https://github.com/leanprover/lean4/pull/4052) adds validation to application of `@[reducible]`/`@[semireducible]`/`@[irreducible]` attributes (with `local`/`scoped` modifiers as well).
Setting `set_option allowUnsafeReductibility true` turns this validation off.
* **Inductive types**
* [#3591](https://github.com/leanprover/lean4/pull/3591) fixes a bug where indices could be incorrectly promoted to parameters.
* [#3398](https://github.com/leanprover/lean4/pull/3398) fixes a bug in the injectivity theorem generator.
* [#4342](https://github.com/leanprover/lean4/pull/4342) fixes elaboration of mutual inductives with instance parameters.
* **Diagnostics and profiling**
* [#3986](https://github.com/leanprover/lean4/pull/3986) adds option `trace.profiler.useHeartbeats` to switch `trace.profiler.threshold` to being in terms of heartbeats instead of milliseconds.
* [#4082](https://github.com/leanprover/lean4/pull/4082) makes `set_option diagnostics true` report kernel diagnostic information.
* **Typeclass resolution**
* [#4119](https://github.com/leanprover/lean4/pull/4119) fixes multiple issues with TC caching interacting with `synthPendingDepth`, adds `maxSynthPendingDepth` option with default value `1`.
* [#4210](https://github.com/leanprover/lean4/pull/4210) ensures local instance cache does not contain multiple copies of the same instance.
* [#4216](https://github.com/leanprover/lean4/pull/4216) fix handling of metavariables, to avoid needing to set the option `backward.synthInstance.canonInstances` to `false`.
* **Other fixes or improvements**
* [#4080](https://github.com/leanprover/lean4/pull/4080) fixes propagation of state for `Lean.Elab.Command.liftCoreM` and `Lean.Elab.Command.liftTermElabM`.
* [#3944](https://github.com/leanprover/lean4/pull/3944) makes the `Repr` deriving handler be consistent between `structure` and `inductive` for how types and proofs are erased.
* [#4113](https://github.com/leanprover/lean4/pull/4113) propagates `maxHeartbeats` to kernel to control "(kernel) deterministic timeout" error.
* [#4125](https://github.com/leanprover/lean4/pull/4125) reverts [#3970](https://github.com/leanprover/lean4/pull/3970) (monadic generalization of `FindExpr`).
* [#4128](https://github.com/leanprover/lean4/pull/4128) catches stack overflow in auto-bound implicits feature.
* [#4129](https://github.com/leanprover/lean4/pull/4129) adds `tryCatchRuntimeEx` combinator to replace `catchRuntimeEx` reader state.
* [#4155](https://github.com/leanprover/lean4/pull/4155) simplifies the expression canonicalizer.
* [#4151](https://github.com/leanprover/lean4/pull/4151) and [#4369](https://github.com/leanprover/lean4/pull/4369)
add many missing trace classes.
* [#4185](https://github.com/leanprover/lean4/pull/4185) makes congruence theorem generators clean up type annotations of argument types.
* [#4192](https://github.com/leanprover/lean4/pull/4192) fixes restoration of infotrees when auto-bound implicit feature is activated,
fixing a pretty printing error in hovers and strengthening the unused variable linter.
* [dfb496](https://github.com/leanprover/lean4/commit/dfb496a27123c3864571aec72f6278e2dad1cecf) fixes `declareBuiltin` to allow it to be called multiple times per declaration.
* Cleanup: [#4112](https://github.com/leanprover/lean4/pull/4112), [#4126](https://github.com/leanprover/lean4/pull/4126), [#4091](https://github.com/leanprover/lean4/pull/4091), [#4139](https://github.com/leanprover/lean4/pull/4139), [#4153](https://github.com/leanprover/lean4/pull/4153).
* Tests: [030406](https://github.com/leanprover/lean4/commit/03040618b8f9b35b7b757858483e57340900cdc4), [#4133](https://github.com/leanprover/lean4/pull/4133).
### Compiler, runtime, and FFI
* [#4100](https://github.com/leanprover/lean4/pull/4100) improves reset/reuse algorithm; it now runs a second pass relaxing the constraint that reused memory cells must only be for the exact same constructor.
* [#2903](https://github.com/leanprover/lean4/pull/2903) fixes segfault in old compiler from mishandling `noConfusion` applications.
* [#4311](https://github.com/leanprover/lean4/pull/4311) fixes bug in constant folding.
* [#3915](https://github.com/leanprover/lean4/pull/3915) documents the runtime memory layout for inductive types.
### Lake
* [#4057](https://github.com/leanprover/lean4/pull/4057) adds support for docstrings on `require` commands.
* [#4088](https://github.com/leanprover/lean4/pull/4088) improves hovers for `family_def` and `library_data` commands.
* [#4147](https://github.com/leanprover/lean4/pull/4147) adds default `README.md` to package templates
* [#4261](https://github.com/leanprover/lean4/pull/4261) extends `lake test` help page, adds help page for `lake check-test`,
adds `lake lint` and tag `@[lint_driver]`, adds support for specifying test and lint drivers from dependencies,
adds `testDriverArgs` and `lintDriverArgs` options, adds support for library test drivers,
makes `lake check-test` and `lake check-lint` only load the package without dependencies.
* [#4270](https://github.com/leanprover/lean4/pull/4270) adds `lake pack` and `lake unpack` for packing and unpacking Lake build artifacts from an archive.
* [#4083](https://github.com/leanprover/lean4/pull/4083)
Switches the manifest format to use `major.minor.patch` semantic
versions. Major version increments indicate breaking changes (e.g., new
required fields and semantic changes to existing fields). Minor version
increments (after `0.x`) indicate backwards-compatible extensions (e.g.,
adding optional fields, removing fields). This change is backwards
compatible. Lake will still successfully read old manifests with numeric
versions. It will treat the numeric version `N` as semantic version
`0.N.0`. Lake will also accept manifest versions with `-` suffixes
(e.g., `x.y.z-foo`) and then ignore the suffix.
* [#4273](https://github.com/leanprover/lean4/pull/4273) adds a lift from `JobM` to `FetchM` for backwards compatibility reasons.
* [#4351](https://github.com/leanprover/lean4/pull/4351) fixes `LogIO`-to-`CliM`-lifting performance issues.
* [#4343](https://github.com/leanprover/lean4/pull/4343) make Lake store the dependency trace for a build in
the cached build long and then verifies that it matches the trace of the current build before replaying the log.
* [#4402](https://github.com/leanprover/lean4/pull/4402) moves the cached log into the trace file (no more `.log.json`).
This means logs are no longer cached on fatal errors and this ensures that an out-of-date log is not associated with an up-to-date trace.
Separately, `.hash` file generation was changed to be more reliable as well.
The `.hash` files are deleted as part of the build and always regenerate with `--rehash`.
* **Other fixes or improvements**
* [#4056](https://github.com/leanprover/lean4/pull/4056) cleans up tests
* [#4244](https://github.com/leanprover/lean4/pull/4244) fixes `noRelease` test when Lean repo is tagged
* [#4346](https://github.com/leanprover/lean4/pull/4346) improves `tests/serve`
* [#4356](https://github.com/leanprover/lean4/pull/4356) adds build log path to the warning for a missing or invalid build log.
### DevOps
* [#3984](https://github.com/leanprover/lean4/pull/3984) adds a script (`script/rebase-stage0.sh`) for `git rebase -i` that automatically updates each stage0.
* [#4108](https://github.com/leanprover/lean4/pull/4108) finishes renamings from transition to Std to Batteries.
* [#4109](https://github.com/leanprover/lean4/pull/4109) adjusts the Github bug template to mention testing using [live.lean-lang.org](https://live.lean-lang.org).
* [#4136](https://github.com/leanprover/lean4/pull/4136) makes CI rerun only when `full-ci` label is added or removed.
* [#4175](https://github.com/leanprover/lean4/pull/4175) and [72b345](https://github.com/leanprover/lean4/commit/72b345c621a9a06d3a5a656da2b793a5eea5f168)
switch to using `#guard_msgs` to run tests as much as possible.
* [#3125](https://github.com/leanprover/lean4/pull/3125) explains the Lean4 `pygments` lexer.
* [#4247](https://github.com/leanprover/lean4/pull/4247) sets up a procedure for preparing release notes.
* [#4032](https://github.com/leanprover/lean4/pull/4032) modernizes build instructions and workflows.
* [#4255](https://github.com/leanprover/lean4/pull/4255) moves some expensive checks from merge queue to releases.
* [#4265](https://github.com/leanprover/lean4/pull/4265) adds aarch64 macOS as native compilation target for CI.
* [f05a82](https://github.com/leanprover/lean4/commit/f05a82799a01569edeb5e2594cd7d56282320f9e) restores macOS aarch64 install suffix in CI
* [#4317](https://github.com/leanprover/lean4/pull/4317) updates build instructions for macOS.
* [#4333](https://github.com/leanprover/lean4/pull/4333) adjusts workflow to update Batteries in manifest when creating `lean-pr-testing-NNNN` Mathlib branches.
* [#4355](https://github.com/leanprover/lean4/pull/4355) simplifies `lean4checker` step of release checklist.
* [#4361](https://github.com/leanprover/lean4/pull/4361) adds installing elan to `pr-release` CI step.
### Breaking changes
While most changes could be considered to be a breaking change, this section makes special note of API changes.
* `Nat.zero_or` and `Nat.or_zero` have been swapped ([#4094](https://github.com/leanprover/lean4/pull/4094)).
* `IsLawfulSingleton` is now `LawfulSingleton` ([#4350](https://github.com/leanprover/lean4/pull/4350)).
* `BitVec.rotateLeft` and `BitVec.rotateRight` now take the shift modulo the bitwidth ([#4229](https://github.com/leanprover/lean4/pull/4229)).
* These are no longer simp lemmas:
`List.length_pos` ([#4172](https://github.com/leanprover/lean4/pull/4172)),
`Option.bind_eq_some` ([#4314](https://github.com/leanprover/lean4/pull/4314)).
* Types in `let` and `have` (both the expressions and tactics) may fail to elaborate due to new restrictions on what sorts of elaboration problems may be postponed ([#4096](https://github.com/leanprover/lean4/pull/4096)).
In particular, tactics embedded in the type will no longer make use of the type of `value` in expressions such as `let x : type := value; body`.
* Now functions defined by well-founded recursion are marked with `@[irreducible]` by default ([#4061](https://github.com/leanprover/lean4/pull/4061)).
Existing proofs that hold by definitional equality (e.g. `rfl`) can be
rewritten to explictly unfold the function definition (using `simp`,
`unfold`, `rw`), or the recursive function can be temporarily made
semireducible (using `unseal f in` before the command), or the function
definition itself can be marked as `@[semireducible]` to get the previous
behavior.
* Due to [#3929](https://github.com/leanprover/lean4/pull/3929):
* The `MessageData.ofPPFormat` constructor has been removed.
Its functionality has been split into two:
- for lazy structured messages, please use `MessageData.lazy`;
- for embedding `Format` or `FormatWithInfos`, use `MessageData.ofFormatWithInfos`.
An example migration can be found in [#3929](https://github.com/leanprover/lean4/pull/3929/files#diff-5910592ab7452a0e1b2616c62d22202d2291a9ebb463145f198685aed6299867L109).
* The `MessageData.ofFormat` constructor has been turned into a function.
If you need to inspect `MessageData`, you can pattern-match on `MessageData.ofFormatWithInfos`.
v4.8.0
---------

View File

@@ -5,7 +5,8 @@ See below for the checklist for release candidates.
We'll use `v4.6.0` as the intended release version as a running example.
- One week before the planned release, ensure that someone has written the first draft of the release blog post
- One week before the planned release, ensure that (1) someone has written the release notes and (2) someone has written the first draft of the release blog post.
If there is any material in `./releases_drafts/`, then the release notes are not done. (See the section "Writing the release notes".)
- `git checkout releases/v4.6.0`
(This branch should already exist, from the release candidates.)
- `git pull`
@@ -13,13 +14,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
- `set(LEAN_VERSION_IS_RELEASE 1)`
- (both of these should already be in place from the release candidates)
- It is possible that the `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Run `git diff master RELEASES.md`.
- You should expect to see additons on `master` in the `v4.7.0-rc1` section; ignore these.
(i.e. the new release notes for the upcoming release candidate).
- Reconcile discrepancies in the `v4.6.0` section,
usually via copy and paste and a commit to `releases/v4.6.0`.
- `git tag v4.6.0`
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
- Now wait, while CI runs.
@@ -30,8 +24,9 @@ We'll use `v4.6.0` as the intended release version as a running example.
you may want to start on the release candidate checklist now.
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
- Edit the release notes on Github to select the "Set as the latest release".
- Copy and paste the Github release notes from the previous releases candidate for this version
(e.g. `v4.6.0-rc1`), and quickly sanity check.
- Follow the instructions in creating a release candidate for the "GitHub release notes" step,
now that we have a written `RELEASES.md` section.
Do a quick sanity check.
- Next, we will move a curated list of downstream repos to the latest stable release.
- For each of the repositories listed below:
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
@@ -94,6 +89,10 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Toolchain bump PR including updated Lake manifest
- Create and push the tag
- Merge the tag into `stable`
- The `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Replace the `v4.6.0` section on `master` with the `v4.6.0` section on `releases/v4.6.0`
and commit this to `master`.
- Merge the release announcement PR for the Lean website - it will be deployed automatically
- Finally, make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
@@ -104,7 +103,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
## Optimistic(?) time estimates:
- Initial checks and push the tag: 30 minutes.
- Note that if `RELEASES.md` has discrepancies this could take longer!
- Waiting for the release: 60 minutes.
- Fixing release notes: 10 minutes.
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
@@ -131,29 +129,26 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
```
- In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
- Our current goal is to have written release notes only about major language features or breaking changes,
and to rely on automatically generated release notes for bugfixes and minor changes.
- Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
- Commit your changes to `RELEASES.md`, and push.
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.`
- We will rely on automatically generated release notes for release candidates,
and the written release notes will be used for stable versions only.
It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion.
- In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
- Commit your changes to `src/CMakeLists.txt`, and push.
- `git tag v4.7.0-rc1`
- `git push origin v4.7.0-rc1`
- Ping the FRO Zulip that release notes need to be written. The release notes do not block completing the rest of this checklist.
- Now wait, while CI runs.
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
- This step can take up to an hour.
- Once the release appears at https://github.com/leanprover/lean4/releases/
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/
- Edit the release notes on Github to select the "Set as a pre-release box".
- Copy the section of `RELEASES.md` for this version into the Github release notes.
- Use the title "Changes since v4.6.0 (from RELEASES.md)"
- Then in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
- This will add a list of all the commits since the last stable version.
- If release notes have been written already, copy the section of `RELEASES.md` for this version into the Github release notes
and use the title "Changes since v4.6.0 (from RELEASES.md)".
- Otherwise, in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
This will add a list of all the commits since the last stable version.
- Delete anything already mentioned in the hand-written release notes above.
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
@@ -179,6 +174,9 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- We do this for the same list of repositories as for stable releases, see above.
As above, there are dependencies between these, and so the process above is iterative.
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
It is essential for Mathlib CI that you then create the next `bump/v4.8.0` branch
for the next development cycle.
Set the `lean-toolchain` file on this branch to same `nightly` you used for this release.
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
@@ -189,8 +187,17 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
Please also make sure that whoever is handling social media knows the release is out.
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
- Removes `(in development)` from the section heading in `RELEASES.md` for `v4.7.0`,
and creates a new `v4.8.0 (in development)` section heading.
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
```
Release candidate, release notes will be copied from `branch releases/v4.7.0` once completed.
```
and inserts the following section before that section:
```
v4.8.0
----------
Development in progress.
```
- Removes all the entries from the `./releases_drafts/` folder.
## Time estimates:
Slightly longer than the corresponding steps for a stable release.
@@ -224,3 +231,18 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
* It is always okay to merge in the following directions:
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
Please remember to push any merges you make to intermediate steps!
# Writing the release notes
We are currently trying a system where release notes are compiled all at once from someone looking through the commit history.
The exact steps are a work in progress.
Here is the general idea:
* The work is done right on the `releases/v4.6.0` branch sometime after it is created but before the stable release is made.
The release notes for `v4.6.0` will be copied to `master`.
* There can be material for release notes entries in commit messages.
* There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
See `./releases_drafts/README.md` for more information.
* The release notes should be written from a downstream expert user's point of view.
This section will be updated when the next release notes are written (for `v4.10.0`).

View File

@@ -149,4 +149,4 @@ def fact : Expr ctx (Ty.fn Ty.int Ty.int) :=
(op (·*·) (delay fun _ => app fact (op (·-·) (var stop) (val 1))) (var stop)))
decreasing_by sorry
#eval fact.interp Env.nil 10
#eval! fact.interp Env.nil 10

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -13,7 +13,7 @@ Recall that nonnegative numerals are considered to be a `Nat` if there are no ty
The operator `/` for `Int` implements integer division.
```lean
#eval -10 / 4 -- -2
#eval -10 / 4 -- -3
```
Similar to `Nat`, the internal representation of `Int` is optimized. Small integers are

View File

@@ -7,12 +7,17 @@ See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
![installing the vscode-lean4 extension](images/code-ext.png)
![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`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Setup: 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)
![show setup guide](images/show-setup-guide.png)
1. Follow the Lean 4 setup guide. It will walk you through learning resources for Lean 4, teach you how to set up Lean's dependencies on your platform, install Lean 4 for you at the click of a button and help you set up your first project.
1. Follow the Lean 4 setup guide. It will:
![setup guide](images/setup_guide.png)
- walk you through learning resources for Lean,
- teach you how to set up Lean's dependencies on your platform,
- install Lean 4 for you at the click of a button,
- help you set up your first project.
![setup guide](images/setup_guide.png)

View File

@@ -125,8 +125,10 @@ rec {
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
${if stdenv.isDarwin then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Lean.staticLib}/libStd.a -Wl,-force_load,${Lean.staticLib}/libLean.a -Wl,-force_load,${leancpp}/lib/lean/libleancpp.a ${leancpp}/lib/libleanrt_initial-exec.a -lc++"
else "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} -lm ${stdlibLinkFlags} \
${if stdenv.isDarwin
then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Std.staticLib}/libStd.a -Wl,-force_load,${Lean.staticLib}/libLean.a -Wl,-force_load,${leancpp}/lib/lean/libleancpp.a ${leancpp}/lib/libleanrt_initial-exec.a -lc++"
else "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} \
-lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName
'';

View File

@@ -224,7 +224,8 @@ with builtins; let
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;
objects = mapAttrs (_: m: m.obj) mods';
staticLib = runCommand "${name}-lib" { buildInputs = [ stdenv.cc.bintools.bintools ]; } ''
bintools = if stdenv.isDarwin then darwin.cctools else stdenv.cc.bintools.bintools;
staticLib = runCommand "${name}-lib" { buildInputs = [ bintools ]; } ''
mkdir -p $out
ar Trcs $out/lib${libName}.a ${lib.concatStringsSep " " (map (drv: "${drv}/${drv.oPath}") (attrValues objects))};
'';

View File

@@ -0,0 +1,65 @@
* Structural recursion can now be explicitly requested using
```
termination_by structural x
```
in analogy to the existing `termination_by x` syntax that causes well-founded recursion to be used.
(#4542)
* The `termination_by?` syntax no longer forces the use of well-founded recursion, and when structural
recursion is inferred, will print the result using the `termination_by` syntax.
* Mutual structural recursion is supported now. This supports both mutual recursion over a non-mutual
data type, as well as recursion over mutual or nested data types:
```lean
mutual
def Even : Nat → Prop
| 0 => True
| n+1 => Odd n
def Odd : Nat → Prop
| 0 => False
| n+1 => Even n
end
mutual
inductive A
| other : B → A
| empty
inductive B
| other : A → B
| empty
end
mutual
def A.size : A → Nat
| .other b => b.size + 1
| .empty => 0
def B.size : B → Nat
| .other a => a.size + 1
| .empty => 0
end
inductive Tree where | node : List Tree → Tree
mutual
def Tree.size : Tree → Nat
| node ts => Tree.list_size ts
def Tree.list_size : List Tree → Nat
| [] => 0
| t::ts => Tree.size t + Tree.list_size ts
end
```
Functional induction principles are generated for these functions as well (`A.size.induct`, `A.size.mutual_induct`).
Nested structural recursion is still not supported.
PRs #4639, #4715, #4642, #4656, #4684, #4715, #4728, #4575, #4731, #4658, #4734, #4738, #4718,
#4733, #4787, #4788, #4789, #4807, #4772
* A bugfix in the structural recursion code may in some cases break existing code, when a parameter
of the type of the recursive argument is bound behind indices of that type. This can usually be
fixed by reordering the parameters of the function (PR #4672)

View File

@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.10)
cmake_policy(SET CMP0054 NEW)
cmake_policy(SET CMP0110 NEW)
if(NOT (${CMAKE_GENERATOR} MATCHES "Unix Makefiles"))
message(FATAL_ERROR "The only supported CMake generator at the moment is 'Unix Makefiles'")
endif()
@@ -9,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 10)
set(LEAN_VERSION_MINOR 11)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -300,11 +301,11 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH)
string(APPEND LEANSHARED_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}")
endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lleanrt")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lnodefs.js -lleanrt")
else()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -lStd -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
endif()
set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags")
@@ -313,7 +314,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LEAN_CXX_STDLIB "-lc++")
endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB} -lStd")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
# in local builds, link executables and not just dynlibs against C++ stdlib as well,
@@ -510,13 +511,13 @@ file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib)
# set up libInit_shared only on Windows; see also stdlib.make.in
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libInit.a.export ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libInit.a.export ${CMAKE_BINARY_DIR}/lib/temp/libStd.a.export ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libStd.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libStd.a.export ${CMAKE_BINARY_DIR}/lib/temp/libLean.a.export -lleancpp -Wl,--no-whole-archive -lInit_shared -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLean.a.export -lleancpp -Wl,--no-whole-archive -lInit_shared -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
else()
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
endif()

View File

@@ -67,12 +67,8 @@ theorem ite_some_none_eq_none [Decidable P] :
-- This is not marked as `simp` as it is already handled by `dite_eq_right_iff`.
theorem dite_some_none_eq_none [Decidable P] {x : P α} :
(if h : P then some (x h) else none) = none ¬P := by
simp only [dite_eq_right_iff]
rfl
simp
@[simp] theorem dite_some_none_eq_some [Decidable P] {x : P α} {y : α} :
(if h : P then some (x h) else none) = some y h : P, x h = y := by
by_cases h : P <;> simp only [h, dite_cond_eq_true, dite_cond_eq_false, Option.some.injEq,
false_iff, not_exists]
case pos => exact fun h_eq Exists.intro h h_eq, fun h_exists => h_exists.2
case neg => exact fun h_false _ h_false
by_cases h : P <;> simp [h]

View File

@@ -188,13 +188,13 @@ theorem ext {x y : StateT σ m α} (h : ∀ s, x.run s = y.run s) : x = y :=
@[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
@[simp] theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp] theorem run_monadMap [Monad m] [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ)
: (monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ) :
(monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
show (f >>= fun g => g <$> x).run s = _

View File

@@ -64,5 +64,5 @@ end StateRefT'
instance (ω σ : Type) (m : Type Type) : MonadControl m (StateRefT' ω σ m) :=
inferInstanceAs (MonadControl m (ReaderT _ _))
instance {m : Type Type} {ω σ : Type} [MonadFinally m] [Monad m] : MonadFinally (StateRefT' ω σ m) :=
instance {m : Type Type} {ω σ : Type} [MonadFinally m] : MonadFinally (StateRefT' ω σ m) :=
inferInstanceAs (MonadFinally (ReaderT _ _))

View File

@@ -474,6 +474,8 @@ class LawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert
insert_emptyc_eq (x : α) : (insert x : β) = singleton x
export LawfulSingleton (insert_emptyc_eq)
attribute [simp] insert_emptyc_eq
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
class Sep (α : outParam <| Type u) (γ : Type v) where
/-- Computes `{ a ∈ c | p a }`. -/
@@ -701,7 +703,7 @@ theorem Ne.elim (h : a ≠ b) : a = b → False := h
theorem Ne.irrefl (h : a a) : False := h rfl
theorem Ne.symm (h : a b) : b a := fun h₁ => h (h₁.symm)
@[symm] theorem Ne.symm (h : a b) : b a := fun h₁ => h (h₁.symm)
theorem ne_comm {α} {a b : α} : a b b a := Ne.symm, Ne.symm
@@ -754,7 +756,7 @@ noncomputable def HEq.elim {α : Sort u} {a : α} {p : α → Sort v} {b : α} (
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
HEq.ndrecOn h₁ h₂
theorem HEq.symm (h : HEq a b) : HEq b a :=
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
h.rec (HEq.refl a)
theorem heq_of_eq (h : a = a') : HEq a a' :=
@@ -810,15 +812,15 @@ instance : Trans Iff Iff Iff where
theorem Eq.comm {a b : α} : a = b b = a := Iff.intro Eq.symm Eq.symm
theorem eq_comm {a b : α} : a = b b = a := Eq.comm
theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
@[symm] theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
theorem Iff.comm: (a b) (b a) := Iff.intro Iff.symm Iff.symm
theorem iff_comm : (a b) (b a) := Iff.comm
theorem And.symm : a b b a := fun ha, hb => hb, ha
@[symm] theorem And.symm : a b b a := fun ha, hb => hb, ha
theorem And.comm : a b b a := Iff.intro And.symm And.symm
theorem and_comm : a b b a := And.comm
theorem Or.symm : a b b a := .rec .inr .inl
@[symm] theorem Or.symm : a b b a := .rec .inr .inl
theorem Or.comm : a b b a := Iff.intro Or.symm Or.symm
theorem or_comm : a b b a := Or.comm
@@ -1089,19 +1091,23 @@ def InvImage {α : Sort u} {β : Sort v} (r : β → β → Prop) (f : α → β
fun a₁ a₂ => r (f a₁) (f a₂)
/--
The transitive closure `r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `r a z` if and only if there exists a sequence
The transitive closure `TransGen r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `TransGen r a z` if and only if there exists a sequence
`a r b r ... r z` of length at least 1 connecting `a` to `z`.
-/
inductive TC {α : Sort u} (r : α α Prop) : α α Prop where
/-- If `r a b` then `r a b`. This is the base case of the transitive closure. -/
| base : a b, r a b TC r a b
inductive Relation.TransGen {α : Sort u} (r : α α Prop) : α α Prop
/-- If `r a b` then `TransGen r a b`. This is the base case of the transitive closure. -/
| single {a b} : r a b TransGen r a b
/-- The transitive closure is transitive. -/
| trans : a b c, TC r a b TC r b c TC r a c
| tail {a b c} : TransGen r a b r b c TransGen r a c
/-- Deprecated synonym for `Relation.TransGen`. -/
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
/-! # Subtype -/
namespace Subtype
theorem existsOfSubtype {α : Type u} {p : α Prop} : { x // p x } Exists (fun x => p x)
| a, h => a, h
@@ -1198,9 +1204,13 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
/-! # Dependent products -/
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
theorem PSigma.exists {α : Sort u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
| x, hx => x, hx
@[deprecated PSigma.exists (since := "2024-07-27")]
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x) :=
PSigma.exists
protected theorem PSigma.eta {α : Sort u} {β : α Sort v} {a₁ a₂ : α} {b₁ : β a₁} {b₂ : β a₂}
(h₁ : a₁ = a₂) (h₂ : Eq.ndrec b₁ h₁ = b₂) : PSigma.mk a₁ b₁ = PSigma.mk a₂ b₂ := by
subst h₁
@@ -1362,6 +1372,9 @@ theorem iff_false_right (ha : ¬a) : (b ↔ a) ↔ ¬b := Iff.comm.trans (iff_fa
theorem of_iff_true (h : a True) : a := h.mpr trivial
theorem iff_true_intro (h : a) : a True := iff_of_true h trivial
theorem eq_iff_true_of_subsingleton [Subsingleton α] (x y : α) : x = y True :=
iff_true_intro (Subsingleton.elim ..)
theorem not_of_iff_false : (p False) ¬p := Iff.mp
theorem iff_false_intro (h : ¬a) : a False := iff_of_false h id
@@ -1539,7 +1552,7 @@ protected abbrev rec
(q : Quot r) : motive q :=
Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2)
@[inherit_doc Quot.rec] protected abbrev recOn
@[inherit_doc Quot.rec, elab_as_elim] protected abbrev recOn
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
@@ -1550,7 +1563,7 @@ protected abbrev rec
Dependent induction principle for a quotient, when the target type is a `Subsingleton`.
In this case the quotient's side condition is trivial so any function can be lifted.
-/
protected abbrev recOnSubsingleton
@[elab_as_elim] protected abbrev recOnSubsingleton
[h : (a : α) Subsingleton (motive (Quot.mk r a))]
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
@@ -1867,7 +1880,7 @@ instance : Subsingleton (Squash α) where
/--
`Antisymm (·≤·)` says that `(·≤·)` is antisymmetric, that is, `a ≤ b → b ≤ a → a = b`.
-/
class Antisymm {α : Sort u} (r : α α Prop) where
class Antisymm {α : Sort u} (r : α α Prop) : Prop where
/-- An antisymmetric relation `(·≤·)` satisfies `a ≤ b → b ≤ a → a = b`. -/
antisymm {a b : α} : r a b r b a a = b

View File

@@ -35,3 +35,5 @@ import Init.Data.Queue
import Init.Data.Channel
import Init.Data.Cast
import Init.Data.Sum
import Init.Data.BEq
import Init.Data.Subtype

View File

@@ -10,5 +10,7 @@ import Init.Data.Array.BinSearch
import Init.Data.Array.InsertionSort
import Init.Data.Array.DecidableEq
import Init.Data.Array.Mem
import Init.Data.Array.Attach
import Init.Data.Array.BasicAux
import Init.Data.Array.Lemmas
import Init.Data.Array.TakeDrop

View File

@@ -0,0 +1,29 @@
/-
Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner, Mario Carneiro
-/
prelude
import Init.Data.Array.Mem
import Init.Data.List.Attach
namespace Array
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
`Array {x // P x}` is the same as the input `Array α`.
-/
@[inline] private unsafe def attachWithImpl
(xs : Array α) (P : α Prop) (_ : x xs, P x) : Array {x // P x} := unsafeCast xs
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
with the same elements but in the type `{x // P x}`. -/
@[implemented_by attachWithImpl] def attachWith
(xs : Array α) (P : α Prop) (H : x xs, P x) : Array {x // P x} :=
xs.data.attachWith P fun x h => H x (Array.Mem.mk h)
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
with the same elements but in the type `{x // x ∈ xs}`. -/
@[inline] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
end Array

View File

@@ -50,6 +50,13 @@ instance : Inhabited (Array α) where
def singleton (v : α) : Array α :=
mkArray 1 v
/-- Low-level version of `size` that directly queries the C array object cached size.
While this is not provable, `usize` always returns the exact size of the array since
the implementation only supports arrays of size less than `USize.size`.
-/
@[extern "lean_array_size", simp]
def usize (a : @& Array α) : USize := a.size.toUSize
/-- Low-level version of `fget` which is as fast as a C array read.
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
`fget` may be slightly slower than `uget`. -/
@@ -60,8 +67,6 @@ def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
getElem xs i h := xs.uget i h
instance : LawfulGetElem (Array α) USize α fun xs i => i.toNat < xs.size where
def back [Inhabited α] (a : Array α) : α :=
a.get! (a.size - 1)
@@ -103,7 +108,7 @@ def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
a'.set (size_set a i v₂ j) v₁
/--
Swaps two entries in an array, or panics if either index is out of bounds.
Swaps two entries in an array, or returns the array unchanged if either index is out of bounds.
This will perform the update destructively provided that `a` has a reference
count of 1 when called.
@@ -176,7 +181,7 @@ def modifyOp (self : Array α) (idx : Nat) (f : αα) : Array α :=
This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < usizeSz` to true. -/
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Array α) (b : β) (f : α β m (ForInStep β)) : m β :=
let sz := USize.ofNat as.size
let sz := as.usize
let rec @[specialize] loop (i : USize) (b : β) : m β := do
if i < sz then
let a := as.uget i lcProof
@@ -282,7 +287,7 @@ def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
/-- See comment at `forInUnsafe` -/
@[inline]
unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α m β) (as : Array α) : m (Array β) :=
let sz := USize.ofNat as.size
let sz := as.usize
let rec @[specialize] map (i : USize) (r : Array NonScalar) : m (Array PNonScalar.{v}) := do
if i < sz then
let v := r.uget i lcProof

View File

@@ -6,7 +6,7 @@ Authors: Mario Carneiro
prelude
import Init.Data.Nat.MinMax
import Init.Data.Nat.Lemmas
import Init.Data.List.Lemmas
import Init.Data.List.Monadic
import Init.Data.Fin.Basic
import Init.Data.Array.Mem
import Init.TacticsExtra
@@ -51,7 +51,7 @@ theorem foldlM_eq_foldlM_data.aux [Monad m]
simp [foldlM_eq_foldlM_data.aux f arr i (j+1) H]
rw (config := {occs := .pos [2]}) [ List.get_drop_eq_drop _ _ _]
rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
theorem foldlM_eq_foldlM_data [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
@@ -141,7 +141,7 @@ where
· rw [ List.get_drop_eq_drop _ i _]
simp only [aux (i + 1), map_eq_pure_bind, data_length, List.foldlM_cons, bind_assoc, pure_bind]
rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
termination_by arr.size - i
decreasing_by decreasing_trivial_pre_omega
@@ -220,7 +220,7 @@ theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size ≤ i) : a[i]? = no
theorem getD_get? (a : Array α) (i : Nat) (d : α) :
Option.getD a[i]? d = if p : i < a.size then a[i]'p else d := by
if h : i < a.size then
simp [setD, h, getElem?]
simp [setD, h, getElem?_def]
else
have p : i a.size := Nat.le_of_not_gt h
simp [setD, getElem?_len_le _ p, h]
@@ -383,18 +383,18 @@ theorem get?_push {a : Array α} : (a.push x)[i]? = if i = a.size then some x el
| Or.inl g =>
have h1 : i < a.size + 1 := by omega
have h2 : i a.size := by omega
simp [getElem?, size_push, g, h1, h2, get_push_lt]
simp [getElem?_def, size_push, g, h1, h2, get_push_lt]
| Or.inr (Or.inl heq) =>
simp [heq, getElem?_pos, get_push_eq]
| Or.inr (Or.inr g) =>
simp only [getElem?, size_push]
simp only [getElem?_def, size_push]
have h1 : ¬ (i < a.size) := by omega
have h2 : ¬ (i < a.size + 1) := by omega
have h3 : i a.size := by omega
simp [h1, h2, h3]
@[simp] theorem get?_size {a : Array α} : a[a.size]? = none := by
simp only [getElem?, Nat.lt_irrefl, dite_false]
simp only [getElem?_def, Nat.lt_irrefl, dite_false]
@[simp] theorem data_set (a : Array α) (i v) : (a.set i v).data = a.data.set i.1 v := rfl

View File

@@ -23,7 +23,7 @@ theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a <
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)
@[simp] theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)

View File

@@ -47,8 +47,6 @@ def get (s : Subarray α) (i : Fin s.size) : α :=
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem (Subarray α) Nat α fun xs i => i < xs.size where
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
if h : i < s.size then s.get i, h else v₀

View File

@@ -0,0 +1,17 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop
namespace Array
theorem exists_of_uset (self : Array α) (i d h) :
l₁ l₂, self.data = l₁ ++ self[i] :: l₂ List.length l₁ = i.toNat
(self.uset i d h).data = l₁ ++ d :: l₂ := by
simpa [Array.getElem_eq_data_getElem] using List.exists_of_set _
end Array

60
src/Init/Data/BEq.lean Normal file
View File

@@ -0,0 +1,60 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Markus Himmel
-/
prelude
import Init.Data.Bool
set_option linter.missingDocs true
/-- `PartialEquivBEq α` says that the `BEq` implementation is a
partial equivalence relation, that is:
* it is symmetric: `a == b → b == a`
* it is transitive: `a == b → b == c → a == c`.
-/
class PartialEquivBEq (α) [BEq α] : Prop where
/-- Symmetry for `BEq`. If `a == b` then `b == a`. -/
symm : (a : α) == b b == a
/-- Transitivity for `BEq`. If `a == b` and `b == c` then `a == c`. -/
trans : (a : α) == b b == c a == c
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- Reflexivity for `BEq`. -/
refl : (a : α) == a
/-- `EquivBEq` says that the `BEq` implementation is an equivalence relation. -/
class EquivBEq (α) [BEq α] extends PartialEquivBEq α, ReflBEq α : Prop
@[simp]
theorem BEq.refl [BEq α] [ReflBEq α] {a : α} : a == a :=
ReflBEq.refl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.refl
theorem BEq.symm [BEq α] [PartialEquivBEq α] {a b : α} : a == b b == a :=
PartialEquivBEq.symm
theorem BEq.comm [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = (b == a) :=
Bool.eq_iff_iff.2 BEq.symm, BEq.symm
theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = false (b == a) = false :=
BEq.comm (α := α) id
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b b == c a == c :=
PartialEquivBEq.trans
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
(a == b) = false b == c (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))
theorem BEq.neq_of_beq_of_neq [BEq α] [PartialEquivBEq α] {a b c : α} :
a == b (b == c) = false (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₂ (BEq.trans (BEq.symm h₁) h₃)
instance (priority := low) [BEq α] [LawfulBEq α] : EquivBEq α where
refl := LawfulBEq.rfl
symm h := (beq_iff_eq _ _).2 <| Eq.symm <| (beq_iff_eq _ _).1 h
trans hab hbc := (beq_iff_eq _ _).2 <| ((beq_iff_eq _ _).1 hab).trans <| (beq_iff_eq _ _).1 hbc

View File

@@ -98,6 +98,37 @@ theorem carry_succ (i : Nat) (x y : BitVec w) (c : Bool) :
exact mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ ..
cases x.toNat.testBit i <;> cases y.toNat.testBit i <;> (simp; omega)
/--
If `x &&& y = 0`, then the carry bit `(x + y + 0)` is always `false` for any index `i`.
Intuitively, this is because a carry is only produced when at least two of `x`, `y`, and the
previous carry are true. However, since `x &&& y = 0`, at most one of `x, y` can be true,
and thus we never have a previous carry, which means that the sum cannot produce a carry.
-/
theorem carry_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) : carry i x y false = false := by
induction i with
| zero => simp
| succ i ih =>
replace h := congrArg (·.getLsb i) h
simp_all [carry_succ]
/-- The final carry bit when computing `x + y + c` is `true` iff `x.toNat + y.toNat + c.toNat ≥ 2^w`. -/
theorem carry_width {x y : BitVec w} :
carry w x y c = decide (x.toNat + y.toNat + c.toNat 2^w) := by
simp [carry]
/--
If `x &&& y = 0`, then addition does not overflow, and thus `(x + y).toNat = x.toNat + y.toNat`.
-/
theorem toNat_add_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) :
(x + y).toNat = x.toNat + y.toNat := by
rw [toNat_add]
apply Nat.mod_eq_of_lt
suffices ¬ decide (x.toNat + y.toNat + false.toNat 2^w) by
simp only [decide_eq_true_eq] at this
omega
rw [ carry_width]
simp [not_eq_true, carry_of_and_eq_zero h]
/-- Carry function for bitwise addition. -/
def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, Bool.xor x (Bool.xor y c))
@@ -159,6 +190,21 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
theorem allOnes_sub_eq_not (x : BitVec w) : allOnes w - x = ~~~x := by
rw [ add_not_self x, BitVec.add_comm, add_sub_cancel]
/-- Addition of bitvectors is the same as bitwise or, if bitwise and is zero. -/
theorem add_eq_or_of_and_eq_zero {w : Nat} (x y : BitVec w)
(h : x &&& y = 0#w) : x + y = x ||| y := by
rw [add_eq_adc, adc, iunfoldr_replace (fun _ => false) (x ||| y)]
· rfl
· simp only [adcb, atLeastTwo, Bool.and_false, Bool.or_false, bne_false, getLsb_or,
Prod.mk.injEq, and_eq_false_imp]
intros i
replace h : (x &&& y).getLsb i = (0#w).getLsb i := by rw [h]
simp only [getLsb_and, getLsb_zero, and_eq_false_imp] at h
constructor
· intros hx
simp_all [hx]
· by_cases hx : x.getLsb i <;> simp_all [hx]
/-! ### Negation -/
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
@@ -235,4 +281,154 @@ theorem sle_eq_carry (x y : BitVec w) :
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
/-! ### mul recurrence for bitblasting -/
/--
A recurrence that describes multiplication as repeated addition.
Is useful for bitblasting multiplication.
-/
def mulRec (l r : BitVec w) (s : Nat) : BitVec w :=
let cur := if r.getLsb s then (l <<< s) else 0
match s with
| 0 => cur
| s + 1 => mulRec l r s + cur
theorem mulRec_zero_eq (l r : BitVec w) :
mulRec l r 0 = if r.getLsb 0 then l else 0 := by
simp [mulRec]
theorem mulRec_succ_eq (l r : BitVec w) (s : Nat) :
mulRec l r (s + 1) = mulRec l r s + if r.getLsb (s + 1) then (l <<< (s + 1)) else 0 := rfl
/--
Recurrence lemma: truncating to `i+1` bits and then zero extending to `w`
equals truncating upto `i` bits `[0..i-1]`, and then adding the `i`th bit of `x`.
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow (x : BitVec w) (i : Nat) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) + (x &&& twoPow w i) := by
rw [add_eq_or_of_and_eq_zero]
· ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp
· simp only [getLsb_twoPow, hik, decide_False, Bool.and_false, Bool.or_false]
by_cases hik' : k < (i + 1)
· have hik'' : k < i := by omega
simp [hik', hik'']
· have hik'' : ¬ (k < i) := by omega
simp [hik', hik'']
· ext k
simp
by_cases hi : x.getLsb i <;> simp [hi] <;> omega
/--
Recurrence lemma: multiplying `l` with the first `s` bits of `r` is the
same as truncating `r` to `s` bits, then zero extending to the original length,
and performing the multplication. -/
theorem mulRec_eq_mul_signExtend_truncate (l r : BitVec w) (s : Nat) :
mulRec l r s = l * ((r.truncate (s + 1)).zeroExtend w) := by
induction s
case zero =>
simp only [mulRec_zero_eq, ofNat_eq_ofNat, Nat.reduceAdd]
by_cases r.getLsb 0
case pos hr =>
simp only [hr, reduceIte, truncate, zeroExtend_one_eq_ofBool_getLsb_zero,
hr, ofBool_true, ofNat_eq_ofNat]
rw [zeroExtend_ofNat_one_eq_ofNat_one_of_lt (by omega)]
simp
case neg hr =>
simp [hr, zeroExtend_one_eq_ofBool_getLsb_zero]
case succ s' hs =>
rw [mulRec_succ_eq, hs]
have heq :
(if r.getLsb (s' + 1) = true then l <<< (s' + 1) else 0) =
(l * (r &&& (BitVec.twoPow w (s' + 1)))) := by
simp only [ofNat_eq_ofNat, and_twoPow]
by_cases hr : r.getLsb (s' + 1) <;> simp [hr]
rw [heq, BitVec.mul_add, zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow]
theorem getLsb_mul (x y : BitVec w) (i : Nat) :
(x * y).getLsb i = (mulRec x y w).getLsb i := by
simp only [mulRec_eq_mul_signExtend_truncate]
rw [truncate, truncate_eq_zeroExtend, truncate_eq_zeroExtend,
truncate_truncate_of_le]
· simp
· omega
/-! ## shiftLeft recurrence for bitblasting -/
/--
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
The theorem `shiftLeft_eq_shiftLeftRec` proves the equivalence of `(x <<< y)` and `shiftLeftRec`.
Together with equations `shiftLeftRec_zero`, `shiftLeftRec_succ`,
this allows us to unfold `shiftLeft` into a circuit for bitblasting.
-/
def shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
match n with
| 0 => x <<< shiftAmt
| n + 1 => (shiftLeftRec x y n) <<< shiftAmt
@[simp]
theorem shiftLeftRec_zero {x : BitVec w₁} {y : BitVec w₂} :
shiftLeftRec x y 0 = x <<< (y &&& twoPow w₂ 0) := by
simp [shiftLeftRec]
@[simp]
theorem shiftLeftRec_succ {x : BitVec w₁} {y : BitVec w₂} :
shiftLeftRec x y (n + 1) = (shiftLeftRec x y n) <<< (y &&& twoPow w₂ (n + 1)) := by
simp [shiftLeftRec]
/--
If `y &&& z = 0`, `x <<< (y ||| z) = x <<< y <<< z`.
This follows as `y &&& z = 0` implies `y ||| z = y + z`,
and thus `x <<< (y ||| z) = x <<< (y + z) = x <<< y <<< z`.
-/
theorem shiftLeft_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
(h : y &&& z = 0#w₂) :
x <<< (y ||| z) = x <<< y <<< z := by
rw [ add_eq_or_of_and_eq_zero _ _ h,
shiftLeft_eq', toNat_add_of_and_eq_zero h]
simp [shiftLeft_add]
/--
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
-/
theorem shiftLeftRec_eq {x : BitVec w₁} {y : BitVec w₂} {n : Nat} :
shiftLeftRec x y n = x <<< (y.truncate (n + 1)).zeroExtend w₂ := by
induction n generalizing x y
case zero =>
ext i
simp only [shiftLeftRec_zero, twoPow_zero, Nat.reduceAdd, truncate_one]
suffices (y &&& 1#w₂) = zeroExtend w₂ (ofBool (y.getLsb 0)) by simp [this]
ext i
by_cases h : (i : Nat) = 0
· simp [h, Bool.and_comm]
· simp [h]; omega
case succ n ih =>
simp only [shiftLeftRec_succ, and_twoPow]
rw [ih]
by_cases h : y.getLsb (n + 1)
· simp only [h, reduceIte]
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h,
shiftLeft_or_of_and_eq_zero]
simp
· simp only [h, false_eq_true, reduceIte, shiftLeft_zero']
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false (i := n + 1)]
simp [h]
/--
Show that `x <<< y` can be written in terms of `shiftLeftRec`.
This can be unfolded in terms of `shiftLeftRec_zero`, `shiftLeftRec_succ` for bitblasting.
-/
theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
x <<< y = shiftLeftRec x y (w₂ - 1) := by
rcases w₂ with rfl | w₂
· simp [of_length_zero]
· simp [shiftLeftRec_eq]
end BitVec

View File

@@ -110,8 +110,8 @@ theorem eq_of_getMsb_eq {x y : BitVec w}
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
@[simp] theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
@[simp] theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false := by simp [of_length_zero]
@[simp] theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false := by simp [of_length_zero]
theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false := by simp
theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false := by simp
@[simp] theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero]
theorem eq_of_toFin_eq : {x y : BitVec w}, x.toFin = y.toFin x = y
@@ -163,6 +163,13 @@ theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_of_le_right (by trivial : 0 < 2) le)
@[simp]
theorem getLsb_ofBool (b : Bool) (i : Nat) : (BitVec.ofBool b).getLsb i = ((i = 0) && b) := by
rcases b with rfl | rfl
· simp [ofBool]
· simp only [ofBool, ofNat_eq_ofNat, cond_true, getLsb_ofNat, Bool.and_true]
by_cases hi : i = 0 <;> simp [hi] <;> omega
/-! ### msb -/
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsb]
@@ -286,6 +293,9 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
/-! ### zeroExtend and truncate -/
theorem truncate_eq_zeroExtend {v : Nat} {x : BitVec w} :
truncate v x = zeroExtend v x := rfl
@[simp, bv_toNat] theorem toNat_zeroExtend' {m n : Nat} (p : m n) (x : BitVec m) :
(zeroExtend' p x).toNat = x.toNat := by
unfold zeroExtend'
@@ -319,7 +329,7 @@ theorem zeroExtend'_eq {x : BitVec w} (h : w ≤ v) : x.zeroExtend' h = x.zeroEx
apply eq_of_toNat_eq
simp [toNat_zeroExtend]
@[simp] theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : BitVec.ofNat m x.toNat = truncate m x := by
apply eq_of_toNat_eq
@@ -373,7 +383,7 @@ theorem nat_eq_toNat (x : BitVec w) (y : Nat)
all_goals (first | apply getLsb_ge | apply Eq.symm; apply getLsb_ge)
<;> omega
@[simp] theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
getLsb (truncate m x) i = (decide (i < m) && getLsb x i) :=
getLsb_zeroExtend m x i
@@ -392,6 +402,12 @@ 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]
@[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k := by
apply eq_of_getLsb_eq
simp
@@ -404,6 +420,28 @@ theorem msb_zeroExtend (x : BitVec w) : (x.zeroExtend v).msb = (decide (0 < v) &
theorem msb_zeroExtend' (x : BitVec w) (h : w v) : (x.zeroExtend' h).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
rw [zeroExtend'_eq, msb_zeroExtend]
/-- zero extending a bitvector to width 1 equals the boolean of the lsb. -/
theorem zeroExtend_one_eq_ofBool_getLsb_zero (x : BitVec w) :
x.zeroExtend 1 = BitVec.ofBool (x.getLsb 0) := by
ext i
simp [getLsb_zeroExtend, Fin.fin_one_eq_zero i]
/-- Zero extending `1#v` to `1#w` equals `1#w` when `v > 0`. -/
theorem zeroExtend_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
(BitVec.ofNat v 1).zeroExtend w = BitVec.ofNat w 1 := by
ext i, hilt
simp only [getLsb_zeroExtend, hilt, decide_True, getLsb_ofNat, Bool.true_and,
Bool.and_iff_right_iff_imp, decide_eq_true_eq]
intros hi₁
have hv := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi₁
omega
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
theorem truncate_one {x : BitVec w} :
x.truncate 1 = ofBool (x.getLsb 0) := by
ext i
simp [show i = 0 by omega]
/-! ## extractLsb -/
@[simp]
@@ -499,6 +537,11 @@ theorem and_assoc (x y z : BitVec w) :
ext i
simp [Bool.and_assoc]
theorem and_comm (x y : BitVec w) :
x &&& y = y &&& x := by
ext i
simp [Bool.and_comm]
/-! ### xor -/
@[simp] theorem toNat_xor (x y : BitVec v) :
@@ -576,7 +619,7 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
ext
simp_all [lt_of_getLsb]
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x &&& cast h y = cast h (x &&& y) := by
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x ^^^ cast h y = cast h (x ^^^ y) := by
ext
simp_all [lt_of_getLsb]
@@ -589,6 +632,15 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
BitVec.toFin (x <<< n) = Fin.ofNat' (x.toNat <<< n) (Nat.two_pow_pos w) := rfl
@[simp]
theorem shiftLeft_zero_eq (x : BitVec w) : x <<< 0 = x := by
apply eq_of_toNat_eq
simp
@[simp]
theorem zero_shiftLeft (n : Nat) : 0#w <<< n = 0#w := by
simp [bv_toNat]
@[simp] theorem getLsb_shiftLeft (x : BitVec m) (n) :
getLsb (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsb x (i - n)) := by
rw [ testBit_toNat, getLsb]
@@ -654,6 +706,22 @@ theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
(x <<< n) <<< m = x <<< (n + m) := by
rw [shiftLeft_add]
/-! ### shiftLeft reductions from BitVec to 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₃} :
x <<< y <<< z = x <<< (y.toNat + z.toNat) := by
simp [shiftLeft_add]
theorem getLsb_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} :
(x <<< y).getLsb i = (decide (i < w₁) && !decide (i < y.toNat) && x.getLsb (i - y.toNat)) := by
simp [shiftLeft_eq', getLsb_shiftLeft]
/-! ### ushiftRight -/
@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
@@ -1043,8 +1111,16 @@ theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n ((2^n - y.toNat) + x.toNat) := by rfl
@[simp, bv_toNat] theorem toNat_sub {n} (x y : BitVec n) :
(x - y).toNat = (((2^n - y.toNat) + x.toNat) % 2^n) := rfl
@[simp] theorem toNat_sub {n} (x y : BitVec n) :
(x - y).toNat = (((2^n - y.toNat) + x.toNat) % 2^n) := rfl
-- We prefer this lemma to `toNat_sub` for the `bv_toNat` simp set.
-- For reasons we don't yet understand, unfolding via `toNat_sub` sometimes
-- results in `omega` generating proof terms that are very slow in the kernel.
@[bv_toNat] theorem toNat_sub' {n} (x y : BitVec n) :
(x - y).toNat = ((x.toNat + (2^n - y.toNat)) % 2^n) := by
rw [toNat_sub, Nat.add_comm]
@[simp] theorem toFin_sub (x y : BitVec n) : (x - y).toFin = toFin x - toFin y := rfl
@[simp] theorem ofFin_sub (x : Fin (2^n)) (y : BitVec n) : .ofFin x - y = .ofFin (x - y.toFin) :=
@@ -1136,6 +1212,18 @@ instance : Std.Associative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_asso
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
right_id := BitVec.mul_one
@[simp]
theorem BitVec.mul_zero {x : BitVec w} : x * 0#w = 0#w := by
apply eq_of_toNat_eq
simp [toNat_mul]
theorem BitVec.mul_add {x y z : BitVec w} :
x * (y + z) = x * y + x * z := by
apply eq_of_toNat_eq
simp only [toNat_mul, toNat_add, Nat.add_mod_mod, Nat.mod_add_mod]
rw [Nat.mul_mod, Nat.mod_mod (y.toNat + z.toNat),
Nat.mul_mod, Nat.mul_add]
@[simp, bv_toNat] theorem toInt_mul (x y : BitVec w) :
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
simp [toInt_eq_toNat_bmod]
@@ -1380,7 +1468,7 @@ theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
@[simp]
theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j)) := by
rcases w with rfl | w
· simp; omega
· simp
· simp only [twoPow, getLsb_shiftLeft, getLsb_ofNat]
by_cases hj : j < i
· simp only [hj, decide_True, Bool.not_true, Bool.and_false, Bool.false_and, Bool.false_eq,
@@ -1395,12 +1483,18 @@ theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j))
simp at hi
simp_all
theorem and_twoPow_eq (x : BitVec w) (i : Nat) :
@[simp]
theorem and_twoPow (x : BitVec w) (i : Nat) :
x &&& (twoPow w i) = if x.getLsb i then twoPow w i else 0#w := by
ext j
simp only [getLsb_and, getLsb_twoPow]
by_cases hj : i = j <;> by_cases hx : x.getLsb i <;> simp_all
@[simp]
theorem twoPow_and (x : BitVec w) (i : Nat) :
(twoPow w i) &&& x = if x.getLsb i then twoPow w i else 0#w := by
rw [BitVec.and_comm, and_twoPow]
@[simp]
theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
x * (twoPow w i) = x <<< i := by
@@ -1414,4 +1508,45 @@ theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
apply Nat.pow_dvd_pow 2 (by omega)
simp [Nat.mul_mod, hpow]
theorem twoPow_zero {w : Nat} : twoPow w 0 = 1#w := by
apply eq_of_toNat_eq
simp
@[simp]
theorem getLsb_one {w i : Nat} : (1#w).getLsb i = (decide (0 < w) && decide (0 = i)) := by
rw [ twoPow_zero, getLsb_twoPow]
/- ### zeroExtend, truncate, and bitwise operations -/
/--
When the `(i+1)`th bit of `x` is false,
keeping the lower `(i + 1)` bits of `x` equals keeping the lower `i` bits.
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false
{x : BitVec w} {i : Nat} (hx : x.getLsb i = false) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) := by
ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp [hx]
· by_cases hik' : k < i + 1 <;> simp [hik'] <;> omega
/--
When the `(i+1)`th bit of `x` is true,
keeping the lower `(i + 1)` bits of `x` equalsk eeping the lower `i` bits
and then performing bitwise-or with `twoPow i = (1 << i)`,
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true
{x : BitVec w} {i : Nat} (hx : x.getLsb i = true) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) ||| (twoPow w i) := by
ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp [hx]
· by_cases hik' : k < i + 1 <;> simp [hik, hik'] <;> omega
end BitVec

View File

@@ -52,8 +52,8 @@ theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} [Decidable (b = true)] : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} [Decidable (b = false)] : decide (b = false) = !b := by cases b <;> simp
@[simp] theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !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
/-! ### and -/
@@ -163,7 +163,7 @@ Consider the term: `¬((b && c) = true)`:
-/
@[simp] theorem and_eq_false_imp : (x y : Bool), (x && y) = false (x = true y = false) := by decide
@[simp] theorem or_eq_true_iff : (x y : Bool), (x || y) = true x = true y = true := by decide
theorem or_eq_true_iff : (x y : Bool), (x || y) = true x = true y = true := by simp
@[simp] theorem or_eq_false_iff : (x y : Bool), (x || y) = false x = false y = false := by decide
@@ -187,11 +187,9 @@ in false_eq and true_eq.
@[simp] theorem true_beq : b, (true == b) = b := by decide
@[simp] theorem false_beq : b, (false == b) = !b := by decide
@[simp] theorem beq_true : b, (b == true) = b := by decide
instance : Std.LawfulIdentity (· == ·) true where
left_id := true_beq
right_id := beq_true
@[simp] theorem beq_false : b, (b == false) = !b := by decide
@[simp] theorem true_bne : (b : Bool), (true != b) = !b := by decide
@[simp] theorem false_bne : (b : Bool), (false != b) = b := by decide
@@ -353,7 +351,7 @@ theorem and_or_inj_left_iff :
/-! ## toNat -/
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
def toNat (b:Bool) : Nat := cond b 1 0
def toNat (b : Bool) : Nat := cond b 1 0
@[simp] theorem toNat_false : false.toNat = 0 := rfl
@@ -496,6 +494,16 @@ protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := co
@[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
theorem cond_pos {b : Bool} {a a' : α} (h : b = true) : (bif b then a else a') = a := by
rw [h, cond_true]
theorem cond_neg {b : Bool} {a a' : α} (h : b = false) : (bif b then a else a') = a' := by
rw [h, cond_false]
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 -/
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true

View File

@@ -37,6 +37,10 @@ def push : ByteArray → UInt8 → ByteArray
def size : (@& ByteArray) Nat
| bs => bs.size
@[extern "lean_sarray_size", simp]
def usize (a : @& ByteArray) : USize :=
a.size.toUSize
@[extern "lean_byte_array_uget"]
def uget : (a : @& ByteArray) (i : USize) i.toNat < a.size UInt8
| bs, i, h => bs[i]
@@ -52,13 +56,9 @@ def get : (a : @& ByteArray) → (@& Fin a.size) → UInt8
instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
instance : GetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
getElem xs i h := xs.uget i h
instance : LawfulGetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
@[extern "lean_byte_array_set"]
def set! : ByteArray (@& Nat) UInt8 ByteArray
| bs, i, b => bs.set! i b
@@ -96,20 +96,24 @@ protected def append (a : ByteArray) (b : ByteArray) : ByteArray :=
instance : Append ByteArray := ByteArray.append
partial def toList (bs : ByteArray) : List UInt8 :=
def toList (bs : ByteArray) : List UInt8 :=
let rec loop (i : Nat) (r : List UInt8) :=
if i < bs.size then
loop (i+1) (bs.get! i :: r)
else
r.reverse
termination_by bs.size - i
decreasing_by decreasing_trivial_pre_omega
loop 0 []
@[inline] partial def findIdx? (a : ByteArray) (p : UInt8 Bool) (start := 0) : Option Nat :=
@[inline] def findIdx? (a : ByteArray) (p : UInt8 Bool) (start := 0) : Option Nat :=
let rec @[specialize] loop (i : Nat) :=
if i < a.size then
if p (a.get! i) then some i else loop (i+1)
else
none
termination_by a.size - i
decreasing_by decreasing_trivial_pre_omega
loop start
/--
@@ -119,7 +123,7 @@ partial def toList (bs : ByteArray) : List UInt8 :=
TODO: avoid code duplication in the future after we improve the compiler.
-/
@[inline] unsafe def forInUnsafe {β : Type v} {m : Type v Type w} [Monad m] (as : ByteArray) (b : β) (f : UInt8 β m (ForInStep β)) : m β :=
let sz := USize.ofNat as.size
let sz := as.usize
let rec @[specialize] loop (i : USize) (b : β) : m β := do
if i < sz then
let a := as.uget i lcProof

View File

@@ -31,11 +31,9 @@ theorem utf8Size_eq (c : Char) : c.utf8Size = 1 c.utf8Size = 2 c.utf8Siz
rw [Char.ofNat, dif_pos]
rfl
@[ext] theorem Char.ext : {a b : Char} a.val = b.val a = b
@[ext] protected theorem ext : {a b : Char} a.val = b.val a = b
| _,_, _,_, rfl => rfl
theorem Char.ext_iff {x y : Char} : x = y x.val = y.val := congrArg _, Char.ext
end Char
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size

View File

@@ -0,0 +1,15 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.Nat.Bitwise
import Init.Data.Fin.Basic
namespace Fin
@[simp] theorem and_val (a b : Fin n) : (a &&& b).val = a.val &&& b.val :=
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt Nat.and_le_left a.isLt)
end Fin

View File

@@ -37,9 +37,7 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (a, h : Fin n) = a := rfl
@[ext] theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem ext_iff {a b : Fin n} : a = b a.1 = b.1 := val_inj.symm
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -47,12 +45,12 @@ theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩
fun h i hi => h i, hi, fun h i, hi => h i hi
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
(a, ha : Fin n) = b, hb a = b := ext_iff
(a, ha : Fin n) = b, hb a = b := Fin.ext_iff
theorem val_mk {m n : Nat} (h : m < n) : (m, h : Fin n).val = m := rfl
theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
a = k, hk (a : Nat) = k := ext_iff
a = k, hk (a : Nat) = k := Fin.ext_iff
theorem mk_val (i : Fin n) : (i, i.isLt : Fin n) = i := Fin.eta ..
@@ -145,7 +143,7 @@ theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) : ∃ j :
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := ext <| by
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@[simp] theorem rev_le_rev {i j : Fin n} : rev i rev j j i := by
@@ -171,12 +169,12 @@ theorem le_last (i : Fin (n + 1)) : i ≤ last n := Nat.le_of_lt_succ i.is_lt
theorem last_pos : (0 : Fin (n + 2)) < last (n + 1) := Nat.succ_pos _
theorem eq_last_of_not_lt {i : Fin (n + 1)} (h : ¬(i : Nat) < n) : i = last n :=
ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
Fin.ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
theorem val_lt_last {i : Fin (n + 1)} : i last n (i : Nat) < n :=
Decidable.not_imp_comm.1 eq_last_of_not_lt
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := ext <| by simp
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := Fin.ext <| by simp
@[simp] theorem rev_zero (n : Nat) : rev 0 = last n := by
rw [ rev_rev (last _), rev_last]
@@ -244,11 +242,11 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt one_pos
@[simp] theorem succ_lt_succ_iff {a b : Fin n} : a.succ < b.succ a < b := Nat.succ_lt_succ_iff
@[simp] theorem succ_inj {a b : Fin n} : a.succ = b.succ a = b := by
refine fun h => ext ?_, congrArg _
refine fun h => Fin.ext ?_, congrArg _
apply Nat.le_antisymm <;> exact succ_le_succ_iff.1 (h Nat.le_refl _)
theorem succ_ne_zero {n} : k : Fin n, Fin.succ k 0
| k, _, heq => Nat.succ_ne_zero k <| ext_iff.1 heq
| k, _, heq => Nat.succ_ne_zero k <| congrArg Fin.val heq
@[simp] theorem succ_zero_eq_one : Fin.succ (0 : Fin (n + 1)) = 1 := rfl
@@ -267,7 +265,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
rw [ succ_zero_eq_one, succ_lt_succ_iff]; exact succ_pos a
@[simp] theorem add_one_lt_iff {n : Nat} {k : Fin (n + 2)} : k + 1 < k k = last _ := by
simp only [lt_def, val_add, val_last, ext_iff]
simp only [lt_def, val_add, val_last, Fin.ext_iff]
let k, hk := k
match Nat.eq_or_lt_of_le (Nat.le_of_lt_succ hk) with
| .inl h => cases h; simp [Nat.succ_pos]
@@ -285,7 +283,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
split <;> simp [*, (Nat.succ_ne_zero _).symm, Nat.ne_of_gt (Nat.lt_succ_self _)]
@[simp] theorem last_le_iff {n : Nat} {k : Fin (n + 1)} : last n k k = last n := by
rw [ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
rw [Fin.ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
@[simp] theorem lt_add_one_iff {n : Nat} {k : Fin (n + 1)} : k < k + 1 k < last n := by
rw [ Decidable.not_iff_not]; simp
@@ -306,10 +304,10 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n m) :
castLE h i, hn = i, Nat.lt_of_lt_of_le hn h := rfl
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [ext_iff]
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [Fin.ext_iff]
@[simp] theorem castLE_succ {m n : Nat} (h : m + 1 n + 1) (i : Fin m) :
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [ext_iff]
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [Fin.ext_iff]
@[simp] theorem castLE_castLE {k m n} (km : k m) (mn : m n) (i : Fin k) :
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
@@ -322,7 +320,7 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h i, hn = i, h hn := rfl
@@ -348,7 +346,7 @@ theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m :=
/-- For rewriting in the reverse direction, see `Fin.cast_castAdd_left`. -/
theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := ext rfl
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := Fin.ext rfl
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
@@ -397,7 +395,7 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
Fin.castSucc a < Fin.castSucc b a < b := .rfl
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [ext_iff]
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [Fin.ext_iff]
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
@@ -409,7 +407,7 @@ theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
simpa [lt_def] using h
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 a = 0 := by simp [ext_iff]
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 a = 0 := by simp [Fin.ext_iff]
theorem castSucc_ne_zero_iff (a : Fin (n + 1)) : castSucc a 0 a 0 :=
not_congr <| castSucc_eq_zero_iff a
@@ -421,7 +419,7 @@ theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
cases n
· exact a.elim0
· simp [ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
@@ -454,7 +452,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
cast h (addNat i m') = addNat i m :=
ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@@ -474,7 +472,7 @@ theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
cast h (natAdd m' i) = natAdd m i :=
ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
Fin.ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
@@ -484,27 +482,27 @@ theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
ext <| (Nat.add_assoc ..).symm
Fin.ext <| (Nat.add_assoc ..).symm
@[simp]
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
ext <| Nat.zero_add _
Fin.ext <| Nat.zero_add _
@[simp]
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := Fin.ext <| Nat.add_comm ..
@[simp]
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := Fin.ext <| Nat.add_comm ..
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSucc (natAdd n i) :=
rfl
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := ext <| by
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
@@ -534,7 +532,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i ≠ 0) (j : Fin
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
Fin.pred i + 1, Nat.add_lt_add_right h 1 (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
i, h := by
simp only [ext_iff, coe_pred, Nat.add_sub_cancel]
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
Fin.pred i + 1, h₁ h₂ = i, Nat.lt_of_succ_lt_succ h₁ := pred_mk_succ i _
@@ -554,14 +552,14 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
{a b : Fin (n + 1)} {ha : a 0} {hb : b 0}, a.pred ha = b.pred hb a = b
| 0, _, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
| i + 1, _, 0, _, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
| i + 1, hi, j + 1, hj, ha, hb => by simp [ext_iff, Nat.succ.injEq]
| i + 1, hi, j + 1, hj, ha, hb => by simp [Fin.ext_iff, Nat.succ.injEq]
@[simp] theorem pred_one {n : Nat} :
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
rw [ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
exact Nat.add_lt_add_right h 1
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@@ -573,10 +571,10 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (castSucc i.succ) (Fin.ne_of_gt (castSucc_pos i.succ_pos)) = castSucc i := rfl
@[simp] theorem addNat_subNat {i : Fin (n + m)} (h : m i) : addNat (subNat m i h) m = i :=
ext <| Nat.sub_add_cancel h
Fin.ext <| Nat.sub_add_cancel h
@[simp] theorem subNat_addNat (i : Fin n) (m : Nat) (h : m addNat i m := le_coe_addNat m i) :
subNat m (addNat i m) h = i := ext <| Nat.add_sub_cancel i m
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n i) :
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [ cast_addNat]; rfl
@@ -786,6 +784,9 @@ theorem coe_sub_iff_le {a b : Fin n} : (↑(a - b) : Nat) = a - b ↔ b ≤ a :=
rw [Nat.mod_eq_of_lt]
all_goals omega
theorem sub_val_of_le {a b : Fin n} : b a (a - b).val = a.val - b.val :=
coe_sub_iff_le.2
theorem coe_sub_iff_lt {a b : Fin n} : ((a - b) : Nat) = n + a - b a < b := by
rw [sub_def, lt_def]
dsimp only
@@ -807,10 +808,10 @@ theorem coe_mul {n : Nat} : ∀ a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
match n with
| 0 => exact Subsingleton.elim (α := Fin 1) ..
| n+1 => simp [ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
| n+1 => simp [Fin.ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
Fin.ext <| by rw [mul_def, mul_def, Nat.mul_comm]
instance : Std.Commutative (α := Fin n) (· * ·) := Fin.mul_comm
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
@@ -826,9 +827,9 @@ instance : Std.LawfulIdentity (α := Fin (n + 1)) (· * ·) 1 where
left_id := Fin.one_mul
right_id := Fin.mul_one
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [ext_iff, mul_def]
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [Fin.ext_iff, mul_def]
protected theorem zero_mul (k : Fin (n + 1)) : (0 : Fin (n + 1)) * k = 0 := by
simp [ext_iff, mul_def]
simp [Fin.ext_iff, mul_def]
end Fin

View File

@@ -101,13 +101,13 @@ Returns an undefined value if `x` is not finite.
instance : ToString Float where
toString := Float.toString
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
instance : Repr Float where
reprPrec n _ := Float.toString n
reprPrec n prec := if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
instance : ReprAtom Float :=
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
@[extern "sin"] opaque Float.sin : Float Float
@[extern "cos"] opaque Float.cos : Float Float
@[extern "tan"] opaque Float.tan : Float Float

View File

@@ -37,6 +37,10 @@ def push : FloatArray → Float → FloatArray
def size : (@& FloatArray) Nat
| ds => ds.size
@[extern "lean_sarray_size", simp]
def usize (a : @& FloatArray) : USize :=
a.size.toUSize
@[extern "lean_float_array_uget"]
def uget : (a : @& FloatArray) (i : USize) i.toNat < a.size Float
| ds, i, h => ds[i]
@@ -58,13 +62,9 @@ def get? (ds : FloatArray) (i : Nat) : Option Float :=
instance : GetElem FloatArray Nat Float fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem FloatArray Nat Float fun xs i => i < xs.size where
instance : GetElem FloatArray USize Float fun xs i => i.val < xs.size where
getElem xs i h := xs.uget i h
instance : LawfulGetElem FloatArray USize Float fun xs i => i.val < xs.size where
@[extern "lean_float_array_uset"]
def uset : (a : FloatArray) (i : USize) Float i.toNat < a.size FloatArray
| ds, i, v, h => ds.uset i v h
@@ -94,7 +94,7 @@ partial def toList (ds : FloatArray) : List Float :=
-/
-- TODO: avoid code duplication in the future after we improve the compiler.
@[inline] unsafe def forInUnsafe {β : Type v} {m : Type v Type w} [Monad m] (as : FloatArray) (b : β) (f : Float β m (ForInStep β)) : m β :=
let sz := USize.ofNat as.size
let sz := as.usize
let rec @[specialize] loop (i : USize) (b : β) : m β := do
if i < sz then
let a := as.uget i lcProof

View File

@@ -62,3 +62,16 @@ instance (P : Prop) : Hashable P where
/-- An opaque (low-level) hash operation used to implement hashing for pointers. -/
@[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
mixHash u 11
/-- `LawfulHashable α` says that the `BEq α` and `Hashable α` instances on `α` are compatible, i.e.,
that `a == b` implies `hash a = hash b`. This is automatic if the `BEq` instance is lawful.
-/
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
/-- If `a == b`, then `hash a = hash b`. -/
hash_eq (a b : α) : a == b hash a = hash b
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b hash a = hash b :=
LawfulHashable.hash_eq a b
instance (priority := low) [BEq α] [Hashable α] [LawfulBEq α] : LawfulHashable α where
hash_eq _ _ h := eq_of_beq h rfl

View File

@@ -4,10 +4,20 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.List.Attach
import Init.Data.List.Basic
import Init.Data.List.BasicAux
import Init.Data.List.Control
import Init.Data.List.Lemmas
import Init.Data.List.Count
import Init.Data.List.Erase
import Init.Data.List.Find
import Init.Data.List.Impl
import Init.Data.List.TakeDrop
import Init.Data.List.Lemmas
import Init.Data.List.MinMax
import Init.Data.List.Monadic
import Init.Data.List.Nat
import Init.Data.List.Notation
import Init.Data.List.Pairwise
import Init.Data.List.Sublist
import Init.Data.List.TakeDrop
import Init.Data.List.Zip

View File

@@ -0,0 +1,199 @@
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.List.Count
import Init.Data.Subtype
namespace List
/-- `O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
but is defined only when all members of `l` satisfy `P`, using the proof
to apply `f`. -/
@[simp] def pmap {P : α Prop} (f : a, P a β) : l : List α, (H : a l, P a) List β
| [], _ => []
| a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
`List {x // P x}` is the same as the input `List α`.
(Someday, the compiler might do this optimization automatically, but until then...)
-/
@[inline] private unsafe def attachWithImpl
(l : List α) (P : α Prop) (_ : x l, P x) : List {x // P x} := unsafeCast l
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `l` to produce a new list
with the same elements but in the type `{x // P x}`. -/
@[implemented_by attachWithImpl] def attachWith
(l : List α) (P : α Prop) (H : x l, P x) : List {x // P x} := pmap Subtype.mk l H
/-- `O(1)`. "Attach" the proof that the elements of `l` are in `l` to produce a new list
with the same elements but in the type `{x // x ∈ l}`. -/
@[inline] def attach (l : List α) : List {x // x l} := attachWith l _ fun _ => id
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
@[inline] private def pmapImpl {P : α Prop} (f : a, P a β) (l : List α) (H : a l, P a) :
List β := (l.attachWith _ H).map fun x, h' => f x h'
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
funext α β p f L h'
let rec go : L' (hL' : x, x L' p x),
pmap f L' hL' = map (fun x, hx => f x hx) (pmap Subtype.mk L' hL')
| nil, hL' => rfl
| cons _ L', hL' => congrArg _ <| go L' fun _ hx => hL' (.tail _ hx)
exact go L h'
@[simp] theorem attach_nil : ([] : List α).attach = [] := rfl
@[simp]
theorem pmap_eq_map (p : α Prop) (f : α β) (l : List α) (H) :
@pmap _ _ p (fun a _ => f a) l H = map f l := by
induction l
· rfl
· simp only [*, pmap, map]
theorem pmap_congr {p q : α Prop} {f : a, p a β} {g : a, q a β} (l : List α) {H₁ H₂}
(h : a l, (h₁ h₂), f a h₁ = g a h₂) : pmap f l H₁ = pmap g l H₂ := by
induction l with
| nil => rfl
| cons x l ih => rw [pmap, pmap, h _ (mem_cons_self _ _), ih fun a ha => h a (mem_cons_of_mem _ ha)]
theorem map_pmap {p : α Prop} (g : β γ) (f : a, p a β) (l H) :
map g (pmap f l H) = pmap (fun a h => g (f a h)) l H := by
induction l
· rfl
· simp only [*, pmap, map]
theorem pmap_map {p : β Prop} (g : b, p b γ) (f : α β) (l H) :
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun a h => H _ (mem_map_of_mem _ h) := by
induction l
· rfl
· simp only [*, pmap, map]
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
theorem attach_map_coe (l : List α) (f : α β) :
(l.attach.map fun (i : {i // i l}) => f i) = l.map f := by
rw [attach, attachWith, map_pmap]; exact pmap_eq_map _ _ _ _
theorem attach_map_val (l : List α) (f : α β) : (l.attach.map fun i => f i.val) = l.map f :=
attach_map_coe _ _
@[simp]
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
(attach_map_coe _ _).trans l.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]
@[simp]
theorem count_attach [DecidableEq α] (l : List α) (a : {x // x l}) : l.attach.count a = l.count a :=
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attach _ _
@[simp]
theorem mem_attach (l : List α) : x, x l.attach
| a, h => by
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
rcases this with _, _, m, rfl
exact m
@[simp]
theorem mem_pmap {p : α Prop} {f : a, p a β} {l H b} :
b pmap f l H (a : _) (h : a l), f a (H a h) = b := by
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
@[simp]
theorem length_pmap {p : α Prop} {f : a, p a β} {l H} : length (pmap f l H) = length l := by
induction l
· rfl
· simp only [*, pmap, length]
@[simp]
theorem length_attach (L : List α) : L.attach.length = L.length :=
length_pmap
@[simp]
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]
@[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
| nil => simp
| cons hd tl hl =>
rcases n with n
· simp only [Option.pmap]
split <;> simp_all
· simp only [hl, pmap, Option.pmap, getElem?_cons_succ]
split <;> rename_i h₁ _ <;> split <;> rename_i h₂ _
· simp_all
· simp at h₂
simp_all
· simp_all
· simp_all
theorem get?_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) (n : Nat) :
get? (pmap f l h) n = Option.pmap f (get? l n) fun x H => h x (get?_mem H) := by
simp only [get?_eq_getElem?]
simp [getElem?_pmap, h]
theorem getElem_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) {n : Nat}
(hn : n < (pmap f l h).length) :
(pmap f l h)[n] =
f (l[n]'(@length_pmap _ _ p f l h hn))
(h _ (getElem_mem l n (@length_pmap _ _ p f l h hn))) := by
induction l generalizing n with
| nil =>
simp only [length, pmap] at hn
exact absurd hn (Nat.not_lt_of_le n.zero_le)
| cons hd tl hl =>
cases n
· simp
· simp [hl]
theorem get_pmap {p : α Prop} (f : a, p a β) {l : List α} (h : a l, p a) {n : Nat}
(hn : n < (pmap f l h).length) :
get (pmap f l h) n, hn =
f (get l n, @length_pmap _ _ p f l h hn)
(h _ (get_mem l n (@length_pmap _ _ p f l h hn))) := by
simp only [get_eq_getElem]
simp [getElem_pmap]
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)) ++
l₂.pmap f fun a ha => h a (mem_append_right l₁ ha) := by
induction l₁ with
| nil => rfl
| cons _ _ ih =>
dsimp only [pmap, cons_append]
rw [ih]
theorem pmap_append' {p : α Prop} (f : a : α, p a β) (l₁ l₂ : List α)
(h₁ : a l₁, p a) (h₂ : a l₂, p a) :
((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₂ _

View File

@@ -22,29 +22,37 @@ along with `@[csimp]` lemmas,
In `Init.Data.List.Lemmas` we develop the full API for these functions.
Recall that `length`, `get`, `set`, `fold`, and `concat` have already been defined in `Init.Prelude`.
Recall that `length`, `get`, `set`, `foldl`, and `concat` have already been defined in `Init.Prelude`.
The operations are organized as follow:
* Equality: `beq`, `isEqv`.
* Lexicographic ordering: `lt`, `le`, and instances.
* Head and tail operators: `head`, `head?`, `headD?`, `tail`, `tail?`, `tailD`.
* Basic operations:
`map`, `filter`, `filterMap`, `foldr`, `append`, `join`, `pure`, `bind`, `replicate`, and `reverse`.
`map`, `filter`, `filterMap`, `foldr`, `append`, `join`, `pure`, `bind`, `replicate`, and
`reverse`.
* Additional functions defined in terms of these: `leftpad`, `rightPad`, and `reduceOption`.
* List membership: `isEmpty`, `elem`, `contains`, `mem` (and the `∈` notation),
and decidability for predicates quantifying over membership in a `List`.
* Sublists: `take`, `drop`, `takeWhile`, `dropWhile`, `partition`, `dropLast`,
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`,
`rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseP`, `eraseIdx`.
* Finding elements: `find?`, `findSome?`, `findIdx`, `indexOf`, `findIdx?`, `indexOf?`,
`countP`, `count`, and `lookup`.
* Logic: `any`, `all`, `or`, and `and`.
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
* Minima and maxima: `minimum?` and `maximum?`.
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`, `removeAll`
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`,
`removeAll`
(currently these functions are mostly only used in meta code,
and do not have API suitable for verification).
Further operations are defined in `Init.Data.List.BasicAux` (because they use `Array` in their implementations), namely:
Further operations are defined in `Init.Data.List.BasicAux`
(because they use `Array` in their implementations), namely:
* Variant getters: `get!`, `get?`, `getD`, `getLast`, `getLast!`, `getLast?`, and `getLastD`.
* Head and tail: `head`, `head!`, `head?`, `headD`, `tail!`, `tail?`, and `tailD`.
* Head and tail: `head!`, `tail!`.
* Other operations on sublists: `partitionMap`, `rotateLeft`, and `rotateRight`.
-/
@@ -88,7 +96,7 @@ namespace List
/-! ### concat -/
@[simp] theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
@[simp high] 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]
@@ -315,6 +323,16 @@ def headD : (as : List α) → (fallback : α) → α
@[simp 1100] theorem headD_nil : @headD α [] d = d := rfl
@[simp 1100] theorem headD_cons : @headD α (a::l) d = a := rfl
/-! ### tail -/
/-- Get the tail of a nonempty list, or return `[]` for `[]`. -/
def tail : List α List α
| [] => []
| _::as => as
@[simp] theorem tail_nil : @tail α [] = [] := rfl
@[simp] theorem tail_cons : @tail α (a::as) = as := rfl
/-! ### tail? -/
/--
@@ -577,6 +595,28 @@ theorem replicate_succ (a : α) (n) : replicate (n+1) a = a :: replicate n a :=
| zero => simp
| succ n ih => simp only [ih, replicate_succ, length_cons, Nat.succ_eq_add_one]
/-! ## Additional functions -/
/-! ### leftpad and rightpad -/
/--
Pads `l : List α` on the left with repeated occurrences of `a : α` until it is of length `n`.
If `l` is initially larger than `n`, just return `l`.
-/
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
/--
Pads `l : List α` on the right with repeated occurrences of `a : α` until it is of length `n`.
If `l` is initially larger than `n`, just return `l`.
-/
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
/-! ### reduceOption -/
/-- Drop `none`s from a list, and replace each remaining `some a` with `a`. -/
@[inline] def reduceOption {α} : List (Option α) List α :=
List.filterMap id
/-! ## List membership
* `L.contains a : Bool` determines, using a `[BEq α]` instance, whether `L` contains an element `· == a`.
@@ -719,7 +759,7 @@ def take : Nat → List α → List α
@[simp] theorem take_nil : ([] : List α).take i = [] := by cases i <;> rfl
@[simp] theorem take_zero (l : List α) : l.take 0 = [] := rfl
@[simp] theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
@[simp] theorem take_succ_cons : (a::as).take (i+1) = a :: as.take i := rfl
/-! ### drop -/
@@ -817,6 +857,8 @@ def dropLast {α} : List α → List α
@[simp] theorem dropLast_cons₂ :
(x::y::zs).dropLast = x :: (y::zs).dropLast := rfl
-- Later this can be proved by `simp` via `[List.length_dropLast, List.length_cons, Nat.add_sub_cancel]`,
-- but we need this while bootstrapping `Array`.
@[simp] theorem length_dropLast_cons (a : α) (as : List α) : (a :: as).dropLast.length = as.length := by
match as with
| [] => rfl
@@ -824,7 +866,49 @@ def dropLast {α} : List α → List α
have ih := length_dropLast_cons b bs
simp [dropLast, ih]
/-! ### isPrefixOf -/
/-! ### Subset -/
/--
`l₁ ⊆ l₂` means that every element of `l₁` is also an element of `l₂`, ignoring multiplicity.
-/
protected def Subset (l₁ l₂ : List α) := a : α, a l₁ a l₂
instance : HasSubset (List α) := List.Subset
instance [DecidableEq α] : DecidableRel (Subset : List α List α Prop) :=
fun _ _ => decidableBAll _ _
/-! ### Sublist and isSublist -/
/-- `l₁ <+ l₂`, or `Sublist l₁ l₂`, says that `l₁` is a (non-contiguous) subsequence of `l₂`. -/
inductive Sublist {α} : List α List α Prop
/-- the base case: `[]` is a sublist of `[]` -/
| slnil : Sublist [] []
/-- If `l₁` is a subsequence of `l₂`, then it is also a subsequence of `a :: l₂`. -/
| cons a : Sublist l₁ l₂ Sublist l₁ (a :: l₂)
/-- If `l₁` is a subsequence of `l₂`, then `a :: l₁` is a subsequence of `a :: l₂`. -/
| cons₂ a : Sublist l₁ l₂ Sublist (a :: l₁) (a :: l₂)
@[inherit_doc] scoped infixl:50 " <+ " => Sublist
/-- True if the first list is a potentially non-contiguous sub-sequence of the second list. -/
def isSublist [BEq α] : List α List α Bool
| [], _ => true
| _, [] => false
| l₁@(hd₁::tl₁), hd₂::tl₂ =>
if hd₁ == hd₂
then tl₁.isSublist tl₂
else l₁.isSublist tl₂
/-! ### IsPrefix / isPrefixOf / isPrefixOf? -/
/--
`IsPrefix l₁ l₂`, or `l₁ <+: l₂`, means that `l₁` is a prefix of `l₂`,
that is, `l₂` has the form `l₁ ++ t` for some `t`.
-/
def IsPrefix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => l₁ ++ t = l₂
@[inherit_doc] infixl:50 " <+: " => IsPrefix
/-- `isPrefixOf l₁ l₂` returns `true` Iff `l₁` is a prefix of `l₂`.
That is, there exists a `t` such that `l₂ == l₁ ++ t`. -/
@@ -839,8 +923,6 @@ def isPrefixOf [BEq α] : List α → List α → Bool
theorem isPrefixOf_cons₂ [BEq α] {a : α} :
isPrefixOf (a::as) (b::bs) = (a == b && isPrefixOf as bs) := rfl
/-! ### isPrefixOf? -/
/-- `isPrefixOf? l₁ l₂` returns `some t` when `l₂ == l₁ ++ t`. -/
def isPrefixOf? [BEq α] : List α List α Option (List α)
| [], l₂ => some l₂
@@ -848,7 +930,7 @@ def isPrefixOf? [BEq α] : List α → List α → Option (List α)
| (x₁ :: l₁), (x₂ :: l₂) =>
if x₁ == x₂ then isPrefixOf? l₁ l₂ else none
/-! ### isSuffixOf -/
/-! ### IsSuffix / isSuffixOf / isSuffixOf? -/
/-- `isSuffixOf l₁ l₂` returns `true` Iff `l₁` is a suffix of `l₂`.
That is, there exists a `t` such that `l₂ == t ++ l₁`. -/
@@ -858,12 +940,28 @@ def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
@[simp] theorem isSuffixOf_nil_left [BEq α] : isSuffixOf ([] : List α) l = true := by
simp [isSuffixOf]
/-! ### isSuffixOf? -/
/-- `isSuffixOf? l₁ l₂` returns `some t` when `l₂ == t ++ l₁`.-/
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
/--
`IsSuffix l₁ l₂`, or `l₁ <:+ l₂`, means that `l₁` is a suffix of `l₂`,
that is, `l₂` has the form `t ++ l₁` for some `t`.
-/
def IsSuffix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => t ++ l₁ = l₂
@[inherit_doc] infixl:50 " <:+ " => IsSuffix
/-! ### IsInfix -/
/--
`IsInfix l₁ l₂`, or `l₁ <:+: l₂`, means that `l₁` is a contiguous
substring of `l₂`, that is, `l₂` has the form `s ++ l₁ ++ t` for some `s, t`.
-/
def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists fun t => s ++ l₁ ++ t = l₂
@[inherit_doc] infixl:50 " <:+: " => IsInfix
/-! ### rotateLeft -/
/--
@@ -906,6 +1004,55 @@ def rotateRight (xs : List α) (n : Nat := 1) : List α :=
@[simp] theorem rotateRight_nil : ([] : List α).rotateRight n = [] := rfl
/-! ## Pairwise, Nodup -/
section Pairwise
variable (R : α α Prop)
/--
`Pairwise R l` means that all the elements with earlier indexes are
`R`-related to all the elements with later indexes.
```
Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3
```
For example if `R = (·≠·)` then it asserts `l` has no duplicates,
and if `R = (·<·)` then it asserts that `l` is (strictly) sorted.
-/
inductive Pairwise : List α Prop
/-- All elements of the empty list are vacuously pairwise related. -/
| nil : Pairwise []
/-- `a :: l` is `Pairwise R` if `a` `R`-relates to every element of `l`,
and `l` is `Pairwise R`. -/
| cons : {a : α} {l : List α}, ( a', a' l R a a') Pairwise l Pairwise (a :: l)
attribute [simp] Pairwise.nil
variable {R}
@[simp] theorem pairwise_cons : Pairwise R (a::l) ( a', a' l R a a') Pairwise R l :=
fun | .cons h₁ h₂ => h₁, h₂, fun h₁, h₂ => h₂.cons h₁
instance instDecidablePairwise [DecidableRel R] :
(l : List α) Decidable (Pairwise R l)
| [] => isTrue .nil
| hd :: tl =>
match instDecidablePairwise tl with
| isTrue ht =>
match decidableBAll (R hd) tl with
| isFalse hf => isFalse fun hf' => hf (pairwise_cons.1 hf').1
| isTrue ht' => isTrue <| pairwise_cons.mpr (And.intro ht' ht)
| isFalse hf => isFalse fun | .cons _ ih => hf ih
end Pairwise
/-- `Nodup l` means that `l` has no duplicates, that is, any element appears at most
once in the List. It is defined as `Pairwise (≠)`. -/
def Nodup : List α Prop := Pairwise (· ·)
instance nodupDecidable [DecidableEq α] : l : List α, Decidable (Nodup l) :=
instDecidablePairwise
/-! ## Manipulating elements -/
/-! ### replace -/
@@ -951,6 +1098,11 @@ theorem erase_cons [BEq α] (a b : α) (l : List α) :
(b :: l).erase a = if b == a then l else b :: l.erase a := by
simp only [List.erase]; split <;> simp_all
/-- `eraseP p l` removes the first element of `l` satisfying the predicate `p`. -/
def eraseP (p : α Bool) : List α List α
| [] => []
| a :: l => bif p a then l else a :: eraseP p l
/-! ### eraseIdx -/
/--
@@ -968,6 +1120,8 @@ def eraseIdx : List α → Nat → List α
@[simp] theorem eraseIdx_cons_zero : (a::as).eraseIdx 0 = as := rfl
@[simp] theorem eraseIdx_cons_succ : (a::as).eraseIdx (i+1) = a :: as.eraseIdx i := rfl
/-! Finding elements -/
/-! ### find? -/
/--
@@ -1005,6 +1159,50 @@ theorem findSome?_cons {f : α → Option β} :
(a::as).findSome? f = match f a with | some b => some b | none => as.findSome? f :=
rfl
/-! ### findIdx -/
/-- Returns the index of the first element satisfying `p`, or the length of the list otherwise. -/
@[inline] def findIdx (p : α Bool) (l : List α) : Nat := go l 0 where
/-- Auxiliary for `findIdx`: `findIdx.go p l n = findIdx p l + n` -/
@[specialize] go : List α Nat Nat
| [], n => n
| a :: l, n => bif p a then n else go l (n + 1)
@[simp] theorem findIdx_nil {α : Type _} (p : α Bool) : [].findIdx p = 0 := rfl
/-! ### indexOf -/
/-- Returns the index of the first element equal to `a`, or the length of the list otherwise. -/
def indexOf [BEq α] (a : α) : List α Nat := findIdx (· == a)
@[simp] theorem indexOf_nil [BEq α] : ([] : List α).indexOf x = 0 := rfl
/-! ### findIdx? -/
/-- Return the index of the first occurrence of an element satisfying `p`. -/
def findIdx? (p : α Bool) : List α (start : Nat := 0) Option Nat
| [], _ => none
| a :: l, i => if p a then some i else findIdx? p l (i + 1)
/-! ### indexOf? -/
/-- Return the index of the first occurrence of `a` in the list. -/
@[inline] def indexOf? [BEq α] (a : α) : List α Option Nat := findIdx? (· == a)
/-! ### countP -/
/-- `countP p l` is the number of elements of `l` that satisfy `p`. -/
@[inline] def countP (p : α Bool) (l : List α) : Nat := go l 0 where
/-- Auxiliary for `countP`: `countP.go p l acc = countP p l + acc`. -/
@[specialize] go : List α Nat Nat
| [], acc => acc
| x :: xs, acc => bif p x then go xs (acc + 1) else go xs acc
/-! ### count -/
/-- `count a l` is the number of occurrences of `a` in `l`. -/
@[inline] def count [BEq α] (a : α) : List α Nat := countP (· == a)
/-! ### lookup -/
/--
@@ -1146,6 +1344,14 @@ def unzip : List (α × β) → List α × List β
/-! ## Ranges and enumeration -/
/-- Sum of a list of natural numbers. -/
-- This is not in the `List` namespace as later `List.sum` will be defined polymorphically.
protected def _root_.Nat.sum (l : List Nat) : Nat := l.foldr (·+·) 0
@[simp] theorem _root_.Nat.sum_nil : Nat.sum ([] : List Nat) = 0 := rfl
@[simp] theorem _root_.Nat.sum_cons (a : Nat) (l : List Nat) :
Nat.sum (a::l) = a + Nat.sum l := rfl
/-! ### range -/
/--
@@ -1161,6 +1367,14 @@ where
@[simp] theorem range_zero : range 0 = [] := rfl
/-! ### range' -/
/-- `range' start len step` is the list of numbers `[start, start+step, ..., start+(len-1)*step]`.
It is intended mainly for proving properties of `range` and `iota`. -/
def range' : (start len : Nat) (step : Nat := 1) List Nat
| _, 0, _ => []
| s, n+1, step => s :: range' (s+step) n step
/-! ### iota -/
/--

View File

@@ -127,12 +127,12 @@ results `y` for which `f x` returns `some y`.
@[inline]
def filterMapM {m : Type u Type v} [Monad m] {α β : Type u} (f : α m (Option β)) (as : List α) : m (List β) :=
let rec @[specialize] loop
| [], bs => pure bs
| [], bs => pure bs.reverse
| a :: as, bs => do
match ( f a) with
| none => loop as bs
| some b => loop as (b::bs)
loop as.reverse []
loop as []
/--
Folds a monadic function over a list from left to right:
@@ -227,6 +227,8 @@ def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
instance : ForIn m (List α) α where
forIn := List.forIn
@[simp] theorem forIn_eq_forIn [Monad m] : @List.forIn α β m _ = forIn := rfl
@[simp] theorem forIn_nil [Monad m] (f : α β m (ForInStep β)) (b : β) : forIn [] b f = pure b :=
rfl

View File

@@ -0,0 +1,242 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Sublist
/-!
# Lemmas about `List.countP` and `List.count`.
-/
namespace List
open Nat
/-! ### countP -/
section countP
variable (p q : α Bool)
@[simp] theorem countP_nil : countP p [] = 0 := rfl
protected theorem countP_go_eq_add (l) : countP.go p l n = n + countP.go p l 0 := by
induction l generalizing n with
| nil => rfl
| cons head tail ih =>
unfold countP.go
rw [ih (n := n + 1), ih (n := n), ih (n := 1)]
if h : p head then simp [h, Nat.add_assoc] else simp [h]
@[simp] theorem countP_cons_of_pos (l) (pa : p a) : countP p (a :: l) = countP p l + 1 := by
have : countP.go p (a :: l) 0 = countP.go p l 1 := show cond .. = _ by rw [pa]; rfl
unfold countP
rw [this, Nat.add_comm, List.countP_go_eq_add]
@[simp] theorem countP_cons_of_neg (l) (pa : ¬p a) : countP p (a :: l) = countP p l := by
simp [countP, countP.go, pa]
theorem countP_cons (a : α) (l) : countP p (a :: l) = countP p l + if p a then 1 else 0 := by
by_cases h : p a <;> simp [h]
theorem length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a => ¬p a) l := by
induction l with
| nil => rfl
| cons x h ih =>
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]
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]
theorem countP_eq_length_filter (l) : countP p l = length (filter p l) := by
induction l with
| nil => rfl
| cons x l ih =>
if h : p x
then rw [countP_cons_of_pos p l h, ih, filter_cons_of_pos h, length]
else rw [countP_cons_of_neg p l h, ih, filter_cons_of_neg h]
theorem countP_le_length : countP p l l.length := by
simp only [countP_eq_length_filter]
apply length_filter_le
@[simp] theorem countP_append (l₁ l₂) : countP p (l₁ ++ l₂) = countP p l₁ + countP p l₂ := by
simp only [countP_eq_length_filter, filter_append, length_append]
theorem countP_pos : 0 < countP p l a l, p a := by
simp only [countP_eq_length_filter, length_pos_iff_exists_mem, mem_filter, exists_prop]
theorem countP_eq_zero : countP p l = 0 a l, ¬p a := by
simp only [countP_eq_length_filter, length_eq_zero, filter_eq_nil]
theorem countP_eq_length : countP p l = l.length a l, p a := by
rw [countP_eq_length_filter, filter_length_eq_length]
theorem Sublist.countP_le (s : l₁ <+ l₂) : countP p l₁ countP p l₂ := by
simp only [countP_eq_length_filter]
apply s.filter _ |>.length_le
theorem countP_filter (l : List α) :
countP p (filter q l) = countP (fun a => p a q a) l := by
simp only [countP_eq_length_filter, filter_filter]
@[simp] theorem countP_true {l : List α} : (l.countP fun _ => true) = l.length := by
rw [countP_eq_length]
simp
@[simp] theorem countP_false {l : List α} : (l.countP fun _ => false) = 0 := by
rw [countP_eq_zero]
simp
@[simp] theorem countP_map (p : β Bool) (f : α β) :
l, countP p (map f l) = countP (p f) l
| [] => rfl
| a :: l => by rw [map_cons, countP_cons, countP_cons, countP_map p f l]; rfl
variable {p q}
theorem countP_mono_left (h : x l, p x q x) : countP p l countP q l := by
induction l with
| nil => apply Nat.le_refl
| cons a l ihl =>
rw [forall_mem_cons] at h
have ha, hl := h
simp [countP_cons]
cases h : p a
· simp only [Bool.false_eq_true, reduceIte, Nat.add_zero]
apply Nat.le_trans ?_ (Nat.le_add_right _ _)
apply ihl hl
· simp only [reduceIte, ha h, succ_le_succ_iff]
apply ihl hl
theorem countP_congr (h : x l, p x q x) : countP p l = countP q l :=
Nat.le_antisymm
(countP_mono_left fun x hx => (h x hx).1)
(countP_mono_left fun x hx => (h x hx).2)
end countP
/-! ### count -/
section count
variable [BEq α]
@[simp] theorem count_nil (a : α) : count a [] = 0 := rfl
theorem count_cons (a b : α) (l : List α) :
count a (b :: l) = count a l + if b == a then 1 else 0 := by
simp [count, countP_cons]
theorem count_tail : (l : List α) (a : α) (h : l []),
l.tail.count a = l.count a - if l.head h == a then 1 else 0
| head :: tail, a, _ => by simp [count_cons]
theorem count_le_length (a : α) (l : List α) : count a l l.length := countP_le_length _
theorem Sublist.count_le (h : l₁ <+ l₂) (a : α) : count a l₁ count a l₂ := h.countP_le _
theorem count_le_count_cons (a b : α) (l : List α) : count a l count a (b :: l) :=
(sublist_cons_self _ _).count_le _
theorem count_singleton (a b : α) : count a [b] = if b == a then 1 else 0 := by
simp [count_cons]
@[simp] theorem count_append (a : α) : l₁ l₂, count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=
countP_append _
variable [LawfulBEq α]
@[simp] theorem count_cons_self (a : α) (l : List α) : count a (a :: l) = count a l + 1 := by
simp [count_cons]
@[simp] theorem count_cons_of_ne (h : a b) (l : List α) : count a (b :: l) = count a l := by
simp only [count_cons, cond_eq_if, beq_iff_eq]
split <;> simp_all
theorem count_singleton_self (a : α) : count a [a] = 1 := by simp
theorem count_concat_self (a : α) (l : List α) :
count a (concat l a) = (count a l) + 1 := by simp
@[simp]
theorem count_pos_iff_mem {a : α} {l : List α} : 0 < count a l a l := by
simp only [count, countP_pos, beq_iff_eq, exists_eq_right]
theorem count_eq_zero_of_not_mem {a : α} {l : List α} (h : a l) : count a l = 0 :=
Decidable.byContradiction fun h' => h <| count_pos_iff_mem.1 (Nat.pos_of_ne_zero h')
theorem not_mem_of_count_eq_zero {a : α} {l : List α} (h : count a l = 0) : a l :=
fun h' => Nat.ne_of_lt (count_pos_iff_mem.2 h') h.symm
theorem count_eq_zero {l : List α} : count a l = 0 a l :=
not_mem_of_count_eq_zero, count_eq_zero_of_not_mem
theorem count_eq_length {l : List α} : count a l = l.length b l, a = b := by
rw [count, countP_eq_length]
refine fun h b hb => Eq.symm ?_, fun h b hb => ?_
· simpa using h b hb
· rw [h b hb, beq_self_eq_true]
@[simp] theorem count_replicate_self (a : α) (n : Nat) : count a (replicate n a) = n :=
(count_eq_length.2 <| fun _ h => (eq_of_mem_replicate h).symm).trans (length_replicate ..)
theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b == a then n else 0 := by
split <;> (rename_i h; simp only [beq_iff_eq] at h)
· exact b = a count_replicate_self ..
· exact count_eq_zero.2 <| mt eq_of_mem_replicate (Ne.symm h)
theorem filter_beq (l : List α) (a : α) : l.filter (· == a) = replicate (count a l) a := by
simp only [count, countP_eq_length_filter, eq_replicate, mem_filter, beq_iff_eq]
exact trivial, fun _ h => h.2
theorem filter_eq {α} [DecidableEq α] (l : List α) (a : α) : l.filter (· = a) = replicate (count a l) a :=
filter_beq l a
theorem le_count_iff_replicate_sublist {l : List α} : n count a l replicate n a <+ l := by
refine fun h => ?_, fun h => ?_
· exact ((replicate_sublist_replicate a).2 h).trans <| filter_beq l a filter_sublist _
· simpa only [count_replicate_self] using h.count_le a
theorem replicate_count_eq_of_count_eq_length {l : List α} (h : count a l = length l) :
replicate (count a l) a = l :=
(le_count_iff_replicate_sublist.mp (Nat.le_refl _)).eq_of_length <|
(length_replicate (count a l) a).trans h
@[simp] theorem count_filter {l : List α} (h : p a) : count a (filter p l) = count a l := by
rw [count, countP_filter]; congr; funext b
simp; rintro rfl; exact h
theorem count_le_count_map [DecidableEq β] (l : List α) (f : α β) (x : α) :
count x l count (f x) (map f l) := by
rw [count, count, countP_map]
apply countP_mono_left; simp (config := { contextual := true })
theorem count_erase (a b : α) :
l : List α, count a (l.erase b) = count a l - if b == a then 1 else 0
| [] => by simp
| c :: l => by
rw [erase_cons]
if hc : c = b then
have hc_beq := (beq_iff_eq _ _).mpr hc
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]
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]
else
rw [if_neg (by simpa using ha), Nat.sub_zero, Nat.sub_zero]
@[simp] theorem count_erase_self (a : α) (l : List α) :
count a (List.erase l a) = count a l - 1 := by rw [count_erase, if_pos (by simp)]
@[simp] theorem count_erase_of_ne (ab : a b) (l : List α) : count a (l.erase b) = count a l := by
rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
end count

View File

@@ -0,0 +1,445 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
Yury Kudryashov
-/
prelude
import Init.Data.List.Pairwise
/-!
# Lemmas about `List.eraseP` and `List.erase`.
-/
namespace List
open Nat
/-! ### eraseP -/
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
theorem eraseP_cons (a : α) (l : List α) :
(a :: l).eraseP p = bif p a then l else a :: l.eraseP p := rfl
@[simp] theorem eraseP_cons_of_pos {l : List α} {p} (h : p a) : (a :: l).eraseP p = l := by
simp [eraseP_cons, h]
@[simp] theorem eraseP_cons_of_neg {l : List α} {p} (h : ¬p a) :
(a :: l).eraseP p = a :: l.eraseP p := by simp [eraseP_cons, h]
theorem eraseP_of_forall_not {l : List α} (h : a, a l ¬p a) : l.eraseP p = l := by
induction l with
| nil => rfl
| cons _ _ ih => simp [h _ (.head ..), ih (forall_mem_cons.1 h).2]
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 =>
if pb : p b then
b, [], l, forall_mem_nil _, pb, by simp [pb]
else
match al with
| .head .. => nomatch pb pa
| .tail _ al =>
let c, l₁, l₂, h₁, h₂, h₃, h₄ := exists_of_eraseP al pa
c, b::l₁, l₂, (forall_mem_cons ..).2 pb, h₁,
h₂, by rw [h₃, cons_append], by simp [pb, h₄]
theorem exists_or_eq_self_of_eraseP (p) (l : List α) :
l.eraseP p = l
a l₁ l₂, ( b l₁, ¬p b) p a l = l₁ ++ a :: l₂ l.eraseP p = l₁ ++ l₂ :=
if h : a l, p a then
let _, ha, pa := h
.inr (exists_of_eraseP ha pa)
else
.inl (eraseP_of_forall_not (h ·, ·, ·))
@[simp] theorem length_eraseP_of_mem (al : a l) (pa : p a) :
length (l.eraseP p) = length l - 1 := by
let _, l₁, l₂, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [length_append, e₁]; rfl
theorem length_eraseP {l : List α} : (l.eraseP p).length = if l.any p then l.length - 1 else l.length := by
split <;> rename_i h
· simp only [any_eq_true] at h
obtain x, m, h := h
simp [length_eraseP_of_mem m h]
· simp only [any_eq_true] at h
rw [eraseP_of_forall_not]
simp_all
theorem eraseP_sublist (l : List α) : l.eraseP p <+ l := by
match exists_or_eq_self_of_eraseP p l with
| .inl h => rw [h]; apply Sublist.refl
| .inr c, l₁, l₂, _, _, h₃, h₄ => rw [h₄, h₃]; simp
theorem eraseP_subset (l : List α) : l.eraseP p l := (eraseP_sublist l).subset
protected theorem Sublist.eraseP : l₁ <+ l₂ l₁.eraseP p <+ l₂.eraseP p
| .slnil => Sublist.refl _
| .cons a s => by
by_cases h : p a
· simpa [h] using s.eraseP.trans (eraseP_sublist _)
· simpa [h] using s.eraseP.cons _
| .cons₂ a s => by
by_cases h : p a
· simpa [h] using s
· simpa [h] using s.eraseP
theorem length_eraseP_le (l : List α) : (l.eraseP p).length l.length :=
l.eraseP_sublist.length_le
theorem mem_of_mem_eraseP {l : List α} : a l.eraseP p a l := (eraseP_subset _ ·)
@[simp] theorem mem_eraseP_of_neg {l : List α} (pa : ¬p a) : a l.eraseP p a l := by
refine mem_of_mem_eraseP, fun al => ?_
match exists_or_eq_self_of_eraseP p l with
| .inl h => rw [h]; assumption
| .inr c, l₁, l₂, h₁, h₂, h₃, h₄ =>
rw [h₄]; rw [h₃] at al
have : a c := fun h => (h pa).elim h₂
simp [this] at al; simp [al]
@[simp] theorem eraseP_eq_self_iff {p} {l : List α} : l.eraseP p = l a l, ¬ p a := by
rw [ Sublist.length_eq (eraseP_sublist l), length_eraseP]
split <;> rename_i h
· simp only [any_eq_true, length_eq_zero] at h
constructor
· intro; simp_all [Nat.sub_one_eq_self]
· intro; obtain x, m, h := h; simp_all
· simp_all
theorem eraseP_map (f : β α) : (l : List β), (map f l).eraseP p = map f (l.eraseP (p f))
| [] => rfl
| b::l => by by_cases h : p (f b) <;> simp [h, eraseP_map f l, eraseP_cons_of_pos]
theorem eraseP_filterMap (f : α Option β) : (l : List α),
(filterMap f l).eraseP p = filterMap f (l.eraseP (fun x => match f x with | some y => p y | none => false))
| [] => rfl
| a::l => by
rw [filterMap_cons, eraseP_cons]
split <;> rename_i h
· simp [h, eraseP_filterMap]
· rename_i b
rw [h, eraseP_cons]
by_cases w : p b
· simp [w]
· simp only [w, cond_false]
rw [filterMap_cons_some h, eraseP_filterMap]
theorem eraseP_filter (f : α Bool) (l : List α) :
(filter f l).eraseP p = filter f (l.eraseP (fun x => p x && f x)) := by
rw [ filterMap_eq_filter, eraseP_filterMap]
congr
ext x
simp only [Option.guard]
split <;> split at * <;> simp_all
theorem eraseP_append_left {a : α} (pa : p a) :
{l₁ : List α} l₂, a l₁ (l₁++l₂).eraseP p = l₁.eraseP p ++ l₂
| x :: xs, l₂, h => by
by_cases h' : p x <;> simp [h']
rw [eraseP_append_left pa l₂ ((mem_cons.1 h).resolve_left (mt _ h'))]
intro | rfl => exact pa
theorem eraseP_append_right :
{l₁ : List α} l₂, ( b l₁, ¬p b) eraseP p (l₁++l₂) = l₁ ++ l₂.eraseP p
| [], l₂, _ => rfl
| x :: xs, l₂, h => by
simp [(forall_mem_cons.1 h).1, eraseP_append_right _ (forall_mem_cons.1 h).2]
theorem eraseP_append (l₁ l₂ : List α) :
(l₁ ++ l₂).eraseP p = if l₁.any p then l₁.eraseP p ++ l₂ else l₁ ++ l₂.eraseP p := by
split <;> rename_i h
· simp only [any_eq_true] at h
obtain x, m, h := h
rw [eraseP_append_left h _ m]
· simp only [any_eq_true] at h
rw [eraseP_append_right _]
simp_all
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ := by
cases exists_or_eq_self_of_eraseP p l with
| inl h =>
constructor
· intro h'
left
exact eraseP_eq_self_iff.1 h, by simp_all
· rintro (-, rfl | a, l₁, l₂, h₁, h₂, rfl, rfl)
· assumption
· rw [eraseP_append_right _ h₁, eraseP_cons_of_pos h₂]
| inr h =>
obtain a, l₁, l₂, h₁, h₂, w₁, w₂ := h
rw [w₂]
subst w₁
constructor
· rintro rfl
right
refine a, l₁, l₂, ?_
simp_all
· rintro (h | h)
· simp_all
· obtain a', l₁', l₂', h₁', h₂', h, rfl := h
have p : l₁ = l₁' := by
have q : l₁ = takeWhile (fun x => !p x) (l₁ ++ a :: l₂) := by
rw [takeWhile_append_of_pos (by simp_all),
takeWhile_cons_of_neg (by simp [h₂]), append_nil]
have q' : l₁' = takeWhile (fun x => !p x) (l₁' ++ a' :: l₂') := by
rw [takeWhile_append_of_pos (by simpa using h₁'),
takeWhile_cons_of_neg (by simp [h₂']), append_nil]
simp [h] at q
rw [q', q]
subst p
simp_all
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
(replicate n a).eraseP p = replicate (n - 1) a := by
cases n <;> simp [replicate_succ, h]
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
(replicate n a).eraseP p = replicate n a := by
rw [eraseP_of_forall_not (by simp_all)]
theorem Nodup.eraseP (p) : Nodup l Nodup (l.eraseP p) :=
Nodup.sublist <| eraseP_sublist _
theorem eraseP_comm {l : List α} (h : a l, ¬ p a ¬ q a) :
(l.eraseP p).eraseP q = (l.eraseP q).eraseP p := by
induction l with
| nil => rfl
| cons a l ih =>
simp only [eraseP_cons]
by_cases h₁ : p a
· by_cases h₂ : q a
· simp_all
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
· by_cases h₂ : 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))]
/-! ### erase -/
section erase
variable [BEq α]
@[simp] theorem erase_cons_head [LawfulBEq α] (a : α) (l : List α) : (a :: l).erase a = l := by
simp [erase_cons]
@[simp] theorem erase_cons_tail {a b : α} {l : List α} (h : ¬(b == a)) :
(b :: l).erase a = b :: l.erase a := by simp only [erase_cons, if_neg h]
theorem erase_of_not_mem [LawfulBEq α] {a : α} : {l : List α}, a l l.erase a = l
| [], _ => rfl
| b :: l, h => by
rw [mem_cons, not_or] at h
simp only [erase_cons, if_neg, erase_of_not_mem h.2, beq_iff_eq, Ne.symm h.1, not_false_eq_true]
theorem erase_eq_eraseP' (a : α) (l : List α) : l.erase a = l.eraseP (· == a) := by
induction l
· simp
· next b t ih =>
rw [erase_cons, eraseP_cons, ih]
if h : b == a then simp [h] else simp [h]
theorem erase_eq_eraseP [LawfulBEq α] (a : α) : l : List α, l.erase a = l.eraseP (a == ·)
| [] => rfl
| b :: l => by
if h : a = b then simp [h] else simp [h, Ne.symm h, erase_eq_eraseP a l]
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 _)
rw [erase_eq_eraseP]; exact l₁, l₂, fun h => h₁ _ h (beq_self_eq_true _), eq_of_beq e h₂, h₃
@[simp] theorem length_erase_of_mem [LawfulBEq α] {a : α} {l : List α} (h : a l) :
length (l.erase a) = length l - 1 := by
rw [erase_eq_eraseP]; exact length_eraseP_of_mem h (beq_self_eq_true a)
theorem length_erase [LawfulBEq α] (a : α) (l : List α) :
length (l.erase a) = if a l then length l - 1 else length l := by
rw [erase_eq_eraseP, length_eraseP]
split <;> split <;> simp_all
theorem erase_sublist (a : α) (l : List α) : l.erase a <+ l :=
erase_eq_eraseP' a l eraseP_sublist ..
theorem erase_subset (a : α) (l : List α) : l.erase a l := (erase_sublist a l).subset
theorem Sublist.erase (a : α) {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₁.erase a <+ l₂.erase a := by
simp only [erase_eq_eraseP']; exact h.eraseP
theorem length_erase_le (a : α) (l : List α) : (l.erase a).length l.length :=
(erase_sublist a l).length_le
theorem mem_of_mem_erase {a b : α} {l : List α} (h : a l.erase b) : a l := erase_subset _ _ h
@[simp] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {l : List α} (ab : a b) :
a l.erase b a l :=
erase_eq_eraseP b l mem_eraseP_of_neg (mt eq_of_beq ab.symm)
@[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
theorem erase_filter [LawfulBEq α] (f : α Bool) (l : List α) :
(filter f l).erase a = filter f (l.erase a) := by
induction l with
| nil => rfl
| cons x xs ih =>
by_cases h : a = x
· rw [erase_cons]
simp only [h, beq_self_eq_true, reduceIte]
rw [filter_cons]
split
· rw [erase_cons_head]
· rw [erase_of_not_mem]
simp_all [mem_filter]
· rw [erase_cons_tail (by simpa using Ne.symm h), filter_cons, filter_cons]
split
· rw [erase_cons_tail (by simpa using Ne.symm h), ih]
· rw [ih]
theorem erase_append_left [LawfulBEq α] {l₁ : List α} (l₂) (h : a l₁) :
(l₁ ++ l₂).erase a = l₁.erase a ++ l₂ := by
simp [erase_eq_eraseP]; exact eraseP_append_left (beq_self_eq_true a) l₂ h
theorem erase_append_right [LawfulBEq α] {a : α} {l₁ : List α} (l₂ : List α) (h : a l₁) :
(l₁ ++ l₂).erase a = (l₁ ++ l₂.erase a) := by
rw [erase_eq_eraseP, erase_eq_eraseP, eraseP_append_right]
intros b h' h''; rw [eq_of_beq h''] at h; exact h h'
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_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 ?_
if ha : a l then ?_ else
simp only [erase_of_not_mem ha, erase_of_not_mem (mt mem_of_mem_erase ha)]
if hb : b l then ?_ else
simp only [erase_of_not_mem hb, erase_of_not_mem (mt mem_of_mem_erase hb)]
match l, l.erase a, exists_erase_eq ha with
| _, _, l₁, l₂, ha', rfl, rfl =>
if h₁ : b l₁ then
rw [erase_append_left _ h₁, erase_append_left _ h₁,
erase_append_right _ (mt mem_of_mem_erase ha'), erase_cons_head]
else
rw [erase_append_right _ h₁, erase_append_right _ h₁, erase_append_right _ ha',
erase_cons_tail ab, erase_cons_head]
theorem erase_eq_iff [LawfulBEq α] {a : α} {l : List α} :
l.erase a = l'
(a l l = l')
l₁ l₂, a l₁ l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ := by
rw [erase_eq_eraseP', eraseP_eq_iff]
simp only [beq_iff_eq, forall_mem_ne', exists_and_left]
constructor
· rintro (h, rfl | a', l', h, rfl, x, rfl, rfl)
· left; simp_all
· right; refine l', h, x, by simp
· rintro (h, rfl | l₁, h, x, rfl, rfl)
· left; simp_all
· right; refine a, l₁, h, by simp
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
(replicate n a).erase a = replicate (n - 1) a := by
cases n <;> simp [replicate_succ]
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(replicate n a).erase b = replicate n a := by
rw [erase_of_not_mem]
simp_all
theorem Nodup.erase_eq_filter [BEq α] [LawfulBEq α] {l} (d : Nodup l) (a : α) : l.erase a = l.filter (· != a) := by
induction d with
| nil => rfl
| cons m _n ih =>
rename_i b l
by_cases h : b = a
· subst h
rw [erase_cons_head, filter_cons_of_neg (by simp)]
apply Eq.symm
rw [filter_eq_self]
simpa [@eq_comm α] using m
· simp [beq_false_of_ne h, ih, h]
theorem Nodup.mem_erase_iff [BEq α] [LawfulBEq α] {a : α} (d : Nodup l) : a l.erase b a b a l := by
rw [Nodup.erase_eq_filter d, mem_filter, and_comm, bne_iff_ne]
theorem Nodup.not_mem_erase [BEq α] [LawfulBEq α] {a : α} (h : Nodup l) : a l.erase a := fun H => by
simpa using ((Nodup.mem_erase_iff h).mp H).left
theorem Nodup.erase [BEq α] [LawfulBEq α] (a : α) : Nodup l Nodup (l.erase a) :=
Nodup.sublist <| erase_sublist _ _
end erase
/-! ### eraseIdx -/
theorem length_eraseIdx : {l i}, i < length l length (@eraseIdx α l i) = length l - 1
| [], _, _ => rfl
| _::_, 0, _ => by simp [eraseIdx]
| x::xs, i+1, h => by
have : i < length xs := Nat.lt_of_succ_lt_succ h
simp [eraseIdx, Nat.add_one]
rw [length_eraseIdx this, Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) this)]
@[simp] theorem eraseIdx_zero (l : List α) : eraseIdx l 0 = tail l := by cases l <;> rfl
theorem eraseIdx_eq_take_drop_succ :
(l : List α) (i : Nat), l.eraseIdx i = l.take i ++ l.drop (i + 1)
| nil, _ => by simp
| a::l, 0 => by simp
| a::l, i + 1 => by simp [eraseIdx_eq_take_drop_succ l i]
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 eraseIdx_subset (l : List α) (k : Nat) : eraseIdx l k l := (eraseIdx_sublist l k).subset
@[simp]
theorem eraseIdx_eq_self : {l : List α} {k : Nat}, eraseIdx l k = l length l k
| [], _ => by simp
| a::l, 0 => by simp [(cons_ne_self _ _).symm]
| a::l, k + 1 => by simp [eraseIdx_eq_self]
theorem eraseIdx_of_length_le {l : List α} {k : Nat} (h : length l k) : eraseIdx l k = l := by
rw [eraseIdx_eq_self.2 h]
theorem eraseIdx_append_of_lt_length {l : List α} {k : Nat} (hk : k < length l) (l' : List α) :
eraseIdx (l ++ l') k = eraseIdx l k ++ l' := by
induction l generalizing k with
| nil => simp_all
| cons x l ih =>
cases k with
| zero => rfl
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_lt_succ_iff]
theorem eraseIdx_append_of_length_le {l : List α} {k : Nat} (hk : length l k) (l' : List α) :
eraseIdx (l ++ l') k = l ++ eraseIdx l' (k - length l) := by
induction l generalizing k with
| nil => simp_all
| cons x l ih =>
cases k with
| zero => simp_all
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_sub_succ]
protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
eraseIdx l k <+: eraseIdx l' k := by
rcases h with t, rfl
if hkl : k < length l then
simp [eraseIdx_append_of_lt_length hkl]
else
rw [Nat.not_lt] at hkl
simp [eraseIdx_append_of_length_le hkl, eraseIdx_of_length_le hkl]
-- See also `mem_eraseIdx_iff_getElem` and `mem_eraseIdx_iff_getElem?` in
-- `Init/Data/List/Nat/Basic.lean`.
end List

View File

@@ -0,0 +1,229 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Lemmas
/-!
# Lemmas about `List.find?`, `List.findSome?`, `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?_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 find?_isSome_of_sublist {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
/-! ### findSome? -/
@[simp] theorem findSome?_cons_of_isSome (l) (h : (f a).isSome) : findSome? f (a :: l) = f a := by
simp only [findSome?]
split <;> simp_all
@[simp] theorem findSome?_cons_of_isNone (l) (h : (f a).isNone) : findSome? f (a :: l) = findSome? f l := by
simp only [findSome?]
split <;> simp_all
theorem exists_of_findSome?_eq_some {l : List α} {f : α Option β} (w : l.findSome? f = some b) :
a, a l f a = b := by
induction l with
| nil => simp_all
| cons h l ih =>
simp_all only [findSome?_cons, mem_cons, exists_eq_or_imp]
split at w <;> simp_all
@[simp] 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 =>
simp only [map_cons, findSome?]
split <;> simp_all
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, findSome?_cons]
split <;> simp_all
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
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 [findSome?_replicate]
@[simp] theorem find?_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]
theorem findSome?_isSome_of_sublist {l₁ l₂ : List α} (h : l₁ <+ l₂) :
(l₁.findSome? f).isSome (l₂.findSome? f).isSome := by
induction h with
| slnil => simp
| cons a h ih
| cons₂ a h ih =>
simp only [findSome?]
split <;> simp_all
/-! ### findIdx -/
theorem findIdx_cons (p : α Bool) (b : α) (l : List α) :
(b :: l).findIdx p = bif p b then 0 else (l.findIdx p) + 1 := by
cases H : p b with
| true => simp [H, findIdx, findIdx.go]
| false => simp [H, findIdx, findIdx.go, findIdx_go_succ]
where
findIdx_go_succ (p : α Bool) (l : List α) (n : Nat) :
List.findIdx.go p l (n + 1) = (findIdx.go p l n) + 1 := by
cases l with
| nil => unfold findIdx.go; exact Nat.succ_eq_add_one n
| cons head tail =>
unfold findIdx.go
cases p head <;> simp only [cond_false, cond_true]
exact findIdx_go_succ p tail (n + 1)
theorem findIdx_of_get?_eq_some {xs : List α} (w : xs.get? (xs.findIdx p) = some y) : p y := by
induction xs with
| nil => simp_all
| cons x xs ih => by_cases h : p x <;> simp_all [findIdx_cons]
theorem findIdx_get {xs : List α} {w : xs.findIdx p < xs.length} :
p (xs.get xs.findIdx p, w) :=
xs.findIdx_of_get?_eq_some (get?_eq_get w)
theorem findIdx_lt_length_of_exists {xs : List α} (h : x xs, p x) :
xs.findIdx p < xs.length := by
induction xs with
| nil => simp_all
| cons x xs ih =>
by_cases p x
· simp_all only [forall_exists_index, and_imp, mem_cons, exists_eq_or_imp, true_or,
findIdx_cons, cond_true, length_cons]
apply Nat.succ_pos
· simp_all [findIdx_cons]
refine Nat.succ_lt_succ ?_
obtain x', m', h' := h
exact ih x' m' h'
theorem findIdx_get?_eq_get_of_exists {xs : List α} (h : x xs, p x) :
xs.get? (xs.findIdx p) = some (xs.get xs.findIdx p, xs.findIdx_lt_length_of_exists h) :=
get?_eq_get (findIdx_lt_length_of_exists h)
/-! ### findIdx? -/
@[simp] theorem findIdx?_nil : ([] : List α).findIdx? p i = none := rfl
@[simp] theorem findIdx?_cons :
(x :: xs).findIdx? p i = if p x then some i else findIdx? p xs (i + 1) := rfl
@[simp] theorem findIdx?_succ :
(xs : List α).findIdx? p (i+1) = (xs.findIdx? p i).map fun i => i + 1 := by
induction xs generalizing i with simp
| cons _ _ _ => split <;> simp_all
theorem findIdx?_eq_some_iff (xs : List α) (p : α Bool) :
xs.findIdx? p = some i (xs.take (i + 1)).map p = replicate i false ++ [true] := by
induction xs generalizing i with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons, Nat.zero_add, findIdx?_succ, take_succ_cons, map_cons]
split <;> cases i <;> simp_all [replicate_succ, succ_inj']
theorem findIdx?_of_eq_some {xs : List α} {p : α Bool} (w : xs.findIdx? p = some i) :
match xs.get? i with | some a => p a | none => false := by
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp_all only [findIdx?_cons, Nat.zero_add, findIdx?_succ]
split at w <;> cases i <;> simp_all [succ_inj']
theorem findIdx?_of_eq_none {xs : List α} {p : α Bool} (w : xs.findIdx? p = none) :
i, match xs.get? i with | some a => ¬ p a | none => true := by
intro i
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp_all only [Bool.not_eq_true, findIdx?_cons, Nat.zero_add, findIdx?_succ]
cases i with
| zero =>
split at w <;> simp_all
| succ i =>
simp only [get?_cons_succ]
apply ih
split at w <;> simp_all
@[simp] theorem findIdx?_append :
(xs ++ ys : List α).findIdx? p =
(xs.findIdx? p <|> (ys.findIdx? p).map fun i => i + xs.length) := by
induction xs with simp
| cons _ _ _ => split <;> simp_all [Option.map_orElse, Option.map_map]; rfl
@[simp] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate, findIdx?_cons, Nat.zero_add, findIdx?_succ, Nat.zero_lt_succ, true_and]
split <;> simp_all
/-! ### indexOf -/
theorem indexOf_cons [BEq α] :
(x :: xs : List α).indexOf y = bif x == y then 0 else xs.indexOf y + 1 := by
dsimp [indexOf]
simp [findIdx_cons]
end List

View File

@@ -193,6 +193,17 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
apply funext; intro α; apply funext; intro n; apply funext; intro a
exact (replicateTR_loop_replicate_eq _ 0 n).symm
/-! ## Additional functions -/
/-! ### leftpad -/
/-- Optimized version of `leftpad`. -/
@[inline] def leftpadTR (n : Nat) (a : α) (l : List α) : List α :=
replicateTR.loop a (n - length l) l
@[csimp] theorem leftpad_eq_leftpadTR : @leftpad = @leftpadTR := by
funext α n a l; simp [leftpad, leftpadTR, replicateTR_loop_eq]
/-! ## Sublists -/
/-! ### take -/
@@ -295,6 +306,24 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
· rw [IH] <;> simp_all
· simp
/-- Tail-recursive version of `eraseP`. -/
@[inline] def erasePTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
@[specialize] go : List α Array α List α
| [], _ => l
| a :: l, acc => bif p a then acc.toListAppend l else go l (acc.push a)
@[csimp] theorem eraseP_eq_erasePTR : @eraseP = @erasePTR := by
funext α p l; simp [erasePTR]
let rec go (acc) : xs, l = acc.data ++ xs
erasePTR.go p l xs acc = acc.data ++ xs.eraseP p
| [] => fun h => by simp [erasePTR.go, eraseP, h]
| x::xs => by
simp [erasePTR.go, eraseP]; cases p x <;> simp
· intro h; rw [go _ xs]; {simp}; simp [h]
exact (go #[] _ rfl).symm
/-! ### eraseIdx -/
/-- Tail recursive version of `List.eraseIdx`. -/
@@ -348,6 +377,26 @@ def unzipTR (l : List (α × β)) : List α × List β :=
/-! ## Ranges and enumeration -/
/-! ### range' -/
/-- Optimized version of `range'`. -/
@[inline] def range'TR (s n : Nat) (step : Nat := 1) : List Nat := go n (s + step * n) [] where
/-- Auxiliary for `range'TR`: `range'TR.go n e = [e-n, ..., e-1] ++ acc`. -/
go : Nat Nat List Nat List Nat
| 0, _, acc => acc
| n+1, e, acc => go n (e-step) ((e-step) :: acc)
@[csimp] theorem range'_eq_range'TR : @range' = @range'TR := by
funext s n step
let rec go (s) : n m,
range'TR.go step n (s + step * n) (range' (s + step * n) m step) = range' s (n + m) step
| 0, m => by simp [range'TR.go]
| n+1, m => by
simp [range'TR.go]
rw [Nat.mul_succ, Nat.add_assoc, Nat.add_sub_cancel, Nat.add_right_comm n]
exact go s n (m + 1)
exact (go s n 0).symm
/-! ### iota -/
/-- Tail-recursive version of `List.iota`. -/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Lemmas
/-!
# Lemmas about `List.minimum?` and `List.maximum?.
-/
namespace List
open Nat
/-! ## Minima and maxima -/
/-! ### minimum? -/
@[simp] theorem minimum?_nil [Min α] : ([] : List α).minimum? = none := rfl
-- We don't put `@[simp]` on `minimum?_cons`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem minimum?_cons [Min α] {xs : List α} : (x :: xs).minimum? = foldl min x xs := rfl
@[simp] theorem minimum?_eq_none_iff {xs : List α} [Min α] : xs.minimum? = none xs = [] := by
cases xs <;> simp [minimum?]
theorem minimum?_mem [Min α] (min_eq_or : a b : α, min a b = a min a b = b) :
{xs : List α} xs.minimum? = some a a xs := by
intro xs
match xs with
| nil => simp
| x :: xs =>
simp only [minimum?_cons, Option.some.injEq, List.mem_cons]
intro eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons y xs ind =>
simp at eq
have p := ind _ eq
cases p with
| inl p =>
cases min_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem le_minimum?_iff [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c) :
{xs : List α} xs.minimum? = some a x, x a b, b xs x b
| nil => by simp
| cons x xs => by
rw [minimum?]
intro eq y
simp only [Option.some.injEq] at eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons z xs ih =>
simp at eq
simp [ih _ eq, le_min_iff, and_assoc]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
-- and `le_min_iff`.
theorem minimum?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ·)]
(le_refl : a : α, a a)
(min_eq_or : a b : α, min a b = a min a b = b)
(le_min_iff : a b c : α, a min b c a b a c) {xs : List α} :
xs.minimum? = some a a xs b, b xs a b := by
refine fun h => minimum?_mem min_eq_or h, (le_minimum?_iff le_min_iff h _).1 (le_refl _), ?_
intro h₁, h₂
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti.1
((le_minimum?_iff le_min_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
theorem minimum?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
(replicate n a).minimum? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, minimum?_cons]
@[simp] theorem minimum?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
(replicate n a).minimum? = some a := by
simp [minimum?_replicate, Nat.ne_of_gt h, w]
/-! ### maximum? -/
@[simp] theorem maximum?_nil [Max α] : ([] : List α).maximum? = none := rfl
-- We don't put `@[simp]` on `maximum?_cons`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem maximum?_cons [Max α] {xs : List α} : (x :: xs).maximum? = foldl max x xs := rfl
@[simp] theorem maximum?_eq_none_iff {xs : List α} [Max α] : xs.maximum? = none xs = [] := by
cases xs <;> simp [maximum?]
theorem maximum?_mem [Max α] (min_eq_or : a b : α, max a b = a max a b = b) :
{xs : List α} xs.maximum? = some a a xs
| nil => by simp
| cons x xs => by
rw [maximum?]; rintro
induction xs generalizing x with simp at *
| cons y xs ih =>
rcases ih (max x y) with h | h <;> simp [h]
simp [ or_assoc, min_eq_or x y]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem maximum?_le_iff [Max α] [LE α]
(max_le_iff : a b c : α, max b c a b a c a) :
{xs : List α} xs.maximum? = some a x, a x b xs, b x
| nil => by simp
| cons x xs => by
rw [maximum?]; rintro y
induction xs generalizing x with
| nil => simp
| cons y xs ih => simp [ih, max_le_iff, and_assoc]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `max_eq_or`,
-- and `le_min_iff`.
theorem maximum?_eq_some_iff [Max α] [LE α] [anti : Antisymm ((· : α) ·)]
(le_refl : a : α, a a)
(max_eq_or : a b : α, max a b = a max a b = b)
(max_le_iff : a b c : α, max b c a b a c a) {xs : List α} :
xs.maximum? = some a a xs b xs, b a := by
refine fun h => maximum?_mem max_eq_or h, (maximum?_le_iff max_le_iff h _).1 (le_refl _), ?_
intro h₁, h₂
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti.1
(h₂ _ (maximum?_mem max_eq_or (xs := x::xs) rfl))
((maximum?_le_iff max_le_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
theorem maximum?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
(replicate n a).maximum? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, maximum?_cons]
@[simp] theorem maximum?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
(replicate n a).maximum? = some a := by
simp [maximum?_replicate, Nat.ne_of_gt h, w]
end List

View File

@@ -0,0 +1,69 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.TakeDrop
/-!
# Lemmas about `List.mapM` and `List.forM`.
-/
namespace List
open Nat
/-! ## Monadic operations -/
-- We may want to replace these `simp` attributes with explicit equational lemmas,
-- as we already have for all the non-monadic functions.
attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
-- Previously `mapM.loop`, `filterMapM.loop`, `forIn.loop`, `forIn'.loop`
-- had attribute `@[simp]`.
-- We don't currently provide simp lemmas,
-- as this is an internal implementation and they don't seem to be needed.
/-! ### mapM -/
/-- Alternate (non-tail-recursive) form of mapM for proofs. -/
def mapM' [Monad m] (f : α m β) : List α m (List β)
| [] => pure []
| a :: l => return ( f a) :: ( l.mapM' f)
@[simp] theorem mapM'_nil [Monad m] {f : α m β} : mapM' f [] = pure [] := rfl
@[simp] theorem mapM'_cons [Monad m] {f : α m β} :
mapM' f (a :: l) = return (( f a) :: ( l.mapM' f)) :=
rfl
theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α m β) (l : List α) :
mapM' f l = mapM f l := by simp [go, mapM] where
go : l acc, mapM.loop f l acc = return acc.reverse ++ ( mapM' f l)
| [], acc => by simp [mapM.loop, mapM']
| a::l, acc => by simp [go l, mapM.loop, mapM']
@[simp] theorem mapM_nil [Monad m] (f : α m β) : [].mapM f = pure [] := rfl
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α m β) :
(a :: l).mapM f = (return ( f a) :: ( l.mapM f)) := by simp [ mapM'_eq_mapM, mapM']
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α m β) {l₁ l₂ : List α} :
(l₁ ++ l₂).mapM f = (return ( l₁.mapM f) ++ ( l₂.mapM f)) := by induction l₁ <;> simp [*]
/-! ### forM -/
-- We use `List.forM` as the simp normal form, rather that `ForM.forM`.
-- As such we need to replace `List.forM_nil` and `List.forM_cons`:
@[simp] theorem forM_nil' [Monad m] : ([] : List α).forM f = (pure .unit : m PUnit) := rfl
@[simp] theorem forM_cons' [Monad m] :
(a::as).forM f = (f a >>= fun _ => as.forM f : m PUnit) :=
List.forM_cons _ _ _
@[simp] theorem forM_append [Monad m] [LawfulMonad m] (l₁ l₂ : List α) (f : α m PUnit) :
(l₁ ++ l₂).forM f = (do l₁.forM f; l₂.forM f) := by
induction l₁ <;> simp [*]
end List

View File

@@ -0,0 +1,10 @@
/-
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.Nat.Basic
import Init.Data.List.Nat.Pairwise
import Init.Data.List.Nat.Range
import Init.Data.List.Nat.TakeDrop

View File

@@ -0,0 +1,125 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Count
import Init.Data.List.MinMax
import Init.Data.Nat.Lemmas
/-!
# Miscellaneous `List` lemmas, that require more `Nat` lemmas than are available in `Init.Data.List.Lemmas`.
In particular, `omega` is available here.
-/
open Nat
namespace List
/-! ### filter -/
theorem length_filter_lt_length_iff_exists (l) :
length (filter p l) < length l x l, ¬p x := by
simpa [length_eq_countP_add_countP p l, countP_eq_length_filter] using
countP_pos (fun x => ¬p x) (l := l)
/-! ### leftpad -/
/-- The length of the List returned by `List.leftpad n a l` is equal
to the larger of `n` and `l.length` -/
@[simp]
theorem leftpad_length (n : Nat) (a : α) (l : List α) :
(leftpad n a l).length = max n l.length := by
simp only [leftpad, length_append, length_replicate, Nat.sub_add_eq_max]
/-! ### eraseIdx -/
theorem mem_eraseIdx_iff_getElem {x : α} :
{l} {k}, x eraseIdx l k i h, i k l[i]'h = x
| [], _ => by
simp only [eraseIdx, not_mem_nil, false_iff]
rintro i, h, -
exact Nat.not_lt_zero _ h
| a::l, 0 => by simp [mem_iff_getElem, Nat.succ_lt_succ_iff]
| a::l, k+1 => by
rw [ Nat.or_exists_add_one]
simp [mem_eraseIdx_iff_getElem, @eq_comm _ a, succ_inj', Nat.succ_lt_succ_iff]
theorem mem_eraseIdx_iff_getElem? {x : α} {l} {k} : x eraseIdx l k i k, l[i]? = some x := by
simp only [mem_eraseIdx_iff_getElem, getElem_eq_iff, exists_and_left]
refine exists_congr fun i => and_congr_right' ?_
constructor
· rintro _, h; exact h
· rintro h;
obtain h', - := getElem?_eq_some.1 h
exact h', h
/-! ### minimum? -/
-- A specialization of `minimum?_eq_some_iff` to Nat.
theorem minimum?_eq_some_iff' {xs : List Nat} :
xs.minimum? = some a (a xs b xs, a b) :=
minimum?_eq_some_iff
(le_refl := Nat.le_refl)
(min_eq_or := fun _ _ => by omega)
(le_min_iff := fun _ _ _ => by omega)
-- This could be generalized,
-- but will first require further work on order typeclasses in the core repository.
theorem minimum?_cons' {a : Nat} {l : List Nat} :
(a :: l).minimum? = some (match l.minimum? with
| none => a
| some m => min a m) := by
rw [minimum?_eq_some_iff']
split <;> rename_i h m
· simp_all
· rw [minimum?_eq_some_iff'] at m
obtain m, le := m
rw [Nat.min_def]
constructor
· split
· exact mem_cons_self a l
· exact mem_cons_of_mem a m
· intro b m
cases List.mem_cons.1 m with
| inl => split <;> omega
| inr h =>
specialize le b h
split <;> omega
/-! ### maximum? -/
-- A specialization of `maximum?_eq_some_iff` to Nat.
theorem maximum?_eq_some_iff' {xs : List Nat} :
xs.maximum? = some a (a xs b xs, b a) :=
maximum?_eq_some_iff
(le_refl := Nat.le_refl)
(max_eq_or := fun _ _ => by omega)
(max_le_iff := fun _ _ _ => by omega)
-- This could be generalized,
-- but will first require further work on order typeclasses in the core repository.
theorem maximum?_cons' {a : Nat} {l : List Nat} :
(a :: l).maximum? = some (match l.maximum? with
| none => a
| some m => max a m) := by
rw [maximum?_eq_some_iff']
split <;> rename_i h m
· simp_all
· rw [maximum?_eq_some_iff'] at m
obtain m, le := m
rw [Nat.max_def]
constructor
· split
· exact mem_cons_of_mem a m
· exact mem_cons_self a l
· intro b m
cases List.mem_cons.1 m with
| inl => split <;> omega
| inr h =>
specialize le b h
split <;> omega
end List

View File

@@ -0,0 +1,73 @@
/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, James Gallicchio
-/
prelude
import Init.Data.Fin.Lemmas
import Init.Data.List.Nat.TakeDrop
import Init.Data.List.Pairwise
/-!
# Lemmas about `List.Pairwise`
-/
namespace List
/-- Given a list `is` of monotonically increasing indices into `l`, getting each index
produces a sublist of `l`. -/
theorem map_getElem_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (· < ·)) :
is.map (l[·]) <+ l := by
suffices n l', l' = l.drop n ( i is, n i) map (l[·]) is <+ l'
from this 0 l (by simp) (by simp)
rintro n l' rfl his
induction is generalizing n with
| nil => simp
| cons hd tl IH =>
simp only [Fin.getElem_fin, map_cons]
have := IH h.of_cons (hd+1) (pairwise_cons.mp h).1
specialize his hd (.head _)
have := (drop_eq_getElem_cons ..).symm this.cons₂ (get l hd)
have := Sublist.append (nil_sublist (take hd l |>.drop n)) this
rwa [nil_append, (drop_append_of_le_length ?_), take_append_drop] at this
simp [Nat.min_eq_left (Nat.le_of_lt hd.isLt), his]
@[deprecated map_getElem_sublist (since := "2024-07-30")]
theorem map_get_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (·.val < ·.val)) :
is.map (get l) <+ l := by
simpa using map_getElem_sublist h
/-- Given a sublist `l' <+ l`, there exists an increasing list of indices `is` such that
`l' = is.map fun i => l[i]`. -/
theorem sublist_eq_map_getElem {l l' : List α} (h : l' <+ l) : is : List (Fin l.length),
l' = is.map (l[·]) is.Pairwise (· < ·) := by
induction h with
| slnil => exact [], by simp
| cons _ _ IH =>
let is, IH := IH
refine is.map (·.succ), ?_
simpa [Function.comp_def, pairwise_map]
| cons₂ _ _ IH =>
rcases IH with is,IH
refine 0, by simp [Nat.zero_lt_succ] :: is.map (·.succ), ?_
simp [Function.comp_def, pairwise_map, IH, get_eq_getElem]
@[deprecated sublist_eq_map_getElem (since := "2024-07-30")]
theorem sublist_eq_map_get (h : l' <+ l) : is : List (Fin l.length),
l' = map (get l) is is.Pairwise (· < ·) := by
simpa using sublist_eq_map_getElem h
theorem pairwise_iff_getElem : Pairwise R l
(i j : Nat) (_hi : i < l.length) (_hj : j < l.length) (_hij : i < j), R l[i] l[j] := by
rw [pairwise_iff_forall_sublist]
constructor <;> intro h
· intros i j hi hj h'
apply h
simpa [h'] using map_getElem_sublist (is := [i, hi, j, hj])
· intros a b h'
have is, h', hij := sublist_eq_map_getElem h'
rcases is with | a', | b', <;> simp at h'
rcases h' with rfl, rfl
apply h; simpa using hij
end List

View File

@@ -0,0 +1,387 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Nat.TakeDrop
import Init.Data.List.Pairwise
/-!
# Lemmas about `List.range` and `List.enum`
-/
namespace List
open Nat
/-! ## Ranges and enumeration -/
/-! ### range' -/
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' _ _ _)
@[simp] theorem range'_eq_nil : range' s n step = [] n = 0 := by
rw [ length_eq_zero, length_range']
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
have h (i) : i n i = 0 j, i = succ j j < n := by
cases i <;> simp [Nat.succ_le, Nat.succ_inj']
simp [range', mem_range', Nat.lt_succ, h]; simp only [ exists_and_right, and_assoc]
rw [exists_comm]; simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
@[simp] theorem mem_range'_1 : m range' s n s m m < s + n := by
simp [mem_range']; exact
fun i, h, e => e Nat.le_add_right .., Nat.add_lt_add_left h _,
fun h₁, h₂ => m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm
theorem pairwise_lt_range' s n (step := 1) (pos : 0 < step := by simp) :
Pairwise (· < ·) (range' s n step) :=
match s, n, step, pos with
| _, 0, _, _ => Pairwise.nil
| s, n + 1, step, pos => by
simp only [range'_succ, pairwise_cons]
constructor
· intros n m
rw [mem_range'] at m
omega
· exact pairwise_lt_range' (s + step) n step pos
theorem pairwise_le_range' s n (step := 1) :
Pairwise (· ·) (range' s n step) :=
match s, n, step with
| _, 0, _ => Pairwise.nil
| s, n + 1, step => by
simp only [range'_succ, pairwise_cons]
constructor
· intros n m
rw [mem_range'] at m
omega
· exact pairwise_le_range' (s + step) n step
theorem nodup_range' (s n : Nat) (step := 1) (h : 0 < step := by simp) : Nodup (range' s n step) :=
(pairwise_lt_range' s n step h).imp Nat.ne_of_lt
@[simp]
theorem map_add_range' (a) : s n step, map (a + ·) (range' s n step) = range' (a + s) n step
| _, 0, _ => rfl
| s, n + 1, step => by simp [range', map_add_range' _ (s + step) n step, Nat.add_assoc]
theorem map_sub_range' (a s n : Nat) (h : a s) :
map (· - a) (range' s n step) = range' (s - a) n step := by
conv => lhs; rw [ Nat.add_sub_cancel' h]
rw [ map_add_range', map_map, (?_ : __ = _), map_id]
funext x; apply Nat.add_sub_cancel_left
theorem range'_append : s m n step : Nat,
range' s m step ++ range' (s + step * m) n step = range' s (n + m) step
| s, 0, n, step => rfl
| s, m + 1, n, step => by
simpa [range', Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
using range'_append (s + step) m n step
@[simp] theorem range'_append_1 (s m n : Nat) :
range' s m ++ range' (s + m) n = range' s (n + m) := by simpa using range'_append s m n 1
theorem range'_sublist_right {s m n : Nat} : range' s m step <+ range' s n step m n :=
fun h => by simpa only [length_range'] using h.length_le,
fun h => by rw [ Nat.sub_add_cancel h, range'_append]; apply sublist_append_left
theorem range'_subset_right {s m n : Nat} (step0 : 0 < step) :
range' s m step range' s n step m n := by
refine fun h => Nat.le_of_not_lt fun hn => ?_, fun h => (range'_sublist_right.2 h).subset
have i, h', e := mem_range'.1 <| h <| mem_range'.2 _, hn, rfl
exact Nat.ne_of_gt h' (Nat.eq_of_mul_eq_mul_left step0 (Nat.add_left_cancel e))
theorem range'_subset_right_1 {s m n : Nat} : range' s m range' s n m n :=
range'_subset_right (by decide)
theorem getElem?_range' (s step) :
{m n : Nat}, m < n (range' s n step)[m]? = some (s + step * m)
| 0, n + 1, _ => by simp [range'_succ]
| m + 1, n + 1, h => by
simp only [range'_succ, getElem?_cons_succ]
exact (getElem?_range' (s + step) step (Nat.lt_of_add_lt_add_right h)).trans <| by
simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
@[simp] theorem getElem_range' {n m step} (i) (H : i < (range' n m step).length) :
(range' n m step)[i] = n + step * i :=
(getElem?_eq_some.1 <| getElem?_range' n step (by simpa using H)).2
theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ [s + step * n] := by
rw [Nat.add_comm n 1]; exact (range'_append s n 1 step).symm
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ [s + n] := by
simp [range'_concat]
/-! ### range -/
theorem range_loop_range' : s n : Nat, range.loop s (range' s n) = range' 0 (n + s)
| 0, n => rfl
| s + 1, n => by rw [ Nat.add_assoc, Nat.add_right_comm n s 1]; exact range_loop_range' s (n + 1)
theorem range_eq_range' (n : Nat) : range n = range' 0 n :=
(range_loop_range' n 0).trans <| by rw [Nat.zero_add]
theorem range_succ_eq_map (n : Nat) : range (n + 1) = 0 :: map succ (range n) := by
rw [range_eq_range', range_eq_range', range', Nat.add_comm, map_add_range']
congr; exact funext (Nat.add_comm 1)
theorem reverse_range' : s n : Nat, reverse (range' s n) = map (s + n - 1 - ·) (range n)
| s, 0 => rfl
| s, n + 1 => by
rw [range'_1_concat, reverse_append, range_succ_eq_map,
show s + (n + 1) - 1 = s + n from rfl, map, map_map]
simp [reverse_range', Nat.sub_right_comm, Nat.sub_sub]
theorem range'_eq_map_range (s n : Nat) : range' s n = map (s + ·) (range n) := by
rw [range_eq_range', map_add_range']; rfl
@[simp] theorem length_range (n : Nat) : length (range n) = n := by
simp only [range_eq_range', length_range']
@[simp] theorem range_eq_nil {n : Nat} : range n = [] n = 0 := by
rw [ length_eq_zero, length_range]
@[simp]
theorem range_sublist {m n : Nat} : range m <+ range n m n := by
simp only [range_eq_range', range'_sublist_right]
@[simp]
theorem range_subset {m n : Nat} : range m range n m n := by
simp only [range_eq_range', range'_subset_right, lt_succ_self]
@[simp]
theorem mem_range {m n : Nat} : m range n m < n := by
simp only [range_eq_range', mem_range'_1, Nat.zero_le, true_and, Nat.zero_add]
theorem not_mem_range_self {n : Nat} : n range n := by simp
theorem self_mem_range_succ (n : Nat) : n range (n + 1) := by simp
theorem pairwise_lt_range (n : Nat) : Pairwise (· < ·) (range n) := by
simp (config := {decide := true}) only [range_eq_range', pairwise_lt_range']
theorem pairwise_le_range (n : Nat) : Pairwise (· ·) (range n) :=
Pairwise.imp Nat.le_of_lt (pairwise_lt_range _)
theorem getElem?_range {m n : Nat} (h : m < n) : (range n)[m]? = some m := by
simp [range_eq_range', getElem?_range' _ _ h]
@[simp] theorem getElem_range {n : Nat} (m) (h : m < (range n).length) : (range n)[m] = m := by
simp [range_eq_range']
theorem range_succ (n : Nat) : range (succ n) = range n ++ [n] := by
simp only [range_eq_range', range'_1_concat, Nat.zero_add]
theorem range_add (a b : Nat) : range (a + b) = range a ++ (range b).map (a + ·) := by
rw [ range'_eq_map_range]
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 0 a b).symm
theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
apply List.ext_getElem
· simp
· simp (config := { contextual := true }) [ getElem_take, Nat.lt_min]
theorem nodup_range (n : Nat) : Nodup (range n) := by
simp (config := {decide := true}) only [range_eq_range', nodup_range']
/-! ### iota -/
theorem iota_eq_reverse_range' : n : Nat, iota n = reverse (range' 1 n)
| 0 => rfl
| n + 1 => by simp [iota, range'_concat, iota_eq_reverse_range' n, reverse_append, Nat.add_comm]
@[simp] theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
@[simp]
theorem mem_iota {m n : Nat} : m iota n 1 m m n := by
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range' 1 n
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
(pairwise_gt_iota n).imp Nat.ne_of_gt
/-! ### enumFrom -/
@[simp]
theorem enumFrom_singleton (x : α) (n : Nat) : enumFrom n [x] = [(n, x)] :=
rfl
@[simp]
theorem enumFrom_eq_nil {n : Nat} {l : List α} : List.enumFrom n l = [] l = [] := by
cases l <;> simp
@[simp] theorem enumFrom_length : {n} {l : List α}, (enumFrom n l).length = l.length
| _, [] => rfl
| _, _ :: _ => congrArg Nat.succ enumFrom_length
@[simp]
theorem getElem?_enumFrom :
n (l : List α) m, (enumFrom n l)[m]? = l[m]?.map fun a => (n + m, a)
| n, [], m => rfl
| n, a :: l, 0 => by simp
| n, a :: l, m + 1 => by
simp only [enumFrom_cons, getElem?_cons_succ]
exact (getElem?_enumFrom (n + 1) l m).trans <| by rw [Nat.add_right_comm]; rfl
@[simp]
theorem getElem_enumFrom (l : List α) (n) (i : Nat) (h : i < (l.enumFrom n).length) :
(l.enumFrom n)[i] = (n + i, l[i]'(by simpa [enumFrom_length] using h)) := by
simp only [enumFrom_length] at h
rw [getElem_eq_getElem?]
simp only [getElem?_enumFrom, getElem?_eq_getElem h]
simp
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?]
theorem mk_mem_enumFrom_iff_le_and_getElem?_sub {n i : Nat} {x : α} {l : List α} :
(i, x) enumFrom n l n i l[i - n]? = x := by
if h : n i then
rcases Nat.exists_eq_add_of_le h with i, rfl
simp [mk_add_mem_enumFrom_iff_getElem?, Nat.add_sub_cancel_left]
else
have : k, n + k i := by rintro k rfl; simp at h
simp [h, mem_iff_get?, this]
theorem le_fst_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
n x.1 :=
(mk_mem_enumFrom_iff_le_and_getElem?_sub.1 h).1
theorem fst_lt_add_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.1 < n + length l := by
rcases mem_iff_get.1 h with i, rfl
simpa using i.isLt
theorem map_enumFrom (f : α β) (n : Nat) (l : List α) :
map (Prod.map id f) (enumFrom n l) = enumFrom n (map f l) := by
induction l generalizing n <;> simp_all
@[simp]
theorem enumFrom_map_fst (n) :
(l : List α), map Prod.fst (enumFrom n l) = range' n l.length
| [] => rfl
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
@[simp]
theorem enumFrom_map_snd : (n) (l : List α), map Prod.snd (enumFrom n l) = l
| _, [] => rfl
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
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 map_fst_add_enumFrom_eq_enumFrom (l : List α) (n k : Nat) :
map (Prod.map (· + n) id) (enumFrom k l) = enumFrom (n + k) l :=
ext_getElem? fun i by simp [(· ·), Nat.add_comm, Nat.add_left_comm]; rfl
theorem map_fst_add_enum_eq_enumFrom (l : List α) (n : Nat) :
map (Prod.map (· + n) id) (enum l) = enumFrom n l :=
map_fst_add_enumFrom_eq_enumFrom l _ _
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
rw [enumFrom_cons, Nat.add_comm, map_fst_add_enumFrom_eq_enumFrom]
theorem enumFrom_map (n : Nat) (l : List α) (f : α β) :
enumFrom n (l.map f) = (enumFrom n l).map (Prod.map id f) := by
induction l with
| nil => rfl
| cons hd tl IH =>
rw [map_cons, enumFrom_cons', enumFrom_cons', map_cons, map_map, IH, map_map]
rfl
theorem enumFrom_append (xs ys : List α) (n : Nat) :
enumFrom n (xs ++ ys) = enumFrom n xs ++ enumFrom (n + xs.length) ys := by
induction xs generalizing ys n with
| nil => simp
| cons x xs IH =>
rw [cons_append, enumFrom_cons, IH, cons_append, enumFrom_cons, length, Nat.add_right_comm,
Nat.add_assoc]
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
@[simp]
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
(l.enumFrom n).unzip = (range' n l.length, l) := by
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
/-! ### enum -/
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
theorem enum_cons' (x : α) (xs : List α) :
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
enumFrom_cons' _ _ _
@[simp]
theorem enum_eq_nil {l : List α} : List.enum l = [] l = [] := enumFrom_eq_nil
@[simp] theorem enum_singleton (x : α) : enum [x] = [(0, x)] := rfl
@[simp] theorem enum_length : (enum l).length = l.length :=
enumFrom_length
@[simp]
theorem getElem?_enum (l : List α) (n : Nat) : (enum l)[n]? = l[n]?.map fun a => (n, a) := by
rw [enum, getElem?_enumFrom, Nat.zero_add]
@[simp]
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]
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]
theorem mem_enum_iff_getElem? {x : Nat × α} {l : List α} : x enum l l[x.1]? = some x.2 :=
mk_mem_enum_iff_getElem?
theorem fst_lt_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.1 < length l := by
simpa using fst_lt_add_of_mem_enumFrom h
theorem snd_mem_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.2 l :=
snd_mem_of_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
@[simp] theorem enum_map_fst (l : List α) : map Prod.fst (enum l) = range l.length := by
simp only [enum, enumFrom_map_fst, range_eq_range']
@[simp]
theorem enum_map_snd (l : List α) : map Prod.snd (enum l) = l :=
enumFrom_map_snd _ _
theorem enum_map (l : List α) (f : α β) : (l.map f).enum = l.enum.map (Prod.map id f) :=
enumFrom_map _ _ _
theorem enum_append (xs ys : List α) : enum (xs ++ ys) = enum xs ++ enumFrom xs.length ys := by
simp [enum, enumFrom_append]
theorem enum_eq_zip_range (l : List α) : l.enum = (range l.length).zip l :=
zip_of_prod (enum_map_fst _) (enum_map_snd _)
@[simp]
theorem unzip_enum_eq_prod (l : List α) : l.enum.unzip = (range l.length, l) := by
simp only [enum_eq_zip_range, unzip_zip, length_range]
end List

View File

@@ -0,0 +1,503 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Zip
import Init.Data.Nat.Lemmas
/-!
# Further lemmas about `List.take`, `List.drop`, `List.zip` and `List.zipWith`.
These are in a separate file from most of the list lemmas
as they required importing more lemmas about natural numbers, and use `omega`.
-/
namespace List
open Nat
/-! ### take -/
@[simp] theorem length_take : (i : Nat) (l : List α), length (take i l) = min i (length l)
| 0, l => by simp [Nat.zero_min]
| succ n, [] => by simp [Nat.min_zero]
| succ n, _ :: l => by simp [Nat.succ_min_succ, length_take]
theorem length_take_le (n) (l : List α) : length (take n l) n := by simp [Nat.min_le_left]
theorem length_take_le' (n) (l : List α) : length (take n l) l.length :=
by simp [Nat.min_le_right]
theorem length_take_of_le (h : n length l) : length (take n l) = n := by simp [Nat.min_eq_left h]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
theorem getElem_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
L[i] = (L.take j)[i]'(length_take .. Nat.lt_min.mpr hj, hi) :=
getElem_of_eq (take_append_drop j L).symm _ getElem_append ..
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
theorem getElem_take' (L : List α) {j i : Nat} {h : i < (L.take j).length} :
(L.take j)[i] =
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
rw [length_take, Nat.lt_min] at h; rw [getElem_take L _ h.1]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
@[deprecated getElem_take (since := "2024-06-12")]
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
get L i, hi = get (L.take j) i, length_take .. Nat.lt_min.mpr hj, hi := by
simp [getElem_take _ hi hj]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
@[deprecated getElem_take (since := "2024-06-12")]
theorem get_take' (L : List α) {j i} :
get (L.take j) i =
get L i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _) := by
simp [getElem_take']
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n m) :
(l.take n)[m]? = none :=
getElem?_eq_none <| Nat.le_trans (length_take_le _ _) h
@[deprecated getElem?_take_eq_none (since := "2024-06-12")]
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} :
(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_eq_none (Nat.le_of_not_lt h)
@[deprecated getElem?_take_eq_if (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]
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]
split
· rw [if_neg (by omega)]
· rw [if_pos (by omega)]
theorem head_take {l : List α} {n : Nat} (h : l.take n []) :
(l.take n).head h = l.head (by simp_all) := by
apply Option.some_inj.1
rw [ head?_eq_head, head?_eq_head, head?_take, if_neg]
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]
split
· rw [if_neg (by omega)]
rw [Nat.min_def]
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [ getLast?_eq_getElem?, getElem?_eq_none (by omega)]
simp
· rw [if_pos]
omega
theorem getLast_take {l : List α} (h : l.take n []) :
(l.take n).getLast h = l[n - 1]?.getD (l.getLast (by simp_all)) := by
rw [getLast_eq_getElem, getElem_take']
simp [length_take, Nat.min_def]
simp at h
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [getElem?_eq_none (by omega), getLast_eq_getElem]
simp
theorem take_take : (n m) (l : List α), take n (take m l) = take (min n m) l
| n, 0, l => by rw [Nat.min_zero, take_zero, take_nil]
| 0, m, l => by rw [Nat.zero_min, take_zero, take_zero]
| succ n, succ m, nil => by simp only [take_nil]
| succ n, succ m, a :: l => by
simp only [take, succ_min_succ, take_take n m l]
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]
split
· next h' => rw [getElem?_set_ne (by omega)]
· rfl
@[simp] theorem take_replicate (a : α) : n m : Nat, take n (replicate m a) = replicate (min n m) a
| n, 0 => by simp [Nat.min_zero]
| 0, m => by simp [Nat.zero_min]
| succ n, succ m => by simp [replicate_succ, succ_min_succ, take_replicate]
@[simp] theorem drop_replicate (a : α) : n m : Nat, drop n (replicate m a) = replicate (m - n) a
| n, 0 => by simp
| 0, m => by simp
| succ n, succ m => by simp [replicate_succ, succ_sub_succ, drop_replicate]
/-- Taking the first `n` elements in `l₁ ++ l₂` is the same as appending the first `n` elements
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
theorem take_append_eq_append_take {l₁ l₂ : List α} {n : Nat} :
take n (l₁ ++ l₂) = take n l₁ ++ take (n - l₁.length) l₂ := by
induction l₁ generalizing n
· simp
· cases n
· simp [*]
· simp only [cons_append, take_succ_cons, length_cons, succ_eq_add_one, cons.injEq,
append_cancel_left_eq, true_and, *]
congr 1
omega
theorem take_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n l₁.length) :
(l₁ ++ l₂).take n = l₁.take n := by
simp [take_append_eq_append_take, Nat.sub_eq_zero_of_le h]
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
`i` elements of `l₂` to `l₁`. -/
theorem take_append {l₁ l₂ : List α} (i : Nat) :
take (l₁.length + i) (l₁ ++ l₂) = l₁ ++ take i l₂ := by
rw [take_append_eq_append_take, take_of_length_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
@[simp]
theorem take_eq_take :
{l : List α} {m n : Nat}, l.take m = l.take n min m l.length = min n l.length
| [], m, n => by simp [Nat.min_zero]
| _ :: xs, 0, 0 => by simp
| x :: xs, m + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, 0, n + 1 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, m + 1, n + 1 => by simp [succ_min_succ, take_eq_take]
theorem take_add (l : List α) (m n : Nat) : l.take (m + n) = l.take m ++ (l.drop m).take n := by
suffices take (m + n) (take m l ++ drop m l) = take m l ++ take n (drop m l) by
rw [take_append_drop] at this
assumption
rw [take_append_eq_append_take, take_of_length_le, append_right_inj]
· simp only [take_eq_take, length_take, length_drop]
omega
apply Nat.le_trans (m := m)
· apply length_take_le
· apply Nat.le_add_right
theorem dropLast_take {n : Nat} {l : List α} (h : n < l.length) :
(l.take n).dropLast = l.take (n - 1) := by
simp only [dropLast_eq_take, length_take, Nat.le_of_lt h, Nat.min_eq_left, take_take, sub_le]
theorem map_eq_append_split {f : α β} {l : List α} {s₁ s₂ : List β}
(h : map f l = s₁ ++ s₂) : l₁ l₂, l = l₁ ++ l₂ map f l₁ = s₁ map f l₂ = s₂ := by
have := h
rw [ take_append_drop (length s₁) l] at this
rw [map_append] at this
refine _, _, rfl, append_inj this ?_
rw [length_map, length_take, Nat.min_eq_left]
rw [ length_map l f, h, length_append]
apply Nat.le_add_right
/-! ### drop -/
theorem lt_length_drop (L : List α) {i j : Nat} (h : i + j < L.length) : j < (L.drop i).length := by
have A : i < L.length := Nat.lt_of_le_of_lt (Nat.le.intro rfl) h
rw [(take_append_drop i L).symm] at h
simpa only [Nat.le_of_lt A, Nat.min_eq_left, Nat.add_lt_add_iff_left, length_take,
length_append] using h
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
theorem getElem_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
L[i + j] = (L.drop i)[j]'(lt_length_drop L h) := by
have : i L.length := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_lt h)
rw [getElem_of_eq (take_append_drop i L).symm h, getElem_append_right'] <;>
simp [Nat.min_eq_left this, Nat.add_sub_cancel_left, Nat.le_add_right]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
@[deprecated getElem_drop (since := "2024-06-12")]
theorem get_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
get L i + j, h = get (L.drop i) j, lt_length_drop L h := by
simp [getElem_drop]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
theorem getElem_drop' (L : List α) {i : Nat} {j : Nat} {h : j < (L.drop i).length} :
(L.drop i)[j] = L[i + j]'(by
rw [Nat.add_comm]
exact Nat.add_lt_of_lt_sub (length_drop i L h)) := by
rw [getElem_drop]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
@[deprecated getElem_drop' (since := "2024-06-12")]
theorem get_drop' (L : List α) {i j} :
get (L.drop i) j = get L i + j, by
rw [Nat.add_comm]
exact Nat.add_lt_of_lt_sub (length_drop i L j.2) := by
simp [getElem_drop']
@[simp]
theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? := by
ext
simp only [getElem?_eq_some, getElem_drop', Option.mem_def]
constructor <;> intro h, ha
· exact _, ha
· refine ?_, ha
rw [length_drop]
rw [Nat.add_comm] at h
apply Nat.lt_sub_of_add_lt h
@[deprecated getElem?_drop (since := "2024-06-12")]
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
simp
theorem head?_drop (l : List α) (n : Nat) :
(l.drop n).head? = l[n]? := by
rw [head?_eq_getElem?, getElem?_drop, Nat.add_zero]
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
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]
rw [length_drop]
split
· rw [getElem?_eq_none (by omega)]
· rw [getLast?_eq_getElem?]
congr
omega
theorem getLast_drop {l : List α} (h : l.drop n []) :
(l.drop n).getLast h = l.getLast (ne_nil_of_length_pos (by simp at h; omega)) := by
simp only [ne_eq, drop_eq_nil_iff_le] at h
apply Option.some_inj.1
simp only [ getLast?_eq_getLast, getLast?_drop, ite_eq_right_iff]
omega
theorem drop_length_cons {l : List α} (h : l []) (a : α) :
(a :: l).drop l.length = [l.getLast h] := by
induction l generalizing a with
| nil =>
cases h rfl
| cons y l ih =>
simp only [drop, length]
by_cases h₁ : l = []
· simp [h₁]
rw [getLast_cons h₁]
exact ih h₁ y
/-- Dropping the elements up to `n` in `l₁ ++ l₂` is the same as dropping the elements up to `n`
in `l₁`, dropping the elements up to `n - l₁.length` in `l₂`, and appending them. -/
theorem drop_append_eq_append_drop {l₁ l₂ : List α} {n : Nat} :
drop n (l₁ ++ l₂) = drop n l₁ ++ drop (n - l₁.length) l₂ := by
induction l₁ generalizing n
· simp
· cases n
· simp [*]
· simp only [cons_append, drop_succ_cons, length_cons, succ_eq_add_one, append_cancel_left_eq, *]
congr 1
omega
theorem drop_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n l₁.length) :
(l₁ ++ l₂).drop n = l₁.drop n ++ l₂ := by
simp [drop_append_eq_append_drop, Nat.sub_eq_zero_of_le h]
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
up to `i` in `l₂`. -/
@[simp]
theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
split <;> rename_i h
· ext1 m
by_cases h' : m < n
· rw [getElem?_append (by simp [length_take]; omega), getElem?_set_ne (by omega),
getElem?_take h']
· by_cases h'' : m = n
· subst h''
rw [getElem?_set_eq _, getElem?_append_right, length_take,
Nat.min_eq_left (by omega), Nat.sub_self, getElem?_cons_zero]
rw [length_take]
exact Nat.min_le_left m l.length
· have h''' : n < m := by omega
rw [getElem?_set_ne (by omega), getElem?_append_right, length_take,
Nat.min_eq_left (by omega)]
· obtain k, rfl := Nat.exists_eq_add_of_lt h'''
have p : n + k + 1 - n = k + 1 := by omega
rw [p]
rw [getElem?_cons_succ, getElem?_drop]
congr 1
omega
· rw [length_take]
exact Nat.le_trans (Nat.min_le_left _ _) (by omega)
· rw [set_eq_of_length_le]
omega
theorem exists_of_set {n : Nat} {a' : α} {l : List α} (h : n < l.length) :
l₁ l₂, l = l₁ ++ l[n] :: l₂ l₁.length = n l.set n a' = l₁ ++ a' :: l₂ := by
refine l.take n, l.drop (n + 1), by simp, length_take_of_le (Nat.le_of_lt h), ?_
simp [set_eq_take_append_cons_drop, h]
theorem drop_set_of_lt (a : α) {n m : Nat} (l : List α)
(hnm : n < m) : drop m (l.set n a) = l.drop m :=
ext_getElem? fun k => by simpa only [getElem?_drop] using getElem?_set_ne (by omega)
theorem drop_take : (m n : Nat) (l : List α), drop n (take m l) = take (m - n) (drop n l)
| 0, _, _ => by simp
| _, 0, _ => by simp
| _, _, [] => by simp
| m+1, n+1, h :: t => by
simp [take_succ_cons, drop_succ_cons, drop_take m n t]
congr 1
omega
theorem take_reverse {α} {xs : List α} {n : Nat} (h : n xs.length) :
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
@[deprecated (since := "2024-06-15")] abbrev reverse_take := @take_reverse
theorem drop_reverse {α} {xs : List α} {n : Nat} (h : n xs.length) :
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 *
congr
omega
· simp only [length_reverse, sub_le]
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_replicate (n) (a : α) : rotateLeft (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (n + 1) % m + min ((n + 1) % m) m = m by
simpa [rotateLeft]
intro h
rw [Nat.min_eq_left (Nat.le_of_lt (Nat.mod_lt _ (by omega)))]
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
omega
/-! ### rotateRight -/
@[simp] theorem rotateRight_replicate (n) (a : α) : rotateRight (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (m - (n + 1) % m) + min (m - (n + 1) % m) m = m by
simpa [rotateRight]
intro h
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
rw [Nat.min_eq_left (by omega)]
omega
/-! ### zipWith -/
@[simp] theorem length_zipWith (f : α β γ) (l₁ l₂) :
length (zipWith f l₁ l₂) = min (length l₁) (length l₂) := by
induction l₁ generalizing l₂ <;> cases l₂ <;>
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
theorem lt_length_left_of_zipWith {f : α β γ} {i : Nat} {l : List α} {l' : List β}
(h : i < (zipWith f l l').length) : i < l.length := by rw [length_zipWith] at h; omega
theorem lt_length_right_of_zipWith {f : α β γ} {i : Nat} {l : List α} {l' : List β}
(h : i < (zipWith f l l').length) : i < l'.length := by rw [length_zipWith] at h; omega
@[simp]
theorem getElem_zipWith {f : α β γ} {l : List α} {l' : List β}
{i : Nat} {h : i < (zipWith f l l').length} :
(zipWith f l l')[i] =
f (l[i]'(lt_length_left_of_zipWith h))
(l'[i]'(lt_length_right_of_zipWith h)) := by
rw [ Option.some_inj, getElem?_eq_getElem, getElem?_zipWith_eq_some]
exact
l[i]'(lt_length_left_of_zipWith h), l'[i]'(lt_length_right_of_zipWith h),
by rw [getElem?_eq_getElem], by rw [getElem?_eq_getElem]; exact rfl, rfl
theorem zipWith_eq_zipWith_take_min : (l₁ : List α) (l₂ : List β),
zipWith f l₁ l₂ = zipWith f (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zipWith_eq_zipWith_take_min l₁ l₂]
theorem reverse_zipWith (h : l.length = l'.length) :
(zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
induction l generalizing l' with
| nil => simp
| cons hd tl hl =>
cases l' with
| nil => simp
| cons hd' tl' =>
simp only [Nat.add_right_cancel_iff, length] at h
have : tl.reverse.length = tl'.reverse.length := by simp [h]
simp [hl h, zipWith_append _ _ _ _ _ this]
@[deprecated reverse_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_reverse := @reverse_zipWith
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
rw [zipWith_eq_zipWith_take_min]
simp
/-! ### zip -/
@[simp] theorem length_zip (l₁ : List α) (l₂ : List β) :
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
simp [zip]
theorem lt_length_left_of_zip {i : Nat} {l : List α} {l' : List β} (h : i < (zip l l').length) :
i < l.length :=
lt_length_left_of_zipWith h
theorem lt_length_right_of_zip {i : Nat} {l : List α} {l' : List β} (h : i < (zip l l').length) :
i < l'.length :=
lt_length_right_of_zipWith h
@[simp]
theorem getElem_zip {l : List α} {l' : List β} {i : Nat} {h : i < (zip l l').length} :
(zip l l')[i] =
(l[i]'(lt_length_left_of_zip h), l'[i]'(lt_length_right_of_zip h)) :=
getElem_zipWith (h := h)
theorem zip_eq_zip_take_min : (l₁ : List α) (l₂ : List β),
zip l₁ l₂ = zip (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zip_eq_zip_take_min l₁ l₂]
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
rw [zip_eq_zip_take_min]
simp
end List

View File

@@ -0,0 +1,269 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.Sublist
/-!
# Lemmas about `List.Pairwise` and `List.Nodup`.
-/
namespace List
open Nat
/-! ## Pairwise and Nodup -/
/-! ### Pairwise -/
theorem Pairwise.sublist : l₁ <+ l₂ l₂.Pairwise R l₁.Pairwise R
| .slnil, h => h
| .cons _ s, .cons _ h₂ => h₂.sublist s
| .cons₂ _ s, .cons h₁ h₂ => (h₂.sublist s).cons fun _ h => h₁ _ (s.subset h)
theorem Pairwise.imp {α R S} (H : {a b}, R a b S a b) :
{l : List α}, l.Pairwise R l.Pairwise S
| _, .nil => .nil
| _, .cons h₁ h₂ => .cons (H h₁ ·) (h₂.imp H)
theorem rel_of_pairwise_cons (p : (a :: l).Pairwise R) : {a'}, a' l R a a' :=
(pairwise_cons.1 p).1 _
theorem Pairwise.of_cons (p : (a :: l).Pairwise R) : Pairwise R l :=
(pairwise_cons.1 p).2
theorem Pairwise.tail : {l : List α} (_p : Pairwise R l), Pairwise R l.tail
| [], h => h
| _ :: _, h => h.of_cons
theorem Pairwise.imp_of_mem {S : α α Prop}
(H : {a b}, a l b l R a b S a b) (p : Pairwise R l) : Pairwise S l := by
induction p with
| nil => constructor
| @cons a l r _ ih =>
constructor
· exact fun x h => H (mem_cons_self ..) (mem_cons_of_mem _ h) <| r x h
· exact ih fun m m' => H (mem_cons_of_mem _ m) (mem_cons_of_mem _ m')
theorem Pairwise.and (hR : Pairwise R l) (hS : Pairwise S l) :
l.Pairwise fun a b => R a b S a b := by
induction hR with
| nil => simp only [Pairwise.nil]
| cons R1 _ IH =>
simp only [Pairwise.nil, pairwise_cons] at hS
exact fun b bl => R1 b bl, hS.1 b bl, IH hS.2
theorem pairwise_and_iff : l.Pairwise (fun a b => R a b S a b) Pairwise R l Pairwise S l :=
fun h => h.imp fun h => h.1, h.imp fun h => h.2, fun hR, hS => hR.and hS
theorem Pairwise.imp₂ (H : a b, R a b S a b T a b)
(hR : Pairwise R l) (hS : l.Pairwise S) : l.Pairwise T :=
(hR.and hS).imp fun h₁, h₂ => H _ _ h₁ h₂
theorem Pairwise.iff_of_mem {S : α α Prop} {l : List α}
(H : {a b}, a l b l (R a b S a b)) : Pairwise R l Pairwise S l :=
Pairwise.imp_of_mem fun m m' => (H m m').1, Pairwise.imp_of_mem fun m m' => (H m m').2
theorem Pairwise.iff {S : α α Prop} (H : a b, R a b S a b) {l : List α} :
Pairwise R l Pairwise S l :=
Pairwise.iff_of_mem fun _ _ => H ..
theorem pairwise_of_forall {l : List α} (H : x y, R x y) : Pairwise R l := by
induction l <;> simp [*]
theorem Pairwise.and_mem {l : List α} :
Pairwise R l Pairwise (fun x y => x l y l R x y) l :=
Pairwise.iff_of_mem <| by simp (config := { contextual := true })
theorem Pairwise.imp_mem {l : List α} :
Pairwise R l Pairwise (fun x y => x l y l R x y) l :=
Pairwise.iff_of_mem <| by simp (config := { contextual := true })
theorem Pairwise.forall_of_forall_of_flip (h₁ : x l, R x x) (h₂ : Pairwise R l)
(h₃ : l.Pairwise (flip R)) : x, x l y, y l R x y := by
induction l with
| nil => exact forall_mem_nil _
| cons a l ih =>
rw [pairwise_cons] at h₂ h₃
simp only [mem_cons]
rintro x (rfl | hx) y (rfl | hy)
· exact h₁ _ (l.mem_cons_self _)
· exact h₂.1 _ hy
· exact h₃.1 _ hx
· exact ih (fun x hx => h₁ _ <| mem_cons_of_mem _ hx) h₂.2 h₃.2 hx hy
theorem pairwise_singleton (R) (a : α) : Pairwise R [a] := by simp
theorem pairwise_pair {a b : α} : Pairwise R [a, b] R a b := by simp
theorem pairwise_map {l : List α} :
(l.map f).Pairwise R l.Pairwise fun a b => R (f a) (f b) := by
induction l
· simp
· simp only [map, pairwise_cons, forall_mem_map, *]
theorem Pairwise.of_map {S : β β Prop} (f : α β) (H : a b : α, S (f a) (f b) R a b)
(p : Pairwise S (map f l)) : Pairwise R l :=
(pairwise_map.1 p).imp (H _ _)
theorem Pairwise.map {S : β β Prop} (f : α β) (H : a b : α, R a b S (f a) (f b))
(p : Pairwise R l) : Pairwise S (map f l) :=
pairwise_map.2 <| p.imp (H _ _)
theorem pairwise_filterMap (f : β Option α) {l : List β} :
Pairwise R (filterMap f l) Pairwise (fun a a' : β => b f a, b' f a', R b b') l := by
let _S (a a' : β) := b f a, b' f a', R b b'
simp only [Option.mem_def]
induction l with
| nil => simp only [filterMap, Pairwise.nil]
| cons a l IH => ?_
match e : f a with
| none =>
rw [filterMap_cons_none e, pairwise_cons]
simp only [e, false_implies, implies_true, true_and, IH]
| some b =>
rw [filterMap_cons_some e]
simpa [IH, e] using fun _ =>
fun h a ha b hab => h _ _ ha hab, fun h a b ha hab => h _ ha _ hab
theorem Pairwise.filterMap {S : β β Prop} (f : α Option β)
(H : a a' : α, R a a' b f a, b' f a', S b b') {l : List α} (p : Pairwise R l) :
Pairwise S (filterMap f l) :=
(pairwise_filterMap _).2 <| p.imp (H _ _)
@[deprecated Pairwise.filterMap (since := "2024-07-29")] abbrev Pairwise.filter_map := @Pairwise.filterMap
theorem pairwise_filter (p : α Prop) [DecidablePred p] {l : List α} :
Pairwise R (filter p l) Pairwise (fun x y => p x p y R x y) l := by
rw [ filterMap_eq_filter, pairwise_filterMap]
simp
theorem Pairwise.filter (p : α Bool) : Pairwise R l Pairwise R (filter p l) :=
Pairwise.sublist (filter_sublist _)
theorem pairwise_append {l₁ l₂ : List α} :
(l₁ ++ l₂).Pairwise R l₁.Pairwise R l₂.Pairwise R a l₁, b l₂, R a b := by
induction l₁ <;> simp [*, or_imp, forall_and, and_assoc, and_left_comm]
theorem pairwise_append_comm {R : α α Prop} (s : {x y}, R x y R y x) {l₁ l₂ : List α} :
Pairwise R (l₁ ++ l₂) Pairwise R (l₂ ++ l₁) := by
have (l₁ l₂ : List α) (H : x : α, x l₁ y : α, y l₂ R x y)
(x : α) (xm : x l₂) (y : α) (ym : y l₁) : R x y := s (H y ym x xm)
simp only [pairwise_append, and_left_comm]; rw [Iff.intro (this l₁ l₂) (this l₂ l₁)]
theorem pairwise_middle {R : α α Prop} (s : {x y}, R x y R y x) {a : α} {l₁ l₂ : List α} :
Pairwise R (l₁ ++ a :: l₂) Pairwise R (a :: (l₁ ++ l₂)) := by
show Pairwise R (l₁ ++ ([a] ++ l₂)) Pairwise R ([a] ++ l₁ ++ l₂)
rw [ append_assoc, pairwise_append, @pairwise_append _ _ ([a] ++ l₁), pairwise_append_comm s]
simp only [mem_append, or_comm]
theorem pairwise_join {L : List (List α)} :
Pairwise R (join L)
( l L, Pairwise R l) Pairwise (fun l₁ l₂ => x l₁, y l₂, R x y) L := by
induction L with
| nil => simp
| cons l L IH =>
simp only [join, pairwise_append, IH, mem_join, exists_imp, and_imp, forall_mem_cons,
pairwise_cons, and_assoc, and_congr_right_iff]
rw [and_comm, and_congr_left_iff]
intros; exact fun h a b c d e => h c d e a b, fun h c d e a b => h a b c d e
theorem pairwise_bind {R : β β Prop} {l : List α} {f : α List β} :
List.Pairwise R (l.bind f)
( a l, Pairwise R (f a)) Pairwise (fun a₁ a₂ => x f a₁, y f a₂, R x y) l := by
simp [List.bind, pairwise_join, pairwise_map]
theorem pairwise_reverse {l : List α} :
l.reverse.Pairwise R l.Pairwise (fun a b => R b a) := by
induction l <;> simp [*, pairwise_append, and_comm]
@[simp] theorem pairwise_replicate {n : Nat} {a : α} :
(replicate n a).Pairwise R n 1 R a a := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, pairwise_cons, mem_replicate, ne_eq, and_imp,
forall_eq_apply_imp_iff, ih]
constructor
· rintro h, h' | h'
· by_cases w : n = 0
· left
subst w
simp
· right
exact h w
· right
exact h'
· rintro (h | h)
· obtain rfl := eq_zero_of_le_zero (le_of_lt_succ h)
simp
· exact fun _ => h, Or.inr h
theorem Pairwise.drop {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.drop n) :=
h.sublist (drop_sublist _ _)
theorem Pairwise.take {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.take n) :=
h.sublist (take_sublist _ _)
theorem pairwise_iff_forall_sublist : l.Pairwise R ( {a b}, [a,b] <+ l R a b) := by
induction l with
| nil => simp
| cons hd tl IH =>
rw [List.pairwise_cons]
constructor <;> intro h
· intro
| a, b, .cons _ hab => exact IH.mp h.2 hab
| _, b, .cons₂ _ hab => refine h.1 _ (hab.subset ?_); simp
· constructor
· intro x hx
apply h
rw [List.cons_sublist_cons, List.singleton_sublist]
exact hx
· apply IH.mpr
intro a b hab
apply h; exact hab.cons _
/-! ### Nodup -/
@[simp]
theorem nodup_nil : @Nodup α [] :=
Pairwise.nil
@[simp]
theorem nodup_cons {a : α} {l : List α} : Nodup (a :: l) a l Nodup l := by
simp only [Nodup, pairwise_cons, forall_mem_ne]
theorem Nodup.sublist : l₁ <+ l₂ Nodup l₂ Nodup l₁ :=
Pairwise.sublist
theorem Sublist.nodup : l₁ <+ l₂ Nodup l₂ Nodup l₁ :=
Nodup.sublist
theorem getElem?_inj {xs : List α}
(h₀ : i < xs.length) (h₁ : Nodup xs) (h₂ : xs[i]? = xs[j]?) : i = j := by
induction xs generalizing i j with
| nil => cases h₀
| cons x xs ih =>
match i, j with
| 0, 0 => rfl
| i+1, j+1 =>
cases h₁ with
| cons ha h₁ =>
simp only [getElem?_cons_succ] at h₂
exact congrArg (· + 1) (ih (Nat.lt_of_succ_lt_succ h₀) h₁ h₂)
| i+1, 0 => ?_
| 0, j+1 => ?_
all_goals
simp only [get?_eq_getElem?, getElem?_cons_zero, getElem?_cons_succ] at h₂
cases h₁; rename_i h' h
have := h x ?_ rfl; cases this
rw [mem_iff_get?]
simp only [get?_eq_getElem?]
exact _, h₂; exact _ , h₂.symm
@[simp] theorem nodup_replicate {n : Nat} {a : α} :
(replicate n a).Nodup n 1 := by simp [Nodup]
end List

View File

@@ -0,0 +1,754 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.TakeDrop
/-!
# Lemmas about `List.Subset`, `List.Sublist`, `List.IsPrefix`, `List.IsSuffix`, and `List.IsInfix`.
-/
namespace List
open Nat
/-! ### isPrefixOf -/
section isPrefixOf
variable [BEq α]
@[simp] theorem isPrefixOf_cons₂_self [LawfulBEq α] {a : α} :
isPrefixOf (a::as) (a::bs) = isPrefixOf as bs := by simp [isPrefixOf_cons₂]
@[simp] theorem isPrefixOf_length_pos_nil {L : List α} (h : 0 < L.length) : isPrefixOf L [] = false := by
cases L <;> simp_all [isPrefixOf]
@[simp] theorem isPrefixOf_replicate {a : α} :
isPrefixOf l (replicate n a) = (decide (l.length n) && l.all (· == a)) := by
induction l generalizing n with
| nil => simp
| cons h t ih =>
cases n
· simp
· simp [replicate_succ, isPrefixOf_cons₂, ih, Nat.succ_le_succ_iff, Bool.and_left_comm]
end isPrefixOf
/-! ### isSuffixOf -/
section isSuffixOf
variable [BEq α]
@[simp] theorem isSuffixOf_cons_nil : isSuffixOf (a::as) ([] : List α) = false := by
simp [isSuffixOf]
@[simp] theorem isSuffixOf_replicate {a : α} :
isSuffixOf l (replicate n a) = (decide (l.length n) && l.all (· == a)) := by
simp [isSuffixOf, all_eq]
end isSuffixOf
/-! ### Subset -/
/-! ### List subset -/
theorem subset_def {l₁ l₂ : List α} : l₁ l₂ {a : α}, a l₁ a l₂ := .rfl
@[simp] theorem nil_subset (l : List α) : [] l := nofun
@[simp] theorem Subset.refl (l : List α) : l l := fun _ i => i
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 (Subset : List α List α Prop) Subset Subset :=
Subset.trans
@[simp] 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₂ :=
fun s _ i => .tail _ (s i)
theorem cons_subset_cons {l₁ l₂ : List α} (a : α) (s : l₁ l₂) : a :: l₁ a :: l₂ :=
fun _ => by simp only [mem_cons]; exact Or.imp_right (@s _)
@[simp] theorem cons_subset : a :: l m a m l m := by
simp only [subset_def, mem_cons, or_imp, forall_and, forall_eq]
@[simp] theorem subset_nil {l : List α} : l [] l = [] :=
fun h => match l with | [] => rfl | _::_ => (nomatch h (.head ..)), fun | rfl => Subset.refl _
theorem map_subset {l₁ l₂ : List α} {f : α β} (h : l₁ l₂) : map f l₁ map f l₂ :=
fun x => by simp only [mem_map]; exact .imp fun a => .imp_left (@h _)
theorem filter_subset {l₁ l₂ : List α} (p : α Bool) (H : l₁ l₂) : filter p l₁ filter p l₂ :=
fun x => by simp_all [mem_filter, subset_def.1 H]
theorem filterMap_subset {l₁ l₂ : List α} (f : α Option β) (H : l₁ l₂) :
filterMap f l₁ filterMap f l₂ := by
intro x
simp only [mem_filterMap]
rintro a, h, w
exact a, H h, w
@[simp] 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_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₂ :=
fun s => Subset.trans s <| subset_append_right _ _
@[simp] theorem append_subset {l₁ l₂ l : List α} :
l₁ ++ l₂ l l₁ l l₂ l := by simp [subset_def, or_imp, forall_and]
theorem replicate_subset {n : Nat} {a : α} {l : List α} : replicate n a l n = 0 a l := by
induction n with
| zero => simp
| succ n ih => simp (config := {contextual := true}) [replicate_succ, ih, cons_subset]
theorem subset_replicate {n : Nat} {a : α} {l : List α} (h : n 0) : l replicate n a x l, x = a := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [cons_subset, mem_replicate, ne_eq, ih, mem_cons, forall_eq_or_imp,
and_congr_left_iff, and_iff_right_iff_imp]
solve_by_elim
@[simp] theorem reverse_subset {l₁ l₂ : List α} : reverse l₁ l₂ l₁ l₂ := by
simp [subset_def]
@[simp] theorem subset_reverse {l₁ l₂ : List α} : l₁ reverse l₂ l₁ l₂ := by
simp [subset_def]
/-! ### Sublist and isSublist -/
@[simp] theorem nil_sublist : l : List α, [] <+ l
| [] => .slnil
| a :: l => (nil_sublist l).cons a
@[simp] theorem Sublist.refl : l : List α, l <+ l
| [] => .slnil
| a :: l => (Sublist.refl l).cons₂ a
theorem Sublist.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ <+ l₂) (h₂ : l₂ <+ l₃) : l₁ <+ l₃ := by
induction h₂ generalizing l₁ with
| slnil => exact h₁
| cons _ _ IH => exact (IH h₁).cons _
| @cons₂ l₂ _ a _ IH =>
generalize e : a :: l₂ = l₂'
match e h₁ with
| .slnil => apply nil_sublist
| .cons a' h₁' => cases e; apply (IH h₁').cons
| .cons₂ a' h₁' => cases e; apply (IH h₁').cons₂
instance : Trans (@Sublist α) Sublist Sublist := Sublist.trans
@[simp] 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
@[simp]
theorem cons_sublist_cons : a :: l₁ <+ a :: l₂ l₁ <+ l₂ :=
fun | .cons _ s => sublist_of_cons_sublist s | .cons₂ _ s => s, .cons₂ _
theorem sublist_or_mem_of_sublist (h : l <+ l₁ ++ a :: l₂) : l <+ l₁ ++ l₂ a l := by
induction l₁ generalizing l with
| nil => match h with
| .cons _ h => exact .inl h
| .cons₂ _ h => exact .inr (.head ..)
| cons b l₁ IH =>
match h with
| .cons _ h => exact (IH h).imp_left (Sublist.cons _)
| .cons₂ _ h => exact (IH h).imp (Sublist.cons₂ _) (.tail _)
theorem Sublist.subset : l₁ <+ l₂ l₁ l₂
| .slnil, _, h => h
| .cons _ s, _, h => .tail _ (s.subset h)
| .cons₂ .., _, .head .. => .head ..
| .cons₂ _ s, _, .tail _ h => .tail _ (s.subset 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₁
theorem mem_of_cons_sublist {a : α} {l₁ l₂ : List α} (s : a :: l₁ <+ l₂) : a l₂ :=
(cons_subset.1 s.subset).1
@[simp] theorem sublist_nil {l : List α} : l <+ [] l = [] :=
fun s => subset_nil.1 s.subset, fun H => H Sublist.refl _
theorem Sublist.length_le : l₁ <+ l₂ length l₁ length l₂
| .slnil => Nat.le_refl 0
| .cons _l s => le_succ_of_le (length_le s)
| .cons₂ _ s => succ_le_succ (length_le s)
theorem Sublist.eq_of_length : l₁ <+ l₂ length l₁ = length l₂ l₁ = l₂
| .slnil, _ => rfl
| .cons a s, h => nomatch Nat.not_lt.2 s.length_le (h lt_succ_self _)
| .cons₂ a s, h => by rw [s.eq_of_length (succ.inj h)]
theorem Sublist.eq_of_length_le (s : l₁ <+ l₂) (h : length l₂ length l₁) : l₁ = l₂ :=
s.eq_of_length <| Nat.le_antisymm s.length_le h
theorem Sublist.length_eq (s : l₁ <+ l₂) : length l₁ = length l₂ l₁ = l₂ :=
s.eq_of_length, congrArg _
protected theorem Sublist.map (f : α β) {l₁ l₂} (s : l₁ <+ l₂) : map f l₁ <+ map f l₂ := by
induction s with
| slnil => simp
| cons a s ih =>
simpa using cons (f a) ih
| cons₂ a s ih =>
simpa using cons₂ (f a) ih
protected theorem Sublist.filterMap (f : α Option β) (s : l₁ <+ l₂) :
filterMap f l₁ <+ filterMap f l₂ := by
induction s <;> simp [filterMap_cons] <;> split <;> simp [*, cons, cons₂]
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 sublist_filterMap_iff {l₁ : List β} {f : α Option β} :
l₁ <+ l₂.filterMap f l', l' <+ l₂ l₁ = l'.filterMap f := by
induction l₂ generalizing l₁ with
| nil => simp
| cons a l₂ ih =>
simp only [filterMap_cons]
split
· simp only [ih]
constructor
· rintro l', h, rfl
exact l', Sublist.cons a h, rfl
· rintro l', h, rfl
cases h with
| cons _ h =>
exact l', h, rfl
| cons₂ _ h =>
rename_i l'
exact l', h, by simp_all
· constructor
· intro w
cases w with
| cons _ h =>
obtain l', s, rfl := ih.1 h
exact l', Sublist.cons a s, rfl
| cons₂ _ h =>
rename_i l'
obtain l', s, rfl := ih.1 h
refine a :: l', Sublist.cons₂ a s, ?_
rwa [filterMap_cons_some]
· rintro l', h, rfl
replace h := h.filterMap f
rwa [filterMap_cons_some] at h
assumption
theorem sublist_map_iff {l₁ : List β} {f : α β} :
l₁ <+ l₂.map f l', l' <+ l₂ l₁ = l'.map f := by
simp only [ filterMap_eq_map, sublist_filterMap_iff]
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₂
| [], _ => nil_sublist _
| _ :: l₁, l₂ => (sublist_append_left l₁ l₂).cons₂ _
@[simp] theorem sublist_append_right : l₁ l₂ : List α, l₂ <+ l₁ ++ l₂
| [], _ => Sublist.refl _
| _ :: l₁, l₂ => (sublist_append_right l₁ l₂).cons _
@[simp] theorem singleton_sublist {a : α} {l} : [a] <+ l a l := by
refine fun h => h.subset (mem_singleton_self _), fun h => ?_
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₂ :=
s.trans <| sublist_append_left ..
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₂
| [] => Iff.rfl
| _ :: l => cons_sublist_cons.trans (append_sublist_append_left l)
theorem Sublist.append_left : l₁ <+ l₂ l, l ++ l₁ <+ l ++ l₂ :=
fun h l => (append_sublist_append_left l).mpr h
theorem Sublist.append_right : l₁ <+ l₂ l, l₁ ++ l <+ l₂ ++ l
| .slnil, _ => Sublist.refl _
| .cons _ h, _ => (h.append_right _).cons _
| .cons₂ _ h, _ => (h.append_right _).cons₂ _
theorem Sublist.append (hl : l₁ <+ l₂) (hr : r₁ <+ r₂) : l₁ ++ r₁ <+ l₂ ++ r₂ :=
(hl.append_right _).trans ((append_sublist_append_left _).2 hr)
theorem sublist_cons_iff {a : α} {l l'} :
l <+ a :: l' l <+ l' r, l = a :: r r <+ l' := by
constructor
· intro h
cases h with
| cons _ h => exact Or.inl h
| cons₂ _ h => exact Or.inr _, rfl, h
· rintro (h | r, rfl, h)
· exact h.cons _
· exact h.cons₂ _
theorem cons_sublist_iff {a : α} {l l'} :
a :: l <+ l' r₁ r₂, l' = r₁ ++ r₂ a r₁ l <+ r₂ := by
induction l' with
| nil => simp
| cons a' l' ih =>
constructor
· intro w
cases w with
| cons _ w =>
obtain r₁, r₂, rfl, h₁, h₂ := ih.1 w
exact a' :: r₁, r₂, by simp, mem_cons_of_mem a' h₁, h₂
| cons₂ _ w =>
exact [a], l', by simp, mem_singleton_self _, w
· rintro r₁, r₂, w, h₁, h₂
rw [w, singleton_append]
exact Sublist.append (by simpa) h₂
theorem sublist_append_iff {l : List α} :
l <+ r₁ ++ r₂ l₁ l₂, l = l₁ ++ l₂ l₁ <+ r₁ l₂ <+ r₂ := by
induction r₁ generalizing l with
| nil =>
constructor
· intro w
refine [], l, by simp_all
· rintro l₁, l₂, rfl, w₁, w₂
simp_all
| cons r r₁ ih =>
constructor
· intro w
simp only [cons_append] at w
cases w with
| cons _ w =>
obtain l₁, l₂, rfl, w₁, w₂ := ih.1 w
exact l₁, l₂, rfl, Sublist.cons r w₁, w₂
| cons₂ _ w =>
rename_i l
obtain l₁, l₂, rfl, w₁, w₂ := ih.1 w
refine r :: l₁, l₂, by simp, cons_sublist_cons.mpr w₁, w₂
· rintro l₁, l₂, rfl, w₁, w₂
cases w₁ with
| cons _ w₁ =>
exact Sublist.cons _ (Sublist.append w₁ w₂)
| cons₂ _ w₁ =>
rename_i l
exact Sublist.cons₂ _ (Sublist.append w₁ w₂)
theorem append_sublist_iff {l₁ l₂ : List α} :
l₁ ++ l₂ <+ r r₁ r₂, r = r₁ ++ r₂ l₁ <+ r₁ l₂ <+ r₂ := by
induction l₁ generalizing r with
| nil =>
constructor
· intro w
refine [], r, by simp_all
· rintro r₁, r₂, rfl, -, w₂
simp only [nil_append]
exact sublist_append_of_sublist_right w₂
| cons a l₁ ih =>
constructor
· rw [cons_append, cons_sublist_iff]
rintro r₁, r₂, rfl, h₁, h₂
obtain s₁, s₂, rfl, t₁, t₂ := ih.1 h₂
refine r₁ ++ s₁, s₂, by simp, ?_, t₂
rw [ singleton_append]
exact Sublist.append (by simpa) t₁
· rintro r₁, r₂, rfl, h₁, h₂
exact Sublist.append h₁ h₂
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
| .cons₂ _ h => by rw [reverse_cons, reverse_cons]; exact h.reverse.append_right _
@[simp] theorem reverse_sublist : l₁.reverse <+ l₂.reverse l₁ <+ l₂ :=
fun h => l₁.reverse_reverse l₂.reverse_reverse h.reverse, Sublist.reverse
theorem sublist_reverse_iff : l₁ <+ l₂.reverse l₁.reverse <+ l₂ :=
by rw [ reverse_sublist, reverse_reverse]
@[simp] theorem append_sublist_append_right (l) : l₁ ++ l <+ l₂ ++ l l₁ <+ l₂ :=
fun h => by
have := h.reverse
simp only [reverse_append, append_sublist_append_left, reverse_sublist] at this
exact this,
fun h => h.append_right l
@[simp] theorem replicate_sublist_replicate {m n} (a : α) :
replicate m a <+ replicate n a m n := by
refine fun h => ?_, fun h => ?_
· have := h.length_le; simp only [length_replicate] at this ; exact this
· induction h with
| refl => apply Sublist.refl
| step => simp [*, replicate, Sublist.cons]
theorem sublist_replicate_iff : l <+ replicate m a n, n m l = replicate n a := by
induction l generalizing m with
| nil =>
simp only [nil_sublist, true_iff]
exact 0, zero_le m, by simp
| cons b l ih =>
constructor
· intro w
cases m with
| zero => simp at w
| succ m =>
simp [replicate_succ] at w
cases w with
| cons _ w =>
obtain n, le, rfl := ih.1 (sublist_of_cons_sublist w)
obtain rfl := (mem_replicate.1 (mem_of_cons_sublist w)).2
exact n+1, Nat.add_le_add_right le 1, rfl
| cons₂ _ w =>
obtain n, le, rfl := ih.1 w
refine n+1, Nat.add_le_add_right le 1, by simp [replicate_succ]
· rintro n, le, w
rw [w]
exact (replicate_sublist_replicate a).2 le
theorem sublist_join_of_mem {L : List (List α)} {l} (h : l L) : l <+ L.join := by
induction L with
| nil => cases h
| cons l' L ih =>
rcases mem_cons.1 h with (rfl | h)
· simp [h]
· simp [ih h, join_cons, sublist_append_of_sublist_right]
theorem sublist_join_iff {L : List (List α)} {l} :
l <+ L.join
L' : List (List α), l = L'.join i (_ : i < L'.length), L'[i] <+ L[i]?.getD [] := by
induction L generalizing l with
| nil =>
constructor
· intro w
simp only [join_nil, sublist_nil] at w
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 [getElem?_nil, Option.getD_none, sublist_nil] at h
exact (forall_getElem L' (· = [])).1 h
| cons l' L ih =>
simp only [join_cons, sublist_append_iff, ih]
constructor
· rintro l₁, l₂, rfl, s, L', rfl, h
refine l₁ :: L', by simp, ?_
intro i lt
cases i <;> simp_all
· rintro L', rfl, h
cases L' with
| nil =>
exact [], [], by simp, by simp, [], by simp, fun i x => by cases x
| cons l₁ L' =>
exact l₁, L'.join, by simp, by simpa using h 0 (by simp), L', rfl,
fun i lt => by simpa using h (i+1) (Nat.add_lt_add_right lt 1)
theorem join_sublist_iff {L : List (List α)} {l} :
L.join <+ l
L' : List (List α), l = L'.join i (_ : i < L.length), L[i] <+ L'[i]?.getD [] := by
induction L generalizing l with
| nil =>
constructor
· intro _
exact [l], by simp, fun i x => by cases x
· rintro L', rfl, _
simp only [join_nil, nil_sublist]
| cons l' L ih =>
simp only [join_cons, append_sublist_iff, ih]
constructor
· rintro l₁, l₂, rfl, s, L', rfl, h
refine l₁ :: L', by simp, ?_
intro i lt
cases i <;> simp_all
· rintro L', rfl, h
cases L' with
| nil =>
exact [], [], by simp, by simpa using h 0 (by simp), [], by simp,
fun i x => by simpa using h (i+1) (Nat.add_lt_add_right x 1)
| cons l₁ L' =>
exact l₁, L'.join, by simp, by simpa using h 0 (by simp), L', rfl,
fun i lt => by simpa using h (i+1) (Nat.add_lt_add_right lt 1)
@[simp] theorem isSublist_iff_sublist [BEq α] [LawfulBEq α] {l₁ l₂ : List α} :
l₁.isSublist l₂ l₁ <+ l₂ := by
cases l₁ <;> cases l₂ <;> simp [isSublist]
case cons.cons hd₁ tl₁ hd₂ tl₂ =>
if h_eq : hd₁ = hd₂ then
simp [h_eq, cons_sublist_cons, isSublist_iff_sublist]
else
simp only [beq_iff_eq, h_eq]
constructor
· intro h_sub
apply Sublist.cons
exact isSublist_iff_sublist.mp h_sub
· intro h_sub
cases h_sub
case cons h_sub =>
exact isSublist_iff_sublist.mpr h_sub
case cons₂ =>
contradiction
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <+ l₂) :=
decidable_of_iff (l₁.isSublist l₂) isSublist_iff_sublist
/-! ### IsPrefix / IsSuffix / IsInfix -/
@[simp] theorem prefix_append (l₁ l₂ : List α) : l₁ <+: l₁ ++ l₂ := l₂, rfl
@[simp] theorem suffix_append (l₁ l₂ : List α) : l₂ <:+ l₁ ++ l₂ := l₁, rfl
theorem infix_append (l₁ l₂ l₃ : List α) : l₂ <:+: l₁ ++ l₂ ++ l₃ := l₁, l₃, rfl
@[simp] theorem infix_append' (l₁ l₂ l₃ : List α) : l₂ <:+: l₁ ++ (l₂ ++ l₃) := by
rw [ List.append_assoc]; apply infix_append
theorem IsPrefix.isInfix : l₁ <+: l₂ l₁ <:+: l₂ := fun t, h => [], t, h
theorem IsSuffix.isInfix : l₁ <:+ l₂ l₁ <:+: l₂ := fun t, h => t, [], by rw [h, append_nil]
@[simp] theorem nil_prefix (l : List α) : [] <+: l := l, rfl
@[simp] theorem nil_suffix (l : List α) : [] <:+ l := l, append_nil _
@[simp] theorem nil_infix (l : List α) : [] <:+: l := (nil_prefix _).isInfix
@[simp] theorem prefix_refl (l : List α) : l <+: l := [], append_nil _
@[simp] theorem suffix_refl (l : List α) : l <:+ l := [], rfl
@[simp] theorem infix_refl (l : List α) : l <:+: l := (prefix_refl l).isInfix
@[simp] theorem suffix_cons (a : α) : l, l <:+ a :: l := suffix_append [a]
theorem infix_cons : l₁ <:+: l₂ l₁ <:+: a :: l₂ := fun L₁, L₂, h => a :: L₁, L₂, h rfl
theorem infix_concat : l₁ <:+: l₂ l₁ <:+: concat l₂ a := fun L₁, L₂, h =>
L₁, concat L₂ a, by simp [ h, concat_eq_append, append_assoc]
theorem IsPrefix.trans : {l₁ l₂ l₃ : List α}, l₁ <+: l₂ l₂ <+: l₃ l₁ <+: l₃
| _, _, _, r₁, rfl, r₂, rfl => r₁ ++ r₂, (append_assoc _ _ _).symm
theorem IsSuffix.trans : {l₁ l₂ l₃ : List α}, l₁ <:+ l₂ l₂ <:+ l₃ l₁ <:+ l₃
| _, _, _, l₁, rfl, l₂, rfl => l₂ ++ l₁, append_assoc _ _ _
theorem IsInfix.trans : {l₁ l₂ l₃ : List α}, l₁ <:+: l₂ l₂ <:+: l₃ l₁ <:+: l₃
| l, _, _, l₁, r₁, rfl, l₂, r₂, rfl => l₂ ++ l₁, r₁ ++ r₂, by simp only [append_assoc]
protected theorem IsInfix.sublist : l₁ <:+: l₂ l₁ <+ l₂
| _, _, h => h (sublist_append_right ..).trans (sublist_append_left ..)
protected theorem IsInfix.subset (hl : l₁ <:+: l₂) : l₁ l₂ :=
hl.sublist.subset
protected theorem IsPrefix.sublist (h : l₁ <+: l₂) : l₁ <+ l₂ :=
h.isInfix.sublist
protected theorem IsPrefix.subset (hl : l₁ <+: l₂) : l₁ l₂ :=
hl.sublist.subset
protected theorem IsSuffix.sublist (h : l₁ <:+ l₂) : l₁ <+ l₂ :=
h.isInfix.sublist
protected theorem IsSuffix.subset (hl : l₁ <:+ l₂) : l₁ l₂ :=
hl.sublist.subset
@[simp] theorem reverse_suffix : reverse l₁ <:+ reverse l₂ l₁ <+: l₂ :=
fun r, e => reverse r, by rw [ reverse_reverse l₁, reverse_append, e, reverse_reverse],
fun r, e => reverse r, by rw [ reverse_append, e]
@[simp] theorem reverse_prefix : reverse l₁ <+: reverse l₂ l₁ <:+ l₂ := by
rw [ reverse_suffix]; simp only [reverse_reverse]
@[simp] theorem reverse_infix : reverse l₁ <:+: reverse l₂ l₁ <:+: l₂ := by
refine fun s, t, e => reverse t, reverse s, ?_, fun s, t, e => reverse t, reverse s, ?_
· rw [ reverse_reverse l₁, append_assoc, reverse_append, reverse_append, e,
reverse_reverse]
· rw [append_assoc, reverse_append, reverse_append, e]
theorem IsInfix.length_le (h : l₁ <:+: l₂) : l₁.length l₂.length :=
h.sublist.length_le
theorem IsPrefix.length_le (h : l₁ <+: l₂) : l₁.length l₂.length :=
h.sublist.length_le
theorem IsSuffix.length_le (h : l₁ <:+ l₂) : l₁.length l₂.length :=
h.sublist.length_le
@[simp] theorem infix_nil : l <:+: [] l = [] := (sublist_nil.1 ·.sublist), (· infix_refl _)
@[simp] theorem prefix_nil : l <+: [] l = [] := (sublist_nil.1 ·.sublist), (· prefix_refl _)
@[simp] theorem suffix_nil : l <:+ [] l = [] := (sublist_nil.1 ·.sublist), (· suffix_refl _)
theorem infix_iff_prefix_suffix (l₁ l₂ : List α) : l₁ <:+: l₂ t, l₁ <+: t t <:+ l₂ :=
fun _, t, e => l₁ ++ t, _, rfl, e append_assoc .. _, rfl,
fun _, t, rfl, s, e => s, t, append_assoc .. e
theorem IsInfix.eq_of_length (h : l₁ <:+: l₂) : l₁.length = l₂.length l₁ = l₂ :=
h.sublist.eq_of_length
theorem IsPrefix.eq_of_length (h : l₁ <+: l₂) : l₁.length = l₂.length l₁ = l₂ :=
h.sublist.eq_of_length
theorem IsSuffix.eq_of_length (h : l₁ <:+ l₂) : l₁.length = l₂.length l₁ = l₂ :=
h.sublist.eq_of_length
theorem prefix_of_prefix_length_le :
{l₁ l₂ l₃ : List α}, l₁ <+: l₃ l₂ <+: l₃ length l₁ length l₂ l₁ <+: l₂
| [], l₂, _, _, _, _ => nil_prefix _
| a :: l₁, b :: l₂, _, r₁, rfl, r₂, e, ll => by
injection e with _ e'; subst b
rcases prefix_of_prefix_length_le _, rfl _, e' (le_of_succ_le_succ ll) with r₃, rfl
exact r₃, rfl
theorem prefix_or_prefix_of_prefix (h₁ : l₁ <+: l₃) (h₂ : l₂ <+: l₃) : l₁ <+: l₂ l₂ <+: l₁ :=
(Nat.le_total (length l₁) (length l₂)).imp (prefix_of_prefix_length_le h₁ h₂)
(prefix_of_prefix_length_le h₂ h₁)
theorem suffix_of_suffix_length_le
(h₁ : l₁ <:+ l₃) (h₂ : l₂ <:+ l₃) (ll : length l₁ length l₂) : l₁ <:+ l₂ :=
reverse_prefix.1 <|
prefix_of_prefix_length_le (reverse_prefix.2 h₁) (reverse_prefix.2 h₂) (by simp [ll])
theorem suffix_or_suffix_of_suffix (h₁ : l₁ <:+ l₃) (h₂ : l₂ <:+ l₃) : l₁ <:+ l₂ l₂ <:+ l₁ :=
(prefix_or_prefix_of_prefix (reverse_prefix.2 h₁) (reverse_prefix.2 h₂)).imp reverse_prefix.1
reverse_prefix.1
theorem prefix_cons_iff : l₁ <+: a :: l₂ l₁ = [] t, l₁ = a :: t t <+: l₂ := by
cases l₁ with
| nil => simp
| cons a' l₁ =>
constructor
· rintro t, h
simp at h
obtain rfl, rfl := h
exact Or.inr l₁, rfl, prefix_append l₁ t
· rintro (h | t, w, s, h')
· simp [h]
· simp only [w]
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]
constructor
· rintro t, rfl, rfl, h
exact rfl, h
· rintro rfl, h
exact l₁, rfl, rfl, h
theorem suffix_cons_iff : l₁ <:+ a :: l₂ l₁ = a :: l₂ l₁ <:+ l₂ := by
constructor
· rintro hd, tl, hl₃
· exact Or.inl hl₃
· simp only [cons_append] at hl₃
injection hl₃ with _ hl₄
exact Or.inr _, hl₄
· rintro (rfl | hl₁)
· exact (a :: l₂).suffix_refl
· exact hl₁.trans (l₂.suffix_cons _)
theorem infix_cons_iff : l₁ <:+: a :: l₂ l₁ <+: a :: l₂ l₁ <:+: l₂ := by
constructor
· rintro hd, tl, t, hl₃
· exact Or.inl t, hl₃
· simp only [cons_append] at hl₃
injection hl₃ with _ hl₄
exact Or.inr _, t, hl₄
· rintro (h | hl₁)
· exact h.isInfix
· exact infix_cons hl₁
theorem infix_of_mem_join : {L : List (List α)}, l L l <:+: join L
| l' :: _, h =>
match h with
| List.Mem.head .. => infix_append [] _ _
| List.Mem.tail _ hlMemL =>
IsInfix.trans (infix_of_mem_join hlMemL) <| (suffix_append _ _).isInfix
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]
theorem take_prefix (n) (l : List α) : take n l <+: l :=
_, take_append_drop _ _
theorem drop_suffix (n) (l : List α) : drop n l <:+ l :=
_, take_append_drop _ _
theorem take_sublist (n) (l : List α) : take n l <+ l :=
(take_prefix n l).sublist
theorem drop_sublist (n) (l : List α) : drop n l <+ l :=
(drop_suffix n l).sublist
theorem take_subset (n) (l : List α) : take n l l :=
(take_sublist n l).subset
theorem drop_subset (n) (l : List α) : drop n l l :=
(drop_sublist n l).subset
theorem mem_of_mem_take {l : List α} (h : a l.take n) : a l :=
take_subset n l h
theorem mem_of_mem_drop {n} {l : List α} (h : a l.drop n) : a l :=
drop_subset _ _ h
theorem IsPrefix.filter (p : α Bool) l₁ l₂ : List α (h : l₁ <+: l₂) :
l₁.filter p <+: l₂.filter p := by
obtain xs, rfl := h
rw [filter_append]; apply prefix_append
theorem IsSuffix.filter (p : α Bool) l₁ l₂ : List α (h : l₁ <:+ l₂) :
l₁.filter p <:+ l₂.filter p := by
obtain xs, rfl := h
rw [filter_append]; apply suffix_append
theorem IsInfix.filter (p : α Bool) l₁ l₂ : List α (h : l₁ <:+: l₂) :
l₁.filter p <:+: l₂.filter p := by
obtain xs, ys, rfl := h
rw [filter_append, filter_append]; apply infix_append _
@[simp] theorem isPrefixOf_iff_prefix [BEq α] [LawfulBEq α] {l₁ l₂ : List α} :
l₁.isPrefixOf l₂ l₁ <+: l₂ := by
induction l₁ generalizing l₂ with
| nil => simp
| cons a l₁ ih =>
cases l₂ with
| nil => simp
| cons a' l₂ => simp [isPrefixOf, ih]
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <+: l₂) :=
decidable_of_iff (l₁.isPrefixOf l₂) isPrefixOf_iff_prefix
@[simp] theorem isSuffixOf_iff_suffix [BEq α] [LawfulBEq α] {l₁ l₂ : List α} :
l₁.isSuffixOf l₂ l₁ <:+ l₂ := by
simp [isSuffixOf]
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <:+ l₂) :=
decidable_of_iff (l₁.isSuffixOf l₂) isSuffixOf_iff_suffix
end List

View File

@@ -5,383 +5,443 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
-/
prelude
import Init.Data.List.Lemmas
import Init.Data.Nat.Lemmas
/-!
# Further lemmas about `List.take`, `List.drop`, `List.zip` and `List.zipWith`.
These are in a separate file from most of the list lemmas
as they required importing more lemmas about natural numbers, and use `omega`.
# Lemmas about `List.zip`, `List.zipWith`, `List.zipWithAll`, and `List.unzip`.
-/
namespace List
open Nat
/-! ### take -/
/-! ### take and drop
@[simp] theorem length_take : (i : Nat) (l : List α), length (take i l) = min i (length l)
| 0, l => by simp [Nat.zero_min]
| succ n, [] => by simp [Nat.min_zero]
| succ n, _ :: l => by simp [Nat.succ_min_succ, length_take]
theorem length_take_le (n) (l : List α) : length (take n l) n := by simp [Nat.min_le_left]
theorem length_take_le' (n) (l : List α) : length (take n l) l.length :=
by simp [Nat.min_le_right]
theorem length_take_of_le (h : n length l) : length (take n l) = n := by simp [Nat.min_eq_left h]
theorem take_take : (n m) (l : List α), take n (take m l) = take (min n m) l
| n, 0, l => by rw [Nat.min_zero, take_zero, take_nil]
| 0, m, l => by rw [Nat.zero_min, take_zero, take_zero]
| succ n, succ m, nil => by simp only [take_nil]
| succ n, succ m, a :: l => by
simp only [take, succ_min_succ, take_take n m l]
@[simp] theorem take_replicate (a : α) : n m : Nat, take n (replicate m a) = replicate (min n m) a
| n, 0 => by simp [Nat.min_zero]
| 0, m => by simp [Nat.zero_min]
| succ n, succ m => by simp [replicate_succ, succ_min_succ, take_replicate]
@[simp] theorem drop_replicate (a : α) : n m : Nat, drop n (replicate m a) = replicate (m - n) a
| n, 0 => by simp
| 0, m => by simp
| succ n, succ m => by simp [replicate_succ, succ_sub_succ, drop_replicate]
/-- Taking the first `n` elements in `l₁ ++ l₂` is the same as appending the first `n` elements
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
theorem take_append_eq_append_take {l₁ l₂ : List α} {n : Nat} :
take n (l₁ ++ l₂) = take n l₁ ++ take (n - l₁.length) l₂ := by
induction l₁ generalizing n
· simp
· cases n
· simp [*]
· simp only [cons_append, take_cons_succ, length_cons, succ_eq_add_one, cons.injEq,
append_cancel_left_eq, true_and, *]
congr 1
omega
theorem take_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n l₁.length) :
(l₁ ++ l₂).take n = l₁.take n := by
simp [take_append_eq_append_take, Nat.sub_eq_zero_of_le h]
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
`i` elements of `l₂` to `l₁`. -/
theorem take_append {l₁ l₂ : List α} (i : Nat) :
take (l₁.length + i) (l₁ ++ l₂) = l₁ ++ take i l₂ := by
rw [take_append_eq_append_take, take_all_of_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
theorem getElem_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
L[i] = (L.take j)[i]'(length_take .. Nat.lt_min.mpr hj, hi) :=
getElem_of_eq (take_append_drop j L).symm _ getElem_append ..
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
theorem getElem_take' (L : List α) {j i : Nat} {h : i < (L.take j).length} :
(L.take j)[i] =
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
rw [length_take, Nat.lt_min] at h; rw [getElem_take L _ h.1]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the big list to the small list. -/
@[deprecated getElem_take (since := "2024-06-12")]
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
get L i, hi = get (L.take j) i, length_take .. Nat.lt_min.mpr hj, hi := by
simp [getElem_take _ hi hj]
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
length `> i`. Version designed to rewrite from the small list to the big list. -/
@[deprecated getElem_take (since := "2024-06-12")]
theorem get_take' (L : List α) {j i} :
get (L.take j) i =
get L i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _) := by
simp [getElem_take']
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n m) :
(l.take n)[m]? = none :=
getElem?_eq_none <| Nat.le_trans (length_take_le _ _) h
@[deprecated getElem?_take_eq_none (since := "2024-06-12")]
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} :
(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_eq_none (Nat.le_of_not_lt h)
@[deprecated getElem?_take_eq_if (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]
Further results on `List.take` and `List.drop`, which rely on stronger automation in `Nat`,
are given in `Init.Data.List.TakeDrop`.
-/
@[simp]
theorem take_eq_take :
{l : List α} {m n : Nat}, l.take m = l.take n min m l.length = min n l.length
| [], m, n => by simp [Nat.min_zero]
| _ :: xs, 0, 0 => by simp
| x :: xs, m + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, 0, n + 1 => by simp [Nat.zero_min, succ_min_succ]
| x :: xs, m + 1, n + 1 => by simp [succ_min_succ, take_eq_take]; omega
theorem drop_one : l : List α, drop 1 l = tail l
| [] | _ :: _ => rfl
theorem take_add (l : List α) (m n : Nat) : l.take (m + n) = l.take m ++ (l.drop m).take n := by
suffices take (m + n) (take m l ++ drop m l) = take m l ++ take n (drop m l) by
rw [take_append_drop] at this
assumption
rw [take_append_eq_append_take, take_all_of_le, append_right_inj]
· simp only [take_eq_take, length_take, length_drop]
omega
apply Nat.le_trans (m := m)
· apply length_take_le
· apply Nat.le_add_right
@[simp] theorem take_append_drop : (n : Nat) (l : List α), take n l ++ drop n l = l
| 0, _ => rfl
| _+1, [] => rfl
| n+1, x :: xs => congrArg (cons x) <| take_append_drop n xs
theorem dropLast_take {n : Nat} {l : List α} (h : n < l.length) :
(l.take n).dropLast = l.take n.pred := by
simp only [dropLast_eq_take, length_take, Nat.le_of_lt h, take_take, pred_le, Nat.min_eq_left]
@[simp] theorem length_drop : (i : Nat) (l : List α), length (drop i l) = length l - i
| 0, _ => rfl
| succ i, [] => Eq.symm (Nat.zero_sub (succ i))
| succ i, x :: l => calc
length (drop (succ i) (x :: l)) = length l - i := length_drop i l
_ = succ (length l) - succ i := (Nat.succ_sub_succ_eq_sub (length l) i).symm
theorem map_eq_append_split {f : α β} {l : List α} {s₁ s₂ : List β}
(h : map f l = s₁ ++ s₂) : l₁ l₂, l = l₁ ++ l₂ map f l₁ = s₁ map f l₂ = s₂ := by
have := h
rw [ take_append_drop (length s₁) l] at this
rw [map_append] at this
refine _, _, rfl, append_inj this ?_
rw [length_map, length_take, Nat.min_eq_left]
rw [ length_map l f, h, length_append]
apply Nat.le_add_right
theorem drop_of_length_le {l : List α} (h : l.length i) : drop i l = [] :=
length_eq_zero.1 (length_drop .. Nat.sub_eq_zero_of_le h)
/-! ### drop -/
theorem length_lt_of_drop_ne_nil {l : List α} {n} (h : drop n l []) : n < l.length :=
gt_of_not_le (mt drop_of_length_le h)
theorem drop_length_cons {l : List α} (h : l []) (a : α) :
(a :: l).drop l.length = [l.getLast h] := by
induction l generalizing a with
| nil =>
cases h rfl
| cons y l ih =>
simp only [drop, length]
by_cases h₁ : l = []
· simp [h₁]
rw [getLast_cons' _ h₁]
exact ih h₁ y
theorem take_of_length_le {l : List α} (h : l.length i) : take i l = l := by
have := take_append_drop i l
rw [drop_of_length_le h, append_nil] at this; exact this
/-- Dropping the elements up to `n` in `l₁ ++ l₂` is the same as dropping the elements up to `n`
in `l₁`, dropping the elements up to `n - l₁.length` in `l₂`, and appending them. -/
theorem drop_append_eq_append_drop {l₁ l₂ : List α} {n : Nat} :
drop n (l₁ ++ l₂) = drop n l₁ ++ drop (n - l₁.length) l₂ := by
induction l₁ generalizing n
· simp
· cases n
· simp [*]
· simp only [cons_append, drop_succ_cons, length_cons, succ_eq_add_one, append_cancel_left_eq, *]
congr 1
omega
theorem lt_length_of_take_ne_self {l : List α} {n} (h : l.take n l) : n < l.length :=
gt_of_not_le (mt take_of_length_le h)
theorem drop_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n l₁.length) :
(l₁ ++ l₂).drop n = l₁.drop n ++ l₂ := by
simp [drop_append_eq_append_drop, Nat.sub_eq_zero_of_le h]
@[deprecated drop_of_length_le (since := "2024-07-07")] abbrev drop_length_le := @drop_of_length_le
@[deprecated take_of_length_le (since := "2024-07-07")] abbrev take_length_le := @take_of_length_le
@[simp] theorem drop_length (l : List α) : drop l.length l = [] := drop_of_length_le (Nat.le_refl _)
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
up to `i` in `l₂`. -/
@[simp]
theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
theorem lt_length_drop (L : List α) {i j : Nat} (h : i + j < L.length) : j < (L.drop i).length := by
have A : i < L.length := Nat.lt_of_le_of_lt (Nat.le.intro rfl) h
rw [(take_append_drop i L).symm] at h
simpa only [Nat.le_of_lt A, Nat.min_eq_left, Nat.add_lt_add_iff_left, length_take,
length_append] using h
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
theorem getElem_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
L[i + j] = (L.drop i)[j]'(lt_length_drop L h) := by
have : i L.length := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_lt h)
rw [getElem_of_eq (take_append_drop i L).symm h, getElem_append_right'] <;>
simp [Nat.min_eq_left this, Nat.add_sub_cancel_left, Nat.le_add_right]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
@[deprecated getElem_drop (since := "2024-06-12")]
theorem get_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
get L i + j, h = get (L.drop i) j, lt_length_drop L h := by
simp [getElem_drop]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
theorem getElem_drop' (L : List α) {i : Nat} {j : Nat} {h : j < (L.drop i).length} :
(L.drop i)[j] = L[i + j]'(by
rw [Nat.add_comm]
exact Nat.add_lt_of_lt_sub (length_drop i L h)) := by
rw [getElem_drop]
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
@[deprecated getElem_drop' (since := "2024-06-12")]
theorem get_drop' (L : List α) {i j} :
get (L.drop i) j = get L i + j, by
rw [Nat.add_comm]
exact Nat.add_lt_of_lt_sub (length_drop i L j.2) := by
simp [getElem_drop']
@[simp] theorem take_length (l : List α) : take l.length l = l := take_of_length_le (Nat.le_refl _)
@[simp]
theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? := by
ext
simp only [getElem?_eq_some, getElem_drop', Option.mem_def]
constructor <;> intro h, ha
· exact _, ha
· refine ?_, ha
rw [length_drop]
rw [Nat.add_comm] at h
apply Nat.lt_sub_of_add_lt h
theorem getElem_cons_drop : (l : List α) (i : Nat) (h : i < l.length),
l[i] :: drop (i + 1) l = drop i l
| _::_, 0, _ => rfl
| _::_, i+1, _ => getElem_cons_drop _ i _
@[deprecated getElem?_drop (since := "2024-06-12")]
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
@[deprecated getElem_cons_drop (since := "2024-06-12")]
theorem get_cons_drop (l : List α) (i) : get l i :: drop (i + 1) l = drop i l := by
simp
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
split <;> rename_i h
· ext1 m
by_cases h' : m < n
· rw [getElem?_append (by simp [length_take]; omega), getElem?_set_ne (by omega),
getElem?_take h']
· by_cases h'' : m = n
· subst h''
rw [getElem?_set_eq (by simp; omega), getElem?_append_right, length_take,
Nat.min_eq_left (by omega), Nat.sub_self, getElem?_cons_zero]
rw [length_take]
exact Nat.min_le_left m l.length
· have h''' : n < m := by omega
rw [getElem?_set_ne (by omega), getElem?_append_right, length_take,
Nat.min_eq_left (by omega)]
· obtain k, rfl := Nat.exists_eq_add_of_lt h'''
have p : n + k + 1 - n = k + 1 := by omega
rw [p]
rw [getElem?_cons_succ, getElem?_drop]
congr 1
omega
· rw [length_take]
exact Nat.le_trans (Nat.min_le_left _ _) (by omega)
· rw [set_eq_of_length_le]
omega
theorem drop_eq_getElem_cons {n} {l : List α} (h) : drop n l = l[n] :: drop (n + 1) l :=
(getElem_cons_drop _ n h).symm
theorem drop_take : (m n : Nat) (l : List α), drop n (take m l) = take (m - n) (drop n l)
@[deprecated drop_eq_getElem_cons (since := "2024-06-12")]
theorem drop_eq_get_cons {n} {l : List α} (h) : drop n l = get l n, h :: drop (n + 1) l := by
simp [drop_eq_getElem_cons]
@[simp]
theorem getElem?_take {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)
| succ _ hn =>
cases l with
| nil => simp only [take_nil]
| cons hd tl =>
cases m
· simp
· simpa using hn (Nat.lt_of_succ_lt_succ h)
@[deprecated getElem?_take (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]
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? :=
getElem?_take (Nat.lt_succ_self n)
theorem tail_drop (l : List α) (n : Nat) : (l.drop n).tail = l.drop (n + 1) := by
induction l generalizing n with
| nil => simp
| cons hd tl hl =>
cases n
· simp
· simp [hl]
@[simp]
theorem drop_eq_nil_iff_le {l : List α} {k : Nat} : l.drop k = [] l.length k := by
refine' fun h => _, drop_eq_nil_of_le
induction k generalizing l with
| zero =>
simp only [drop] at h
simp [h]
| succ k hk =>
cases l
· simp
· simp only [drop] at h
simpa [Nat.succ_le_succ_iff] using hk h
@[simp]
theorem take_eq_nil_iff {l : List α} {k : Nat} : l.take k = [] k = 0 l = [] := by
cases l <;> cases k <;> simp [Nat.succ_ne_zero]
theorem drop_eq_nil_of_eq_nil : {as : List α} {i}, as = [] as.drop i = []
| _, _, rfl => drop_nil
theorem ne_nil_of_drop_ne_nil {as : List α} {i : Nat} (h: as.drop i []) : as [] :=
mt drop_eq_nil_of_eq_nil h
theorem take_eq_nil_of_eq_nil : {as : List α} {i}, as = [] as.take i = []
| _, _, rfl => take_nil
theorem ne_nil_of_take_ne_nil {as : List α} {i : Nat} (h : as.take i []) : as [] :=
mt take_eq_nil_of_eq_nil h
theorem set_take {l : List α} {n m : Nat} {a : α} :
(l.set m a).take n = (l.take n).set m a := by
induction n generalizing l m with
| zero => simp
| succ _ hn =>
cases l with
| nil => simp
| cons hd tl => cases m <;> simp_all
theorem drop_set {l : List α} {n m : Nat} {a : α} :
(l.set m a).drop n = if m < n then l.drop n else (l.drop n).set (m - n) a := by
induction n generalizing l m with
| zero => simp
| succ _ hn =>
cases l with
| nil => simp
| cons hd tl =>
cases m
· simp_all
· simp only [hn, set_cons_succ, drop_succ_cons, succ_lt_succ_iff]
congr 2
exact (Nat.add_sub_add_right ..).symm
theorem set_drop {l : List α} {n m : Nat} {a : α} :
(l.drop n).set m a = (l.set (n + m) a).drop n := by
rw [drop_set, if_neg, add_sub_self_left n m]
exact (Nat.not_lt).2 (le_add_right n m)
theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
(l.take i).concat l[i] = l.take (i+1) :=
Eq.symm <| (append_left_inj _).1 <| (take_append_drop (i+1) l).trans <| by
rw [concat_eq_append, append_assoc, singleton_append, get_drop_eq_drop, take_append_drop]
@[deprecated take_succ_cons (since := "2024-07-25")]
theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
@[deprecated take_of_length_le (since := "2024-07-25")]
theorem take_all_of_le {n} {l : List α} (h : length l n) : take n l = l :=
take_of_length_le h
theorem drop_left : l₁ l₂ : List α, drop (length l₁) (l₁ ++ l₂) = l₂
| [], _ => rfl
| _ :: l₁, l₂ => drop_left l₁ l₂
@[simp]
theorem drop_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : drop n (l₁ ++ l₂) = l₂ := by
rw [ h]; apply drop_left
theorem take_left : l₁ l₂ : List α, take (length l₁) (l₁ ++ l₂) = l₁
| [], _ => rfl
| a :: l₁, l₂ => congrArg (cons a) (take_left l₁ l₂)
@[simp]
theorem take_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : take n (l₁ ++ l₂) = l₁ := by
rw [ h]; apply take_left
theorem take_succ {l : List α} {n : Nat} : l.take (n + 1) = l.take n ++ l[n]?.toList := by
induction l generalizing n with
| nil =>
simp only [take_nil, Option.toList, getElem?_nil, append_nil]
| cons hd tl hl =>
cases n
· simp only [take, Option.toList, getElem?_cons_zero, nil_append]
· simp only [take, hl, getElem?_cons_succ, cons_append]
@[deprecated (since := "2024-07-25")]
theorem drop_sizeOf_le [SizeOf α] (l : List α) (n : Nat) : sizeOf (l.drop n) sizeOf l := by
induction l generalizing n with
| nil => rw [drop_nil]; apply Nat.le_refl
| cons _ _ lih =>
induction n with
| zero => apply Nat.le_refl
| succ n =>
exact Trans.trans (lih _) (Nat.le_add_left _ _)
theorem dropLast_eq_take (l : List α) : l.dropLast = l.take (l.length - 1) := by
cases l with
| nil => simp [dropLast]
| cons x l =>
induction l generalizing x <;> simp_all [dropLast]
@[simp] theorem map_take (f : α β) :
(L : List α) (i : Nat), (L.take i).map f = (L.map f).take i
| [], i => by simp
| _, 0 => by simp
| h :: t, n + 1 => by dsimp; rw [map_take f t n]
@[simp] theorem map_drop (f : α β) :
(L : List α) (i : Nat), (L.drop i).map f = (L.map f).drop i
| [], i => by simp
| L, 0 => by simp
| h :: t, n + 1 => by
dsimp
rw [map_drop f t]
@[simp] theorem drop_drop (n : Nat) : (m) (l : List α), drop n (drop m l) = drop (n + m) l
| m, [] => by simp
| 0, l => by simp
| m + 1, a :: l =>
calc
drop n (drop (m + 1) (a :: l)) = drop n (drop m l) := rfl
_ = drop (n + m) l := drop_drop n m l
_ = drop (n + (m + 1)) (a :: l) := rfl
theorem take_drop : (m n : Nat) (l : List α), take n (drop m l) = drop m (take (m + n) l)
| 0, _, _ => by simp
| _, 0, _ => by simp
| _, _, [] => by simp
| m+1, n+1, h :: t => by
simp [take_succ_cons, drop_succ_cons, drop_take m n t]
congr 1
omega
| _+1, _, _ :: _ => by simpa [Nat.succ_add, take_succ_cons, drop_succ_cons] using take_drop ..
theorem take_reverse {α} {xs : List α} (n : Nat) (h : n xs.length) :
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
@[deprecated drop_drop (since := "2024-06-15")]
theorem drop_add (m n) (l : List α) : drop (m + n) l = drop m (drop n l) := by
simp [drop_drop]
@[deprecated (since := "2024-06-15")] abbrev reverse_take := @take_reverse
/-! ### takeWhile and dropWhile -/
theorem takeWhile_cons (p : α Bool) (a : α) (l : List α) :
(a :: l).takeWhile p = if p a then a :: l.takeWhile p else [] := by
simp only [takeWhile]
by_cases h: p a <;> simp [h]
@[simp] theorem takeWhile_cons_of_pos {p : α Bool} {a : α} {l : List α} (h : p a) :
(a :: l).takeWhile p = a :: l.takeWhile p := by
simp [takeWhile_cons, h]
@[simp] theorem takeWhile_cons_of_neg {p : α Bool} {a : α} {l : List α} (h : ¬ p a) :
(a :: l).takeWhile p = [] := by
simp [takeWhile_cons, h]
theorem dropWhile_cons :
(x :: xs : List α).dropWhile p = if p x then xs.dropWhile p else x :: xs := by
split <;> simp_all [dropWhile]
@[simp] theorem dropWhile_cons_of_pos {a : α} {l : List α} (h : p a) :
(a :: l).dropWhile p = l.dropWhile p := by
simp [dropWhile_cons, h]
@[simp] theorem dropWhile_cons_of_neg {a : α} {l : List α} (h : ¬ p a) :
(a :: l).dropWhile p = a :: l := by
simp [dropWhile_cons, h]
theorem head?_takeWhile (p : α Bool) (l : List α) : (l.takeWhile p).head? = l.head?.filter p := by
cases l with
| nil => rfl
| cons x xs =>
simp only [takeWhile_cons, head?_cons, Option.filter_some]
split <;> simp
theorem head_takeWhile (p : α Bool) (l : List α) (w) :
(l.takeWhile p).head w = l.head (by rintro rfl; simp_all) := by
cases l with
| nil => rfl
| cons x xs =>
simp only [takeWhile_cons, head_cons]
simp only [takeWhile_cons] at w
split <;> simp_all
theorem head?_dropWhile_not (p : α Bool) (l : List α) :
match (l.dropWhile p).head? with | some x => p x = false | none => True := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [dropWhile_cons]
split <;> rename_i h <;> split at h <;> simp_all
theorem head_dropWhile_not (p : α Bool) (l : List α) (w) :
p ((l.dropWhile p).head w) = false := by
simpa [head?_eq_head, w] using head?_dropWhile_not p l
theorem takeWhile_map (f : α β) (p : β Bool) (l : List α) :
(l.map f).takeWhile p = (l.takeWhile (p f)).map f := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [map_cons, takeWhile_cons]
split <;> simp_all
theorem dropWhile_map (f : α β) (p : β Bool) (l : List α) :
(l.map f).dropWhile p = (l.dropWhile (p f)).map f := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [map_cons, dropWhile_cons]
split <;> simp_all
theorem takeWhile_filterMap (f : α Option β) (p : β Bool) (l : List α) :
(l.filterMap f).takeWhile p = (l.takeWhile fun a => (f a).all p).filterMap f := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [filterMap_cons]
split <;> rename_i h
· simp only [takeWhile_cons, h]
split <;> simp_all
· simp [takeWhile_cons, h, ih]
split <;> simp_all [filterMap_cons]
theorem dropWhile_filterMap (f : α Option β) (p : β Bool) (l : List α) :
(l.filterMap f).dropWhile p = (l.dropWhile fun a => (f a).all p).filterMap f := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [filterMap_cons]
split <;> rename_i h
· simp only [dropWhile_cons, h]
split <;> simp_all
· simp [dropWhile_cons, h, ih]
split <;> simp_all [filterMap_cons]
theorem takeWhile_filter (p q : α Bool) (l : List α) :
(l.filter p).takeWhile q = (l.takeWhile fun a => !p a || q a).filter p := by
simp [ filterMap_eq_filter, takeWhile_filterMap]
theorem dropWhile_filter (p q : α Bool) (l : List α) :
(l.filter p).dropWhile q = (l.dropWhile fun a => !p a || q a).filter p := by
simp [ filterMap_eq_filter, dropWhile_filterMap]
@[simp] theorem takeWhile_append_dropWhile (p : α Bool) :
(l : List α), takeWhile p l ++ dropWhile p l = l
| [] => rfl
| x :: xs => by simp [takeWhile, dropWhile]; cases p x <;> simp [takeWhile_append_dropWhile p xs]
theorem takeWhile_append {xs ys : List α} :
(xs ++ ys).takeWhile p =
if (xs.takeWhile p).length = xs.length then xs ++ ys.takeWhile p else xs.takeWhile p := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [cons_append, takeWhile_cons]
split
· simp_all only [length_cons, add_one_inj]
split <;> rfl
· simp_all
@[simp] theorem takeWhile_append_of_pos {p : α Bool} {l₁ l₂ : List α} (h : a l₁, p a) :
(l₁ ++ l₂).takeWhile p = l₁ ++ l₂.takeWhile p := by
induction l₁ with
| nil => simp
| cons x xs ih => simp_all [takeWhile_cons]
theorem dropWhile_append {xs ys : List α} :
(xs ++ ys).dropWhile p =
if (xs.dropWhile p).isEmpty then ys.dropWhile p else xs.dropWhile p ++ ys := by
induction xs with
| nil => simp
| cons h t ih =>
simp only [cons_append, dropWhile_cons]
split <;> simp_all
@[simp] theorem dropWhile_append_of_pos {p : α Bool} {l₁ l₂ : List α} (h : a l₁, p a) :
(l₁ ++ l₂).dropWhile p = l₂.dropWhile p := by
induction l₁ with
| nil => simp
| cons x xs ih => simp_all [dropWhile_cons]
@[simp] theorem takeWhile_replicate_eq_filter (p : α Bool) :
(replicate n a).takeWhile p = (replicate n a).filter p := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, takeWhile_cons]
split <;> simp_all
theorem takeWhile_replicate (p : α Bool) :
(replicate n a).takeWhile p = if p a then replicate n a else [] := by
rw [takeWhile_replicate_eq_filter, filter_replicate]
@[simp] theorem dropWhile_replicate_eq_filter_not (p : α Bool) :
(replicate n a).dropWhile p = (replicate n a).filter (fun a => !p a) := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, dropWhile_cons]
split <;> simp_all
theorem dropWhile_replicate (p : α Bool) :
(replicate n a).dropWhile p = if p a then [] else replicate n a := by
simp only [dropWhile_replicate_eq_filter_not, filter_replicate]
split <;> simp_all
theorem take_takeWhile {l : List α} (p : α Bool) n :
(l.takeWhile p).take n = (l.takeWhile p).take n := by
induction l with
| nil => rfl
| cons x xs ih =>
by_cases h : p x <;> simp [takeWhile_cons, h, ih]
@[simp] theorem all_takeWhile {l : List α} : (l.takeWhile p).all p = true := by
induction l with
| nil => rfl
| cons h t ih => by_cases p h <;> simp_all
@[simp] theorem any_dropWhile {l : List α} :
(l.dropWhile p).any (fun x => !p x) = !l.all p := by
induction l with
| nil => rfl
| cons h t ih => by_cases p h <;> simp_all
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_replicate (n) (a : α) : rotateLeft (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (n + 1) % m + min ((n + 1) % m) m = m by
simpa [rotateLeft]
intro h
rw [Nat.min_eq_left (Nat.le_of_lt (Nat.mod_lt _ (by omega)))]
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
omega
@[simp] theorem rotateLeft_zero (l : List α) : rotateLeft l 0 = l := by
simp [rotateLeft]
-- TODO Batteries defines its own `getElem?_rotate`, which we need to adapt.
-- TODO Prove `map_rotateLeft`, using `ext` and `getElem?_rotateLeft`.
/-! ### rotateRight -/
@[simp] theorem rotateRight_replicate (n) (a : α) : rotateRight (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (m - (n + 1) % m) + min (m - (n + 1) % m) m = m by
simpa [rotateRight]
intro h
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
rw [Nat.min_eq_left (by omega)]
omega
@[simp] theorem rotateRight_zero (l : List α) : rotateRight l 0 = l := by
simp [rotateRight]
/-! ### zipWith -/
@[simp] theorem length_zipWith (f : α β γ) (l₁ l₂) :
length (zipWith f l₁ l₂) = min (length l₁) (length l₂) := by
induction l₁ generalizing l₂ <;> cases l₂ <;>
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
theorem zipWith_eq_zipWith_take_min : (l₁ : List α) (l₂ : List β),
zipWith f l₁ l₂ = zipWith f (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zipWith_eq_zipWith_take_min l₁ l₂]
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
rw [zipWith_eq_zipWith_take_min]
simp
/-! ### zip -/
@[simp] theorem length_zip (l₁ : List α) (l₂ : List β) :
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
simp [zip]
theorem zip_eq_zip_take_min : (l₁ : List α) (l₂ : List β),
zip l₁ l₂ = zip (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zip_eq_zip_take_min l₁ l₂]
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
rw [zip_eq_zip_take_min]
simp
/-! ### minimum? -/
-- A specialization of `minimum?_eq_some_iff` to Nat.
theorem minimum?_eq_some_iff' {xs : List Nat} :
xs.minimum? = some a (a xs b xs, a b) :=
minimum?_eq_some_iff
(le_refl := Nat.le_refl)
(min_eq_or := fun _ _ => by omega)
(le_min_iff := fun _ _ _ => by omega)
/-! ### maximum? -/
-- A specialization of `maximum?_eq_some_iff` to Nat.
theorem maximum?_eq_some_iff' {xs : List Nat} :
xs.maximum? = some a (a xs b xs, b a) :=
maximum?_eq_some_iff
(le_refl := Nat.le_refl)
(max_eq_or := fun _ _ => by omega)
(max_le_iff := fun _ _ _ => by omega)
-- TODO Batteries defines its own `getElem?_rotate`, which we need to adapt.
-- TODO Prove `map_rotateRight`, using `ext` and `getElem?_rotateRight`.
end List

363
src/Init/Data/List/Zip.lean Normal file
View File

@@ -0,0 +1,363 @@
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
prelude
import Init.Data.List.TakeDrop
/-!
# Lemmas about `List.zip`, `List.zipWith`, `List.zipWithAll`, and `List.unzip`.
-/
namespace List
open Nat
/-! ## Zippers -/
/-! ### zip -/
theorem zip_map (f : α γ) (g : β δ) :
(l₁ : List α) (l₂ : List β), zip (l₁.map f) (l₂.map g) = (zip l₁ l₂).map (Prod.map f g)
| [], l₂ => rfl
| l₁, [] => by simp only [map, zip_nil_right]
| a :: l₁, b :: l₂ => by
simp only [map, zip_cons_cons, zip_map, Prod.map]; constructor
theorem zip_map_left (f : α γ) (l₁ : List α) (l₂ : List β) :
zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id) := by rw [ zip_map, map_id]
theorem zip_map_right (f : β γ) (l₁ : List α) (l₂ : List β) :
zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f) := by rw [ zip_map, map_id]
theorem zip_append :
{l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
| [], r₁, l₂, r₂, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
| l₁, r₁, [], r₂, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
| a :: l₁, r₁, b :: l₂, r₂, h => by
simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
theorem zip_map' (f : α β) (g : α γ) :
l : List α, zip (l.map f) (l.map g) = l.map fun a => (f a, g a)
| [] => rfl
| a :: l => by simp only [map, zip_cons_cons, zip_map']
theorem of_mem_zip {a b} : {l₁ : List α} {l₂ : List β}, (a, b) zip l₁ l₂ a l₁ b l₂
| _ :: l₁, _ :: l₂, h => by
cases h
case head => simp
case tail h =>
· have := of_mem_zip h
exact Mem.tail _ this.1, Mem.tail _ this.2
@[deprecated of_mem_zip (since := "2024-07-28")] abbrev mem_zip := @of_mem_zip
theorem map_fst_zip :
(l₁ : List α) (l₂ : List β), l₁.length l₂.length map Prod.fst (zip l₁ l₂) = l₁
| [], bs, _ => rfl
| _ :: as, _ :: bs, h => by
simp [Nat.succ_le_succ_iff] at h
show _ :: map Prod.fst (zip as bs) = _ :: as
rw [map_fst_zip as bs h]
| a :: as, [], h => by simp at h
theorem map_snd_zip :
(l₁ : List α) (l₂ : List β), l₂.length l₁.length map Prod.snd (zip l₁ l₂) = l₂
| _, [], _ => by
rw [zip_nil_right]
rfl
| [], b :: bs, h => by simp at h
| a :: as, b :: bs, h => by
simp [Nat.succ_le_succ_iff] at h
show _ :: map Prod.snd (zip as bs) = _ :: bs
rw [map_snd_zip as bs h]
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 _
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 _
/-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :
zip (replicate n a) (replicate n b) = replicate n (a, b) := by
induction n with
| zero => rfl
| succ n ih => simp [replicate_succ, ih]
/-! ### zipWith -/
theorem zipWith_comm (f : α β γ) :
(la : List α) (lb : List β), zipWith f la lb = zipWith (fun b a => f a b) lb la
| [], _ => List.zipWith_nil_right.symm
| _ :: _, [] => rfl
| _ :: as, _ :: bs => congrArg _ (zipWith_comm f as bs)
theorem zipWith_comm_of_comm (f : α α β) (comm : x y : α, f x y = f y x) (l l' : List α) :
zipWith f l l' = zipWith f l' l := by
rw [zipWith_comm]
simp only [comm]
@[simp]
theorem zipWith_same (f : α α δ) : l : List α, zipWith f l l = l.map fun a => f a a
| [] => rfl
| _ :: xs => congrArg _ (zipWith_same f xs)
/--
See also `getElem?_zipWith'` for a variant
using `Option.map` and `Option.bind` rather than a `match`.
-/
theorem getElem?_zipWith {f : α β γ} {i : Nat} :
(List.zipWith f as bs)[i]? = match as[i]?, bs[i]? with
| some a, some b => some (f a b) | _, _ => none := by
induction as generalizing bs i with
| nil => cases bs with
| nil => simp
| cons b bs => simp
| cons a as aih => cases bs with
| nil => simp
| cons b bs => cases i <;> simp_all
/-- Variant of `getElem?_zipWith` using `Option.map` and `Option.bind` rather than a `match`. -/
theorem getElem?_zipWith' {f : α β γ} {i : Nat} :
(zipWith f l₁ l₂)[i]? = (l₁[i]?.map f).bind fun g => l₂[i]?.map g := by
induction l₁ generalizing l₂ i with
| nil => rw [zipWith] <;> simp
| cons head tail =>
cases l₂
· simp
· cases i <;> simp_all
theorem getElem?_zipWith_eq_some (f : α β γ) (l₁ : List α) (l₂ : List β) (z : γ) (i : Nat) :
(zipWith f l₁ l₂)[i]? = some z
x y, l₁[i]? = some x l₂[i]? = some y f x y = z := by
induction l₁ generalizing l₂ i
· simp
· cases l₂ <;> cases i <;> simp_all
theorem getElem?_zip_eq_some (l₁ : List α) (l₂ : List β) (z : α × β) (i : Nat) :
(zip l₁ l₂)[i]? = some z l₁[i]? = some z.1 l₂[i]? = some z.2 := by
cases z
rw [zip, getElem?_zipWith_eq_some]; constructor
· rintro x, y, h₀, h₁, h₂
simpa [h₀, h₁] using h₂
· rintro h₀, h₁
exact _, _, h₀, h₁, rfl
@[deprecated getElem?_zipWith (since := "2024-06-12")]
theorem get?_zipWith {f : α β γ} :
(List.zipWith f as bs).get? i = match as.get? i, bs.get? i with
| some a, some b => some (f a b) | _, _ => none := by
simp [getElem?_zipWith]
set_option linter.deprecated false in
@[deprecated getElem?_zipWith (since := "2024-06-07")] abbrev zipWith_get? := @get?_zipWith
theorem head?_zipWith {f : α β γ} :
(List.zipWith f as bs).head? = match as.head?, bs.head? with
| some a, some b => some (f a b) | _, _ => none := by
simp [head?_eq_getElem?, getElem?_zipWith]
theorem head_zipWith {f : α β γ} (h):
(List.zipWith f as bs).head h = f (as.head (by rintro rfl; simp_all)) (bs.head (by rintro rfl; simp_all)) := by
apply Option.some.inj
rw [ head?_eq_head, head?_zipWith, head?_eq_head, head?_eq_head]
@[simp]
theorem zipWith_map {μ} (f : γ δ μ) (g : α γ) (h : β δ) (l₁ : List α) (l₂ : List β) :
zipWith f (l₁.map g) (l₂.map h) = zipWith (fun a b => f (g a) (h b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_map_left (l₁ : List α) (l₂ : List β) (f : α α') (g : α' β γ) :
zipWith g (l₁.map f) l₂ = zipWith (fun a b => g (f a) b) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_map_right (l₁ : List α) (l₂ : List β) (f : β β') (g : α β' γ) :
zipWith g l₁ (l₂.map f) = zipWith (fun a b => g a (f b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_foldr_eq_zip_foldr {f : α β γ} (i : δ):
(zipWith f l₁ l₂).foldr g i = (zip l₁ l₂).foldr (fun p r => g (f p.1 p.2) r) i := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_foldl_eq_zip_foldl {f : α β γ} (i : δ):
(zipWith f l₁ l₂).foldl g i = (zip l₁ l₂).foldl (fun r p => g r (f p.1 p.2)) i := by
induction l₁ generalizing i l₂ <;> cases l₂ <;> simp_all
@[simp]
theorem zipWith_eq_nil_iff {f : α β γ} {l l'} : zipWith f l l' = [] l = [] l' = [] := by
cases l <;> cases l' <;> simp
theorem map_zipWith {δ : Type _} (f : α β) (g : γ δ α) (l : List γ) (l' : List δ) :
map f (zipWith g l l') = zipWith (fun x y => f (g x y)) l l' := by
induction l generalizing l' with
| nil => simp
| cons hd tl hl =>
· cases l'
· simp
· simp [hl]
theorem take_zipWith : (zipWith f l l').take n = zipWith f (l.take n) (l'.take n) := by
induction l generalizing l' n with
| nil => simp
| cons hd tl hl =>
cases l'
· simp
· cases n
· simp
· simp [hl]
@[deprecated take_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_take := @take_zipWith
theorem drop_zipWith : (zipWith f l l').drop n = zipWith f (l.drop n) (l'.drop n) := by
induction l generalizing l' n with
| nil => simp
| cons hd tl hl =>
· cases l'
· simp
· cases n
· simp
· simp [hl]
@[deprecated drop_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_drop := @drop_zipWith
theorem tail_zipWith : (zipWith f l l').tail = zipWith f l.tail l'.tail := by
rw [ drop_one]; simp [drop_zipWith]
@[deprecated tail_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_tail := @tail_zipWith
theorem zipWith_append (f : α β γ) (l la : List α) (l' lb : List β)
(h : l.length = l'.length) :
zipWith f (l ++ la) (l' ++ lb) = zipWith f l l' ++ zipWith f la lb := by
induction l generalizing l' with
| nil =>
have : l' = [] := eq_nil_of_length_eq_zero (by simpa using h.symm)
simp [this]
| cons hl tl ih =>
cases l' with
| nil => simp at h
| cons head tail =>
simp only [length_cons, Nat.succ.injEq] at h
simp [ih _ h]
/-- See also `List.zipWith_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zipWith_replicate' {a : α} {b : β} {n : Nat} :
zipWith f (replicate n a) (replicate n b) = replicate n (f a b) := by
induction n with
| zero => rfl
| succ n ih => simp [replicate_succ, ih]
/-! ### zipWithAll -/
theorem getElem?_zipWithAll {f : Option α Option β γ} {i : Nat} :
(zipWithAll f as bs)[i]? = match as[i]?, bs[i]? with
| none, none => .none | a?, b? => some (f a? b?) := by
induction as generalizing bs i with
| nil => induction bs generalizing i with
| nil => simp
| cons b bs bih => cases i <;> simp_all
| cons a as aih => cases bs with
| nil =>
specialize @aih []
cases i <;> simp_all
| cons b bs => cases i <;> simp_all
@[deprecated getElem?_zipWithAll (since := "2024-06-12")]
theorem get?_zipWithAll {f : Option α Option β γ} :
(zipWithAll f as bs).get? i = match as.get? i, bs.get? i with
| none, none => .none | a?, b? => some (f a? b?) := by
simp [getElem?_zipWithAll]
set_option linter.deprecated false in
@[deprecated getElem?_zipWithAll (since := "2024-06-07")] abbrev zipWithAll_get? := @get?_zipWithAll
theorem head?_zipWithAll {f : Option α Option β γ} :
(zipWithAll f as bs).head? = match as.head?, bs.head? with
| none, none => .none | a?, b? => some (f a? b?) := by
simp [head?_eq_getElem?, getElem?_zipWithAll]
theorem head_zipWithAll {f : Option α Option β γ} (h) :
(zipWithAll f as bs).head h = f as.head? bs.head? := by
apply Option.some.inj
rw [ head?_eq_head, head?_zipWithAll]
split <;> simp_all
theorem zipWithAll_map {μ} (f : Option γ Option δ μ) (g : α γ) (h : β δ) (l₁ : List α) (l₂ : List β) :
zipWithAll f (l₁.map g) (l₂.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWithAll_map_left (l₁ : List α) (l₂ : List β) (f : α α') (g : Option α' Option β γ) :
zipWithAll g (l₁.map f) l₂ = zipWithAll (fun a b => g (f <$> a) b) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWithAll_map_right (l₁ : List α) (l₂ : List β) (f : β β') (g : Option α Option β' γ) :
zipWithAll g l₁ (l₂.map f) = zipWithAll (fun a b => g a (f <$> b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem map_zipWithAll {δ : Type _} (f : α β) (g : Option γ Option δ α) (l : List γ) (l' : List δ) :
map f (zipWithAll g l l') = zipWithAll (fun x y => f (g x y)) l l' := by
induction l generalizing l' with
| nil => simp
| cons hd tl hl =>
cases l' <;> simp_all
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
induction n with
| zero => rfl
| succ n ih => simp [replicate_succ, ih]
/-! ### unzip -/
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
induction l <;> simp_all
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
induction l <;> simp_all
@[deprecated unzip_fst (since := "2024-07-28")] abbrev unzip_left := @unzip_fst
@[deprecated unzip_snd (since := "2024-07-28")] abbrev unzip_right := @unzip_snd
theorem unzip_eq_map : l : List (α × β), unzip l = (l.map Prod.fst, l.map Prod.snd)
| [] => rfl
| (a, b) :: l => by simp only [unzip_cons, map_cons, unzip_eq_map l]
theorem zip_unzip : l : List (α × β), zip (unzip l).1 (unzip l).2 = l
| [] => rfl
| (a, b) :: l => by simp only [unzip_cons, zip_cons_cons, zip_unzip l]
theorem unzip_zip_left :
{l₁ : List α} {l₂ : List β}, length l₁ length l₂ (unzip (zip l₁ l₂)).1 = l₁
| [], l₂, _ => rfl
| l₁, [], h => by rw [eq_nil_of_length_eq_zero (Nat.eq_zero_of_le_zero h)]; rfl
| a :: l₁, b :: l₂, h => by
simp only [zip_cons_cons, unzip_cons, unzip_zip_left (le_of_succ_le_succ h)]
theorem unzip_zip_right :
{l₁ : List α} {l₂ : List β}, length l₂ length l₁ (unzip (zip l₁ l₂)).2 = l₂
| [], l₂, _ => by simp_all
| l₁, [], _ => by simp
| a :: l₁, b :: l₂, h => by
simp only [zip_cons_cons, unzip_cons, unzip_zip_right (le_of_succ_le_succ h)]
theorem unzip_zip {l₁ : List α} {l₂ : List β} (h : length l₁ = length l₂) :
unzip (zip l₁ l₂) = (l₁, l₂) := by
ext
· rw [unzip_zip_left (Nat.le_of_eq h)]
· rw [unzip_zip_right (Nat.le_of_eq h.symm)]
theorem zip_of_prod {l : List α} {l' : List β} {lp : List (α × β)} (hl : lp.map Prod.fst = l)
(hr : lp.map Prod.snd = l') : lp = l.zip l' := by
rw [ hl, hr, zip_unzip lp, unzip_fst, unzip_snd, zip_unzip, zip_unzip]
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
ext1 <;> simp

View File

@@ -100,6 +100,14 @@ def blt (a b : Nat) : Bool :=
ble a.succ b
attribute [simp] Nat.zero_le
attribute [simp] Nat.not_lt_zero
theorem and_forall_add_one {p : Nat Prop} : p 0 ( n, p (n + 1)) n, p n :=
fun h n => Nat.casesOn n h.1 h.2, fun h => h _, fun _ => h _
theorem or_exists_add_one : p 0 (Exists fun n => p (n + 1)) Exists p :=
fun h => h.elim (fun h0 => 0, h0) fun n, hn => n + 1, hn,
fun n, h => match n with | 0 => Or.inl h | n+1 => Or.inr n, h
/-! # Helper "packing" theorems -/
@@ -124,13 +132,8 @@ instance : LawfulBEq Nat where
eq_of_beq h := Nat.eq_of_beq_eq_true h
rfl := by simp [BEq.beq]
@[simp] theorem beq_eq_true_eq (a b : Nat) : ((a == b) = true) = (a = b) := propext <| Iff.intro eq_of_beq (fun h => by subst h; apply LawfulBEq.rfl)
@[simp] theorem not_beq_eq_true_eq (a b : Nat) : ((!(a == b)) = true) = ¬(a = b) :=
propext <| Iff.intro
(fun h₁ h₂ => by subst h₂; rw [LawfulBEq.rfl] at h₁; contradiction)
(fun h =>
have : ¬ ((a == b) = true) := fun h' => absurd (eq_of_beq h') h
by simp [this])
theorem beq_eq_true_eq (a b : Nat) : ((a == b) = true) = (a = b) := by simp
theorem not_beq_eq_true_eq (a b : Nat) : ((!(a == b)) = true) = ¬(a = b) := by simp
/-! # Nat.add theorems -/
@@ -355,7 +358,7 @@ protected theorem pos_of_ne_zero {n : Nat} : n ≠ 0 → 0 < n := (eq_zero_or_po
theorem lt.base (n : Nat) : n < succ n := Nat.le_refl (succ n)
@[simp] theorem lt_succ_self (n : Nat) : n < succ n := lt.base n
theorem lt_succ_self (n : Nat) : n < succ n := lt.base n
@[simp] protected theorem lt_add_one (n : Nat) : n < n + 1 := lt.base n
@@ -392,11 +395,11 @@ theorem le_or_eq_of_le_succ {m n : Nat} (h : m ≤ succ n) : m ≤ n m = suc
theorem le_or_eq_of_le_add_one {m n : Nat} (h : m n + 1) : m n m = n + 1 :=
le_or_eq_of_le_succ h
theorem le_add_right : (n k : Nat), n n + k
@[simp] theorem le_add_right : (n k : Nat), n n + k
| n, 0 => Nat.le_refl n
| n, k+1 => le_succ_of_le (le_add_right n k)
theorem le_add_left (n m : Nat): n m + n :=
@[simp] theorem le_add_left (n m : Nat): n m + n :=
Nat.add_comm n m le_add_right n m
theorem le_of_add_right_le {n m k : Nat} (h : n + k m) : n m :=
@@ -532,7 +535,7 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
rw [Nat.add_comm _ b, Nat.add_comm _ b]
apply Nat.le_of_add_le_add_left
protected theorem add_le_add_iff_right {n : Nat} : m + n k + n m k :=
@[simp] protected theorem add_le_add_iff_right {n : Nat} : m + n k + n m k :=
Nat.le_of_add_le_add_right, fun h => Nat.add_le_add_right h _
/-! ### le/lt -/
@@ -638,6 +641,10 @@ theorem succ_lt_succ_iff : succ a < succ b ↔ a < b := ⟨lt_of_succ_lt_succ, s
theorem add_one_inj : a + 1 = b + 1 a = b := succ_inj'
theorem ne_add_one (n : Nat) : n n + 1 := fun h => by cases h
theorem add_one_ne (n : Nat) : n + 1 n := fun h => by cases h
theorem add_one_le_add_one_iff : a + 1 b + 1 a b := succ_le_succ_iff
theorem add_one_lt_add_one_iff : a + 1 < b + 1 a < b := succ_lt_succ_iff
@@ -705,8 +712,7 @@ protected theorem one_ne_zero : 1 ≠ (0 : Nat) :=
protected theorem zero_ne_one : 0 (1 : Nat) :=
fun h => Nat.noConfusion h
@[simp] theorem succ_ne_zero (n : Nat) : succ n 0 :=
fun h => Nat.noConfusion h
theorem succ_ne_zero (n : Nat) : succ n 0 := by simp
/-! # mul + order -/
@@ -814,8 +820,14 @@ theorem sub_one_lt_of_lt {n m : Nat} (h : m < n) : n - 1 < n :=
/-! # pred theorems -/
@[simp] protected theorem pred_zero : pred 0 = 0 := rfl
@[simp] protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
protected theorem pred_zero : pred 0 = 0 := rfl
protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
@[simp] protected theorem zero_sub_one : 0 - 1 = 0 := rfl
@[simp] protected theorem add_one_sub_one (n : Nat) : n + 1 - 1 = n := rfl
theorem sub_one_eq_self (n : Nat) : n - 1 = n n = 0 := by cases n <;> simp [ne_add_one]
theorem eq_self_sub_one (n : Nat) : n = n - 1 n = 0 := by cases n <;> simp [add_one_ne]
theorem succ_pred {a : Nat} (h : a 0) : a.pred.succ = a := by
induction a with

View File

@@ -86,7 +86,7 @@ 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]
@[simp] theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
unfold testBit
simp [shiftRight_succ_inside]
@@ -265,8 +265,8 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
have x_eq : x = y + 2^j := Nat.eq_add_of_sub_eq x_ge_j y_eq
simp only [Nat.two_pow_pos, x_eq, Nat.le_add_left, true_and, ite_true]
have y_lt_x : y < x := by
simp [x_eq]
exact Nat.lt_add_of_pos_right (Nat.two_pow_pos j)
simp only [x_eq, Nat.lt_add_right_iff_pos]
exact Nat.two_pow_pos j
simp only [hyp y y_lt_x]
if i_lt_j : i < j then
rw [Nat.add_comm _ (2^_), testBit_two_pow_add_gt i_lt_j]
@@ -504,3 +504,27 @@ 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]
/-! ### le -/
theorem le_of_testBit {n m : Nat} (h : i, n.testBit i = true m.testBit i = true) : n m := by
induction n using div2Induction generalizing m
next n ih =>
have : n / 2 m / 2 := by
rcases n with (_|n)
· simp
· exact ih (Nat.succ_pos _) fun i => by simpa using h (i + 1)
rw [ div_add_mod n 2, div_add_mod m 2]
cases hn : n.testBit 0
· have hn2 : n % 2 = 0 := by simp at hn; omega
rw [hn2]
omega
· have hn2 : n % 2 = 1 := by simpa using hn
have hm2 : m % 2 = 1 := by simpa using h _ hn
omega
theorem and_le_left {n m : Nat} : n &&& m n :=
le_of_testBit (by simpa using fun i x _ => x)
theorem and_le_right {n m : Nat} : n &&& m m :=
le_of_testBit (by simp)

View File

@@ -46,6 +46,9 @@ theorem gcd_succ (x y : Nat) : gcd (succ x) y = gcd (y % succ x) (succ x) := by
theorem gcd_add_one (x y : Nat) : gcd (x + 1) y = gcd (y % (x + 1)) (x + 1) := by
rw [gcd]; rfl
theorem gcd_def (x y : Nat) : gcd x y = if x = 0 then y else gcd (y % x) x := by
cases x <;> simp [Nat.gcd_add_one]
@[simp] theorem gcd_one_left (n : Nat) : gcd 1 n = 1 := by
rw [gcd_succ, mod_one]
rfl

View File

@@ -19,6 +19,14 @@ and later these lemmas should be organised into other files more systematically.
-/
namespace Nat
@[deprecated and_forall_add_one (since := "2024-07-30")] abbrev and_forall_succ := @and_forall_add_one
@[deprecated or_exists_add_one (since := "2024-07-30")] abbrev or_exists_succ := @or_exists_add_one
@[simp] theorem exists_ne_zero {P : Nat Prop} : ( n, ¬ n = 0 P n) n, P (n + 1) :=
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
/-! ## add -/
protected theorem add_add_add_comm (a b c d : Nat) : (a + b) + (c + d) = (a + c) + (b + d) := by
@@ -36,13 +44,13 @@ protected theorem eq_zero_of_add_eq_zero_right (h : n + m = 0) : n = 0 :=
protected theorem add_eq_zero_iff : n + m = 0 n = 0 m = 0 :=
Nat.eq_zero_of_add_eq_zero, fun h₁, h₂ => h₂.symm h₁
protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k m = k :=
@[simp] protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k m = k :=
Nat.add_left_cancel, fun | rfl => rfl
protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n m = k :=
@[simp] protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n m = k :=
Nat.add_right_cancel, fun | rfl => rfl
protected theorem add_le_add_iff_left {n : Nat} : n + m n + k m k :=
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m n + k m k :=
Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _
protected theorem lt_of_add_lt_add_right : {n : Nat}, k + n < m + n k < m
@@ -52,10 +60,10 @@ protected theorem lt_of_add_lt_add_right : ∀ {n : Nat}, k + n < m + n → k <
protected theorem lt_of_add_lt_add_left {n : Nat} : n + k < n + m k < m := by
rw [Nat.add_comm n, Nat.add_comm n]; exact Nat.lt_of_add_lt_add_right
protected theorem add_lt_add_iff_left {k n m : Nat} : k + n < k + m n < m :=
@[simp] protected theorem add_lt_add_iff_left {k n m : Nat} : k + n < k + m n < m :=
Nat.lt_of_add_lt_add_left, fun h => Nat.add_lt_add_left h _
protected theorem add_lt_add_iff_right {k n m : Nat} : n + k < m + k n < m :=
@[simp] protected theorem add_lt_add_iff_right {k n m : Nat} : n + k < m + k n < m :=
Nat.lt_of_add_lt_add_right, fun h => Nat.add_lt_add_right h _
protected theorem add_lt_add_of_le_of_lt {a b c d : Nat} (hle : a b) (hlt : c < d) :
@@ -75,10 +83,10 @@ protected theorem pos_of_lt_add_right (h : n < n + k) : 0 < k :=
protected theorem pos_of_lt_add_left : n < k + n 0 < k := by
rw [Nat.add_comm]; exact Nat.pos_of_lt_add_right
protected theorem lt_add_right_iff_pos : n < n + k 0 < k :=
@[simp] protected theorem lt_add_right_iff_pos : n < n + k 0 < k :=
Nat.pos_of_lt_add_right, Nat.lt_add_of_pos_right
protected theorem lt_add_left_iff_pos : n < k + n 0 < k :=
@[simp] protected theorem lt_add_left_iff_pos : n < k + n 0 < k :=
Nat.pos_of_lt_add_left, Nat.lt_add_of_pos_left
protected theorem add_pos_left (h : 0 < m) (n) : 0 < m + n :=
@@ -115,8 +123,6 @@ protected theorem add_sub_cancel_right (n m : Nat) : (n + m) - m = n := Nat.add_
theorem succ_sub_one (n) : succ n - 1 = n := rfl
protected theorem add_one_sub_one (n : Nat) : (n + 1) - 1 = n := rfl
protected theorem one_add_sub_one (n : Nat) : (1 + n) - 1 = n := Nat.add_sub_cancel_left 1 _
protected theorem sub_sub_self {n m : Nat} (h : m n) : n - (n - m) = m :=

View File

@@ -173,13 +173,13 @@ instance : LawfulBEq PolyCnstr where
eq_of_beq {a b} h := by
cases a; rename_i eq₁ lhs₁ rhs₁
cases b; rename_i eq₂ lhs₂ rhs₂
have h : eq₁ == eq₂ && lhs₁ == lhs₂ && rhs₁ == rhs₂ := h
have h : eq₁ == eq₂ && (lhs₁ == lhs₂ && rhs₁ == rhs₂) := h
simp at h
have h₁, h₂, h₃ := h
have h₁, h₂, h₃ := h
rw [h₁, h₂, h₃]
rfl {a} := by
cases a; rename_i eq lhs rhs
show (eq == eq && lhs == lhs && rhs == rhs) = true
show (eq == eq && (lhs == lhs && rhs == rhs)) = true
simp [LawfulBEq.rfl]
def PolyCnstr.mul (k : Nat) (c : PolyCnstr) : PolyCnstr :=

View File

@@ -19,6 +19,7 @@ def getM [Alternative m] : Option α → m α
| some a => pure a
@[deprecated getM (since := "2024-04-17")]
-- `[Monad m]` is not needed here.
def toMonad [Monad m] [Alternative m] : Option α m α := getM
/-- Returns `true` on `some x` and `false` on `none`. -/
@@ -26,7 +27,7 @@ def toMonad [Monad m] [Alternative m] : Option α → m α := getM
| some _ => true
| none => false
@[deprecated isSome, inline] def toBool : Option α Bool := isSome
@[deprecated isSome (since := "2024-04-17"), inline] def toBool : Option α Bool := isSome
/-- Returns `true` on `none` and `false` on `some x`. -/
@[inline] def isNone : Option α Bool
@@ -80,7 +81,9 @@ theorem map_id : (Option.map id : Option α → Option α) = id :=
| none => false
/--
Implementation of `OrElse`'s `<|>` syntax for `Option`.
Implementation of `OrElse`'s `<|>` syntax for `Option`. If the first argument is `some a`, returns
`some a`, otherwise evaluates and returns the second argument. See also `or` for a version that is
strict in the second argument.
-/
@[always_inline, macro_inline] protected def orElse : Option α (Unit Option α) Option α
| some a, _ => some a
@@ -89,6 +92,12 @@ Implementation of `OrElse`'s `<|>` syntax for `Option`.
instance : OrElse (Option α) where
orElse := Option.orElse
/-- If the first argument is `some a`, returns `some a`, otherwise returns the second argument.
This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
@[always_inline, macro_inline] def or : Option α Option α Option α
| some a, _ => some a
| none, b => b
@[inline] protected def lt (r : α α Prop) : Option α Option α Prop
| none, some _ => True
| some x, some y => r x y
@@ -203,6 +212,9 @@ instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
@[simp] theorem all_none : Option.all p none = true := rfl
@[simp] theorem all_some : Option.all p (some x) = p x := rfl
@[simp] theorem any_none : Option.any p none = false := rfl
@[simp] theorem any_some : Option.any p (some x) = p x := rfl
/-- The minimum of two optional values. -/
protected def min [Min α] : Option α Option α Option α
| some x, some y => some (Min.min x y)

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.Option.BasicAux
import Init.Data.Option.Instances
import Init.Classical
import Init.Ext
@@ -41,6 +42,21 @@ theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.get
theorem getD_eq_iff {o : Option α} {a b} : o.getD a = b (o = some b o = none a = b) := by
cases o <;> simp
@[simp] theorem get!_none [Inhabited α] : (none : Option α).get! = default := rfl
@[simp] theorem get!_some [Inhabited α] {a : α} : (some a).get! = a := rfl
theorem get_eq_get! [Inhabited α] : (o : Option α) {h : o.isSome} o.get h = o.get!
| some _, _ => rfl
theorem get_eq_getD {fallback : α} : (o : Option α) {h : o.isSome} o.get h = o.getD fallback
| some _, _ => rfl
theorem some_get! [Inhabited α] : (o : Option α) o.isSome some (o.get!) = o
| some _, _ => rfl
theorem get!_eq_getD_default [Inhabited α] (o : Option α) : o.get! = o.getD default := rfl
theorem mem_unique {o : Option α} {a b : α} (ha : a o) (hb : b o) : a = b :=
some.inj <| ha hb
@@ -66,7 +82,7 @@ theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by cases x <;> sim
cases a <;> simp
theorem eq_some_iff_get_eq : o = some a h : o.isSome, o.get h = a := by
cases o <;> simp; nofun
cases o <;> simp
theorem eq_some_of_isSome : {o : Option α} (h : o.isSome), o = some (o.get h)
| some _, _ => rfl
@@ -145,6 +161,12 @@ theorem map_eq_some : f <$> x = some b ↔ ∃ a, x = some a ∧ f a = b := map_
@[simp] theorem map_eq_none' : x.map f = none x = none := by
cases x <;> simp only [map_none', map_some', eq_self_iff_true]
theorem isSome_map {x : Option α} : (f <$> x).isSome = x.isSome := by
cases x <;> simp
@[simp] theorem isSome_map' {x : Option α} : (x.map f).isSome = x.isSome := by
cases x <;> simp
theorem map_eq_none : f <$> x = none x = none := map_eq_none'
theorem map_eq_bind {x : Option α} : x.map f = x.bind (some f) := by
@@ -168,6 +190,19 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
theorem mem_map_of_mem (g : α β) (h : a x) : g a Option.map g x := h.symm map_some' ..
@[simp] theorem filter_none (p : α Bool) : none.filter p = none := rfl
theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
@[simp] theorem all_guard (p : α Prop) [DecidablePred p] (a : α) :
Option.all q (guard p a) = (!p a || q a) := by
simp only [guard]
split <;> simp_all
@[simp] theorem any_guard (p : α Prop) [DecidablePred p] (a : α) :
Option.any q (guard p a) = (p a && q a) := by
simp only [guard]
split <;> simp_all
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α β} :
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp
@@ -236,3 +271,46 @@ end
@[simp] theorem toList_some (a : α) : (a : Option α).toList = [a] := rfl
@[simp] theorem toList_none (α : Type _) : (none : Option α).toList = [] := rfl
@[simp] theorem or_some : (some a).or o = some a := rfl
@[simp] theorem none_or : none.or o = o := rfl
theorem or_eq_bif : or o o' = bif o.isSome then o else o' := by
cases o <;> rfl
@[simp] theorem isSome_or : (or o o').isSome = (o.isSome || o'.isSome) := by
cases o <;> rfl
@[simp] theorem isNone_or : (or o o').isNone = (o.isNone && o'.isNone) := by
cases o <;> rfl
@[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
cases o <;> simp
theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
cases o₁ <;> cases o₂ <;> rfl
instance : Std.Associative (or (α := α)) := @or_assoc _
@[simp]
theorem or_none : or o none = o := by
cases o <;> rfl
instance : Std.LawfulIdentity (or (α := α)) none where
left_id := @none_or _
right_id := @or_none _
@[simp]
theorem or_self : or o o = o := by
cases o <;> rfl
instance : Std.IdempotentOp (or (α := α)) := @or_self _
theorem or_eq_orElse : or o o' = o.orElse (fun _ => o') := by
cases o <;> rfl
theorem map_or : f <$> or o o' = (f <$> o).or (f <$> o') := by
cases o <;> rfl
theorem map_or' : (or o o').map f = (o.map f).or (o'.map f) := by
cases o <;> rfl

View File

@@ -230,7 +230,7 @@ protected def Int.repr : Int → String
| negSucc m => "-" ++ Nat.repr (succ m)
instance : Repr Int where
reprPrec i _ := i.repr
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
def hexDigitRepr (n : Nat) : String :=
String.singleton <| Nat.digitChar n

View File

@@ -0,0 +1,27 @@
/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
prelude
import Init.Ext
namespace Subtype
universe u
variable {α : Sort u} {p q : α Prop}
@[ext]
protected theorem ext : {a1 a2 : { x // p x }}, (a1 : α) = (a2 : α) a1 = a2
| _, _, _, _, rfl => rfl
@[simp]
protected theorem «forall» {q : { a // p a } Prop} : ( x, q x) a b, q a, b :=
fun h a b h a, b, fun h a, b h a b
@[simp]
protected theorem «exists» {q : { a // p a } Prop} :
(Exists fun x => q x) Exists fun a => Exists fun b => q a, b :=
fun a, b, h a, b, h, fun a, b, h a, b, h
end Subtype

View File

@@ -7,3 +7,4 @@ prelude
import Init.Data.UInt.Basic
import Init.Data.UInt.Log2
import Init.Data.UInt.Lemmas
import Init.Data.UInt.Bitwise

View File

@@ -0,0 +1,24 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.UInt.Basic
import Init.Data.Fin.Bitwise
set_option hygiene false in
macro "declare_bitwise_uint_theorems" typeName:ident : command =>
`(
namespace $typeName
@[simp] protected theorem and_toNat (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := Fin.and_val ..
end $typeName
)
declare_bitwise_uint_theorems UInt8
declare_bitwise_uint_theorems UInt16
declare_bitwise_uint_theorems UInt32
declare_bitwise_uint_theorems UInt64
declare_bitwise_uint_theorems USize

View File

@@ -26,6 +26,8 @@ theorem add_def (a b : $typeName) : a + b = ⟨a.val + b.val⟩ := rfl
| _, _ => rfl
theorem val_eq_of_lt {a : Nat} : a < size ((ofNat a).val : Nat) = a :=
Nat.mod_eq_of_lt
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, val_eq_of_lt h]
theorem le_def {a b : $typeName} : a b a.1 b.1 := .rfl
theorem lt_def {a b : $typeName} : a < b a.1 < b.1 := .rfl
@@ -48,6 +50,7 @@ protected theorem ne_of_lt {a b : $typeName} (h : a < b) : a ≠ b := ne_of_val_
@[simp] protected theorem zero_toNat : (0 : $typeName).toNat = 0 := Nat.zero_mod _
@[simp] protected theorem mod_toNat (a b : $typeName) : (a % b).toNat = a.toNat % b.toNat := Fin.mod_val ..
@[simp] protected theorem div_toNat (a b : $typeName) : (a / b).toNat = a.toNat / b.toNat := Fin.div_val ..
@[simp] protected theorem sub_toNat_of_le (a b : $typeName) : b a (a - b).toNat = a.toNat - b.toNat := Fin.sub_val_of_le
@[simp] protected theorem modn_toNat (a : $typeName) (b : Nat) : (a.modn b).toNat = a.toNat % b := Fin.modn_val ..
protected theorem modn_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % m) < m
| u, h => Fin.modn_lt u h
@@ -55,6 +58,8 @@ open $typeName (modn_lt) in
protected theorem mod_lt (a b : $typeName) (h : 0 < b) : a % b < b := modn_lt _ (by simp [lt_def] at h; exact h)
protected theorem toNat.inj : {a b : $typeName}, a.toNat = b.toNat a = b
| _, _, _, _, rfl => rfl
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.1.2
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := rfl
end $typeName
)

View File

@@ -10,58 +10,39 @@ import Init.RCases
namespace Lean
namespace Parser.Attr
/-- Registers an extensionality theorem.
* When `@[ext]` is applied to a structure, it generates `.ext` and `.ext_iff` theorems and registers
them for the `ext` tactic.
/--
The flag `(iff := false)` prevents `ext` from generating an `ext_iff` lemma.
-/
syntax extIff := atomic("(" &"iff" " := " &"false" ")")
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic.
/--
The flag `(flat := false)` causes `ext` to not flatten parents' fields when generating an `ext` lemma.
-/
syntax extFlat := atomic("(" &"flat" " := " &"false" ")")
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the lemma. Higher-priority lemmas are chosen first, and the default is `1000`.
/--
Registers an extensionality theorem.
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic, and it generates an "`ext_iff`" theorem.
The name of the theorem is from adding the suffix `_iff` to the theorem name.
* When `@[ext]` is applied to a structure, it generates an `.ext` theorem and applies the `@[ext]` attribute to it.
The result is an `.ext` and an `.ext_iff` theorem with the `.ext` theorem registered for the `ext` tactic.
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the `ext` lemma.
Higher-priority lemmas are chosen first, and the default is `1000`.
* The flag `@[ext (iff := false)]` disables generating an `ext_iff` theorem.
* The flag `@[ext (flat := false)]` causes generated structure extensionality theorems to show inherited fields based on their representation,
rather than flattening the parents' fields into the lemma's equality hypotheses.
structures in the generated extensionality theorems. -/
syntax (name := ext) "ext" (" (" &"flat" " := " term ")")? (ppSpace prio)? : attr
-/
syntax (name := ext) "ext" (ppSpace extIff)? (ppSpace extFlat)? (ppSpace prio)? : attr
end Parser.Attr
-- TODO: rename this namespace?
-- Remark: `ext` has scoped syntax, Mathlib may depend on the actual namespace name.
namespace Elab.Tactic.Ext
/--
Creates the type of the extensionality theorem for the given structure,
elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
-/
scoped syntax (name := extType) "ext_type% " term:max ppSpace ident : term
/--
Creates the type of the iff-variant of the extensionality theorem for the given structure,
elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
-/
scoped syntax (name := extIffType) "ext_iff_type% " term:max ppSpace ident : term
/--
`declare_ext_theorems_for A` declares the extensionality theorems for the structure `A`.
These theorems state that two expressions with the structure type are equal if their fields are equal.
-/
syntax (name := declareExtTheoremFor) "declare_ext_theorems_for " ("(" &"flat" " := " term ") ")? ident (ppSpace prio)? : command
macro_rules | `(declare_ext_theorems_for $[(flat := $f)]? $struct:ident $(prio)?) => do
let flat := f.getD (mkIdent `true)
let names Macro.resolveGlobalName struct.getId.eraseMacroScopes
let name match names.filter (·.2.isEmpty) with
| [] => Macro.throwError s!"unknown constant {struct.getId}"
| [(name, _)] => pure name
| _ => Macro.throwError s!"ambiguous name {struct.getId}"
let extName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext"
let extIffName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext_iff"
`(@[ext $(prio)?] protected theorem $extName:ident : ext_type% $flat $struct:ident :=
fun {..} {..} => by intros; subst_eqs; rfl
protected theorem $extIffName:ident : ext_iff_type% $flat $struct:ident :=
fun {..} {..} =>
fun h => by cases h; and_intros <;> rfl,
fun _ => by (repeat cases _ _); subst_eqs; rfl)
/--
Applies extensionality lemmas that are registered with the `@[ext]` attribute.
@@ -96,19 +77,8 @@ macro "ext1" xs:(colGt ppSpace rintroPat)* : tactic =>
end Elab.Tactic.Ext
end Lean
attribute [ext] Prod PProd Sigma PSigma
attribute [ext] funext propext Subtype.eq
@[ext] theorem Prod.ext : {x y : Prod α β} x.fst = y.fst x.snd = y.snd x = y
| _,_, _,_, rfl, rfl => rfl
@[ext] theorem PProd.ext : {x y : PProd α β} x.fst = y.fst x.snd = y.snd x = y
| _,_, _,_, rfl, rfl => rfl
@[ext] theorem Sigma.ext : {x y : Sigma β} x.fst = y.fst HEq x.snd y.snd x = y
| _,_, _,_, rfl, .rfl => rfl
@[ext] theorem PSigma.ext : {x y : PSigma β} x.fst = y.fst HEq x.snd y.snd x = y
| _,_, _,_, rfl, .rfl => rfl
@[ext] protected theorem PUnit.ext (x y : PUnit) : x = y := rfl
protected theorem Unit.ext (x y : Unit) : x = y := rfl

View File

@@ -7,22 +7,57 @@ prelude
import Init.Util
@[never_extract]
private def outOfBounds [Inhabited α] : α :=
def outOfBounds [Inhabited α] : α :=
panic! "index out of bounds"
/--
The class `GetElem coll idx elem valid` implements the `xs[i]` notation.
Given `xs[i]` with `xs : coll` and `i : idx`, Lean looks for an instance of
`GetElem coll idx elem valid` and uses this to infer the type of return
value `elem` and side conditions `valid` required to ensure `xs[i]` yields
a valid value of type `elem`.
theorem outOfBounds_eq_default [Inhabited α] : (outOfBounds : α) = default := rfl
/--
The classes `GetElem` and `GetElem?` implement lookup notation,
specifically `xs[i]`, `xs[i]?`, `xs[i]!`, and `xs[i]'p`.
Both classes are indexed by types `coll`, `idx`, and `elem` which are
the collection, the index, and the element types.
A single collection may support lookups with multiple index
types. The relation `valid` determines when the index is guaranteed to be
valid; lookups of valid indices are guaranteed not to fail.
For example, an instance for arrays looks like
`GetElem (Array α) Nat α (fun xs i => i < xs.size)`. In other words, given an
array `xs` and a natural number `i`, `xs[i]` will return an `α` when `valid xs i`
holds, which is true when `i` is less than the size of the array. `Array`
additionally supports indexing with `USize` instead of `Nat`.
In either case, because the bounds are checked at compile time,
no runtime check is required.
Given `xs[i]` with `xs : coll` and `i : idx`, Lean looks for an instance of
`GetElem coll idx elem valid` and uses this to infer the type of the return
value `elem` and side condition `valid` required to ensure `xs[i]` yields
a valid value of type `elem`. The tactic `get_elem_tactic` is
invoked to prove validity automatically. The `xs[i]'p` notation uses the
proof `p` to satisfy the validity condition.
If the proof `p` is long, it is often easier to place the
proof in the context using `have`, because `get_elem_tactic` tries
`assumption`.
For example, the instance for arrays looks like
`GetElem (Array α) Nat α (fun xs i => i < xs.size)`.
The proof side-condition `valid xs i` is automatically dispatched by the
`get_elem_tactic` tactic, which can be extended by adding more clauses to
`get_elem_tactic_trivial`.
`get_elem_tactic` tactic; this tactic can be extended by adding more clauses to
`get_elem_tactic_trivial` using `macro_rules`.
`xs[i]?` and `xs[i]!` do not impose a proof obligation; the former returns
an `Option elem`, with `none` signalling that the value isn't present, and
the latter returns `elem` but panics if the value isn't there, returning
`default : elem` based on the `Inhabited elem` instance.
These are provided by the `GetElem?` class, for which there is a default instance
generated from a `GetElem` class as long as `valid xs i` is always decidable.
Important instances include:
* `arr[i] : α` where `arr : Array α` and `i : Nat` or `i : USize`: does array
indexing with no runtime bounds check and a proof side goal `i < arr.size`.
* `l[i] : α` where `l : List α` and `i : Nat`: index into a list, with proof
side goal `i < l.length`.
-/
class GetElem (coll : Type u) (idx : Type v) (elem : outParam (Type w))
(valid : outParam (coll idx Prop)) where
@@ -30,33 +65,10 @@ class GetElem (coll : Type u) (idx : Type v) (elem : outParam (Type w))
The syntax `arr[i]` gets the `i`'th element of the collection `arr`. If there
are proof side conditions to the application, they will be automatically
inferred by the `get_elem_tactic` tactic.
The actual behavior of this class is type-dependent, but here are some
important implementations:
* `arr[i] : α` where `arr : Array α` and `i : Nat` or `i : USize`: does array
indexing with no bounds check and a proof side goal `i < arr.size`.
* `l[i] : α` where `l : List α` and `i : Nat`: index into a list, with proof
side goal `i < l.length`.
* `stx[i] : Syntax` where `stx : Syntax` and `i : Nat`: get a syntax argument,
no side goal (returns `.missing` out of range)
There are other variations on this syntax:
* `arr[i]!` is syntax for `getElem! arr i` which should panic and return
`default : α` if the index is not valid.
* `arr[i]?` is syntax for `getElem?` which should return `none` if the index
is not valid.
* `arr[i]'h` is syntax for `getElem arr i h` with `h` an explicit proof the
index is valid.
-/
getElem (xs : coll) (i : idx) (h : valid xs i) : elem
getElem? (xs : coll) (i : idx) [Decidable (valid xs i)] : Option elem :=
if h : _ then some (getElem xs i h) else none
getElem! [Inhabited elem] (xs : coll) (i : idx) [Decidable (valid xs i)] : elem :=
match getElem? xs i with | some e => e | none => outOfBounds
export GetElem (getElem getElem! getElem?)
export GetElem (getElem)
@[inherit_doc getElem]
syntax:max term noWs "[" withoutPosition(term) "]" : term
@@ -66,6 +78,30 @@ macro_rules | `($x[$i]) => `(getElem $x $i (by get_elem_tactic))
syntax term noWs "[" withoutPosition(term) "]'" term:max : term
macro_rules | `($x[$i]'$h) => `(getElem $x $i $h)
/-- Helper function for implementation of `GetElem?.getElem?`. -/
abbrev decidableGetElem? [GetElem coll idx elem valid] (xs : coll) (i : idx) [Decidable (valid xs i)] :
Option elem :=
if h : valid xs i then some xs[i] else none
@[inherit_doc GetElem]
class GetElem? (coll : Type u) (idx : Type v) (elem : outParam (Type w))
(valid : outParam (coll idx Prop)) extends GetElem coll idx elem valid where
/--
The syntax `arr[i]?` gets the `i`'th element of the collection `arr`,
if it is present (and wraps it in `some`), and otherwise returns `none`.
-/
getElem? : coll idx Option elem
/--
The syntax `arr[i]!` gets the `i`'th element of the collection `arr`,
if it is present, and otherwise panics at runtime and returns the `default` term
from `Inhabited elem`.
-/
getElem! [Inhabited elem] (xs : coll) (i : idx) : elem :=
match getElem? xs i with | some e => e | none => outOfBounds
export GetElem? (getElem? getElem!)
/--
The syntax `arr[i]?` gets the `i`'th element of the collection `arr` or
returns `none` if `i` is out of bounds.
@@ -78,32 +114,51 @@ panics `i` is out of bounds.
-/
macro:max x:term noWs "[" i:term "]" noWs "!" : term => `(getElem! $x $i)
instance (priority := low) [GetElem coll idx elem valid] [ xs i, Decidable (valid xs i)] :
GetElem? coll idx elem valid where
getElem? xs i := decidableGetElem? xs i
theorem getElem_congr_coll [GetElem coll idx elem valid] {c d : coll} {i : idx} {h : valid c i}
(h' : c = d) : c[i] = d[i]'(h' h) := by
cases h'; rfl
theorem getElem_congr [GetElem coll idx elem valid] {c : coll} {i j : idx} {h : valid c i}
(h' : i = j) : c[i] = c[j]'(h' h) := by
cases h'; rfl
class LawfulGetElem (cont : Type u) (idx : Type v) (elem : outParam (Type w))
(dom : outParam (cont idx Prop)) [ge : GetElem cont idx elem dom] : Prop where
(dom : outParam (cont idx Prop)) [ge : GetElem? cont idx elem dom] : Prop where
getElem?_def (c : cont) (i : idx) [Decidable (dom c i)] :
c[i]? = if h : dom c i then some (c[i]'h) else none := by intros; eq_refl
getElem!_def [Inhabited elem] (c : cont) (i : idx) [Decidable (dom c i)] :
c[i]! = match c[i]? with | some e => e | none => default := by intros; eq_refl
c[i]? = if h : dom c i then some (c[i]'h) else none := by
intros
try simp only [getElem?] <;> congr
getElem!_def [Inhabited elem] (c : cont) (i : idx) :
c[i]! = match c[i]? with | some e => e | none => default := by
intros
simp only [getElem!, getElem?, outOfBounds_eq_default]
export LawfulGetElem (getElem?_def getElem!_def)
theorem getElem?_pos [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
instance (priority := low) [GetElem coll idx elem valid] [ xs i, Decidable (valid xs i)] :
LawfulGetElem coll idx elem valid where
theorem getElem?_pos [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) (h : dom c i) [Decidable (dom c i)] : c[i]? = some (c[i]'h) := by
rw [getElem?_def]
exact dif_pos h
theorem getElem?_neg [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
theorem getElem?_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) (h : ¬dom c i) [Decidable (dom c i)] : c[i]? = none := by
rw [getElem?_def]
exact dif_neg h
theorem getElem!_pos [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
theorem getElem!_pos [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
[Inhabited elem] (c : cont) (i : idx) (h : dom c i) [Decidable (dom c i)] :
c[i]! = c[i]'h := by
simp only [getElem!_def, getElem?_def, h]
theorem getElem!_neg [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
theorem getElem!_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
[Inhabited elem] (c : cont) (i : idx) (h : ¬dom c i) [Decidable (dom c i)] : c[i]! = default := by
simp only [getElem!_def, getElem?_def, h]
@@ -111,23 +166,22 @@ namespace Fin
instance instGetElemFinVal [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
getElem xs i h := getElem xs i.1 h
instance instGetElem?FinVal [GetElem? cont Nat elem dom] : GetElem? cont (Fin n) elem fun xs i => dom xs i where
getElem? xs i := getElem? xs i.val
getElem! xs i := getElem! xs i.val
instance [GetElem cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
instance [GetElem? cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
LawfulGetElem cont (Fin n) elem fun xs i => dom xs i where
getElem?_def _c _i _d := h.getElem?_def ..
getElem!_def _c _i _d := h.getElem!_def ..
getElem!_def _c _i := h.getElem!_def ..
@[simp] theorem getElem_fin [GetElem Cont Nat Elem Dom] (a : Cont) (i : Fin n) (h : Dom a i) :
@[simp] theorem getElem_fin [GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) (h : Dom a i) :
a[i] = a[i.1] := rfl
@[simp] theorem getElem?_fin [h : GetElem Cont Nat Elem Dom] (a : Cont) (i : Fin n)
[Decidable (Dom a i)] : a[i]? = a[i.1]? := by rfl
@[simp] theorem getElem?_fin [h : GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) : a[i]? = a[i.1]? := by rfl
@[simp] theorem getElem!_fin [GetElem Cont Nat Elem Dom] (a : Cont) (i : Fin n)
[Decidable (Dom a i)] [Inhabited Elem] : a[i]! = a[i.1]! := rfl
@[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)
@@ -139,17 +193,15 @@ namespace List
instance : GetElem (List α) Nat α fun as i => i < as.length where
getElem as i h := as.get i, h
instance : LawfulGetElem (List α) Nat α fun as i => i < as.length where
@[simp] theorem getElem_cons_zero (a : α) (as : List α) (h : 0 < (a :: as).length) : getElem (a :: as) 0 h = a := by
rfl
@[deprecated (since := "2024-6-12")] abbrev cons_getElem_zero := @getElem_cons_zero
@[deprecated (since := "2024-06-12")] abbrev cons_getElem_zero := @getElem_cons_zero
@[simp] theorem getElem_cons_succ (a : α) (as : List α) (i : Nat) (h : i + 1 < (a :: as).length) : getElem (a :: as) (i+1) h = getElem as i (Nat.lt_of_succ_lt_succ h) := by
rfl
@[deprecated (since := "2024-6-12")] abbrev cons_getElem_succ := @getElem_cons_succ
@[deprecated (since := "2024-06-12")] abbrev cons_getElem_succ := @getElem_cons_succ
theorem get_drop_eq_drop (as : List α) (i : Nat) (h : i < as.length) : as[i] :: as.drop (i+1) = as.drop i :=
match as, i with
@@ -163,8 +215,6 @@ namespace Array
instance : GetElem (Array α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem (Array α) Nat α fun xs i => i < xs.size where
end Array
namespace Lean.Syntax
@@ -172,6 +222,4 @@ namespace Lean.Syntax
instance : GetElem Syntax Nat Syntax fun _ _ => True where
getElem stx i _ := stx.getArg i
instance : LawfulGetElem Syntax Nat Syntax fun _ _ => True where
end Lean.Syntax

View File

@@ -399,9 +399,16 @@ def setTailInfo (stx : Syntax) (info : SourceInfo) : Syntax :=
| some stx => stx
| none => stx
/--
Replaces the trailing whitespace in `stx`, if any, with an empty substring.
The trailing substring's `startPos` and `str` are preserved in order to ensure that the result could
have been produced by the parser, in case any syntax consumers rely on such an assumption.
-/
def unsetTrailing (stx : Syntax) : Syntax :=
match stx.getTailInfo with
| SourceInfo.original lead pos _ endPos => stx.setTailInfo (SourceInfo.original lead pos "".toSubstring endPos)
| SourceInfo.original lead pos trail endPos =>
stx.setTailInfo (SourceInfo.original lead pos { trail with stopPos := trail.startPos } endPos)
| _ => stx
@[specialize] private partial def updateFirst {α} [Inhabited α] (a : Array α) (f : α Option α) (i : Nat) : Option (Array α) :=

View File

@@ -218,6 +218,14 @@ structure Config where
to find candidate `simp` theorems. It approximates Lean 3 `simp` behavior.
-/
index : Bool := true
/--
When `true` (default: `true`), `simp` will **not** create a proof for a rewriting rule associated
with an `rfl`-theorem.
Rewriting rules are provided by users by annotating theorems with the attribute `@[simp]`.
If the proof of the theorem is just `rfl` (reflexivity), and `implicitDefEqProofs := true`, `simp`
will **not** create a proof term which is an application of the annotated theorem.
-/
implicitDefEqProofs : Bool := true
deriving Inhabited, BEq
-- Configuration object for `simp_all`

View File

@@ -267,6 +267,7 @@ syntax (name := rawNatLit) "nat_lit " num : term
@[inherit_doc] infixr:90 "" => Function.comp
@[inherit_doc] infixr:35 " × " => Prod
@[inherit_doc] infixr:35 " ×' " => PProd
@[inherit_doc] infix:50 " " => Dvd.dvd
@[inherit_doc] infixl:55 " ||| " => HOr.hOr
@@ -703,6 +704,28 @@ syntax (name := checkSimp) "#check_simp " term "~>" term : command
-/
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
/--
`#discr_tree_key t` prints the discrimination tree keys for a term `t` (or, if it is a single identifier, the type of that constant).
It uses the default configuration for generating keys.
For example,
```
#discr_tree_key (∀ {a n : Nat}, bar a (OfNat.ofNat n))
-- bar _ (@OfNat.ofNat Nat _ _)
#discr_tree_simp_key Nat.add_assoc
-- @HAdd.hAdd Nat Nat Nat _ (@HAdd.hAdd Nat Nat Nat _ _ _) _
```
`#discr_tree_simp_key` is similar to `#discr_tree_key`, but treats the underlying type
as one of a simp lemma, i.e. transforms it into an equality and produces the key of the
left-hand side.
-/
syntax (name := discrTreeKeyCmd) "#discr_tree_key " term : command
@[inherit_doc discrTreeKeyCmd]
syntax (name := discrTreeSimpKeyCmd) "#discr_tree_simp_key" term : command
/--
The `seal foo` command ensures that the definition of `foo` is sealed, meaning it is marked as `[irreducible]`.
This command is particularly useful in contexts where you want to prevent the reduction of `foo` in proofs.

View File

@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Data.List.Lemmas
import Init.Data.List.Zip
import Init.Data.Int.DivModLemmas
import Init.Data.Nat.Gcd

View File

@@ -38,6 +38,10 @@ theorem ext {a b : LinearCombo} (w₁ : a.const = b.const) (w₂ : a.coeffs = b.
subst w₁; subst w₂
congr
/-- Check if a linear combination is an atom, i.e. the constant term is zero and there is exactly one nonzero coefficient, which is one. -/
def isAtom (a : LinearCombo) : Bool :=
a.const == 0 && (a.coeffs.filter (· == 1)).length == 1 && a.coeffs.all fun c => c == 0 || c == 1
/--
Evaluate a linear combination `⟨r, [c_1, …, c_k]⟩` at values `[v_1, …, v_k]` to obtain
`r + (c_1 * x_1 + (c_2 * x_2 + ... (c_k * x_k + 0))))`.

View File

@@ -320,7 +320,7 @@ Because this is in the `Eq` namespace, if you have a variable `h : a = b`,
For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality)
-/
theorem Eq.symm {α : Sort u} {a b : α} (h : Eq a b) : Eq b a :=
@[symm] theorem Eq.symm {α : Sort u} {a b : α} (h : Eq a b) : Eq b a :=
h rfl
/--
@@ -488,9 +488,9 @@ attribute [unbox] Prod
/--
Similar to `Prod`, but `α` and `β` can be propositions.
You can use `α ×' β` as notation for `PProd α β`.
We use this type internally to automatically generate the `brecOn` recursor.
-/
@[pp_using_anonymous_constructor]
structure PProd (α : Sort u) (β : Sort v) where
/-- The first projection out of a pair. if `p : PProd α β` then `p.1 : α`. -/
fst : α
@@ -2214,12 +2214,17 @@ def Char.utf8Size (c : Char) : Nat :=
or `none`. In functional programming languages, this type is used to represent
the possibility of failure, or sometimes nullability.
For example, the function `HashMap.find? : HashMap α β → α → Option β` looks up
For example, the function `HashMap.get? : HashMap α β → α → Option β` looks up
a specified key `a : α` inside the map. Because we do not know in advance
whether the key is actually in the map, the return type is `Option β`, where
`none` means the value was not in the map, and `some b` means that the value
was found and `b` is the value retrieved.
The `xs[i]` syntax, which is used to index into collections, has a variant
`xs[i]?` that returns an optional value depending on whether the given index
is valid. For example, if `m : HashMap α β` and `a : α`, then `m[a]?` is
equivalent to `HashMap.get? m a`.
To extract a value from an `Option α`, we use pattern matching:
```
def map (f : α → β) (x : Option α) : Option β :=
@@ -3172,8 +3177,8 @@ class MonadStateOf (σ : semiOutParam (Type u)) (m : Type u → Type v) where
export MonadStateOf (set)
/--
Like `withReader`, but with `ρ` explicit. This is useful if a monad supports
`MonadWithReaderOf` for multiple different types `ρ`.
Like `get`, but with `σ` explicit. This is useful if a monad supports
`MonadStateOf` for multiple different types `σ`.
-/
abbrev getThe (σ : Type u) {m : Type u Type v} [MonadStateOf σ m] : m σ :=
MonadStateOf.get

View File

@@ -202,6 +202,17 @@ theorem exists_imp : ((∃ x, p x) → b) ↔ ∀ x, p x → b := forall_exists_
@[simp] theorem exists_const (α) [i : Nonempty α] : ( _ : α, b) b :=
fun _, h => h, i.elim Exists.intro
@[congr]
theorem exists_prop_congr {p p' : Prop} {q q' : p Prop} (hq : h, q h q' h) (hp : p p') :
Exists q h : p', q' (hp.2 h) :=
fun _, _ hp.1 _, (hq _).1 _, fun _, _ _, (hq _).2 _
theorem exists_prop_of_true {p : Prop} {q : p Prop} (h : p) : (Exists fun h' : p => q h') q h :=
@exists_const (q h) p h
@[simp] theorem exists_true_left (p : True Prop) : Exists p p True.intro :=
exists_prop_of_true _
section forall_congr
theorem forall_congr' (h : a, p a q a) : ( a, p a) a, q a :=
@@ -253,6 +264,9 @@ end forall_congr
@[simp] theorem not_exists : (¬ x, p x) x, ¬p x := exists_imp
theorem forall_not_of_not_exists (h : ¬ x, p x) : x, ¬p x := not_exists.mp h
theorem not_exists_of_forall_not (h : x, ¬p x) : ¬ x, p x := not_exists.mpr h
theorem forall_and : ( x, p x q x) ( x, p x) ( x, q x) :=
fun h => fun x => (h x).1, fun x => (h x).2, fun h₁, h₂ x => h₁ x, h₂ x
@@ -292,6 +306,8 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
@[simp] theorem exists_eq_left' : ( a, a' = a p a) p a' := by simp [@eq_comm _ a']
@[simp] theorem exists_eq_right' : ( a, p a a' = a) p a' := by simp [@eq_comm _ a']
@[simp] theorem forall_eq_or_imp : ( a, a = a' q a p a) p a' a, q a p a := by
simp only [or_imp, forall_and, forall_eq]
@@ -304,6 +320,11 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
@[simp] theorem exists_eq_right_right' : ( (a : α), p a q a a' = a) p a' q a' := by
simp [@eq_comm _ a']
@[simp] theorem exists_or_eq_left (y : α) (p : α Prop) : x : α, x = y p x := y, .inl rfl
@[simp] theorem exists_or_eq_right (y : α) (p : α Prop) : x : α, p x x = y := y, .inr rfl
@[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 : ( _h : a, b) a b :=
fun hp, hq => hp, hq, fun hp, hq => hp, hq
@@ -368,9 +389,6 @@ 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_false_iff_not (p : Prop) {_ : Decidable p} : (decide p = false) ¬p :=
of_decide_eq_false, decide_eq_false
@[simp] 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]

View File

@@ -102,3 +102,11 @@ instance ShareCommonT.monadShareCommon [Monad m] : MonadShareCommon (ShareCommon
@[inline] def ShareCommonT.run [Monad m] (x : ShareCommonT σ m α) : m α := x.run' default
@[inline] def ShareCommonM.run (x : ShareCommonM σ α) : α := ShareCommonT.run x
/--
A more restrictive but efficient max sharing primitive.
Remark: it optimizes the number of RC operations, and the strategy for caching results.
-/
@[extern "lean_sharecommon_quick"]
def ShareCommon.shareCommon' (a : α) : α := a

View File

@@ -129,6 +129,7 @@ instance : Std.LawfulIdentity Or False where
@[simp] theorem iff_false (p : Prop) : (p False) = ¬p := propext (·.1), (·, False.elim)
@[simp] theorem false_iff (p : Prop) : (False p) = ¬p := propext (·.2), (False.elim, ·)
@[simp] theorem false_implies (p : Prop) : (False p) = True := eq_true False.elim
@[simp] theorem forall_false (p : False Prop) : ( h : False, p h) = True := eq_true (False.elim ·)
@[simp] theorem implies_true (α : Sort u) : (α True) = True := eq_true fun _ => trivial
@[simp] theorem true_implies (p : Prop) : (True p) = p := propext (· trivial), (fun _ => ·)
@[simp] theorem not_false_eq_true : (¬ False) = True := eq_true False.elim
@@ -228,25 +229,22 @@ instance : Std.Associative (· || ·) := ⟨Bool.or_assoc⟩
@[simp] theorem Bool.not_not (b : Bool) : (!!b) = b := by cases b <;> rfl
@[simp] theorem Bool.not_true : (!true) = false := by decide
@[simp] theorem Bool.not_false : (!false) = true := by decide
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
@[simp] theorem Bool.not_beq_false (b : Bool) : (!(b == false)) = (b == true) := by cases b <;> rfl
@[simp] theorem beq_true (b : Bool) : (b == true) = b := by cases b <;> rfl
@[simp] theorem beq_false (b : Bool) : (b == false) = !b := by cases b <;> rfl
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
@[simp] theorem Bool.not_eq_false' (b : Bool) : ((!b) = false) = (b = true) := by cases b <;> simp
@[simp] theorem Bool.beq_to_eq (a b : Bool) :
(a == b) = (a = b) := by cases a <;> cases b <;> decide
@[simp] theorem Bool.not_beq_to_not_eq (a b : Bool) :
(!(a == b)) = ¬(a = b) := by cases a <;> cases b <;> decide
@[simp] theorem Bool.not_eq_true (b : Bool) : (¬(b = true)) = (b = false) := by cases b <;> decide
@[simp] theorem Bool.not_eq_false (b : Bool) : (¬(b = false)) = (b = true) := by cases b <;> decide
@[simp] theorem decide_eq_true_eq [Decidable p] : (decide p = true) = p :=
propext <| Iff.intro of_decide_eq_true decide_eq_true
@[simp] theorem decide_eq_false_iff_not {_ : Decidable p} : (decide p = false) ¬p :=
of_decide_eq_false, decide_eq_false
@[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
cases h <;> (rename_i hp; simp [decide, hp])
@[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
@@ -254,10 +252,10 @@ instance : Std.Associative (· || ·) := ⟨Bool.or_assoc⟩
@[simp] theorem cond_false (a b : α) : cond false a b = b := rfl
@[simp] theorem beq_self_eq_true [BEq α] [LawfulBEq α] (a : α) : (a == a) = true := LawfulBEq.rfl
@[simp] theorem beq_self_eq_true' [DecidableEq α] (a : α) : (a == a) = true := by simp [BEq.beq]
theorem beq_self_eq_true' [DecidableEq α] (a : α) : (a == a) = true := by simp
@[simp] theorem bne_self_eq_false [BEq α] [LawfulBEq α] (a : α) : (a != a) = false := by simp [bne]
@[simp] theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp [bne]
theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp
@[simp] theorem decide_False : decide False = false := rfl
@[simp] theorem decide_True : decide True = true := rfl
@@ -283,7 +281,10 @@ These will both normalize to `a = b` with the first via `bne_eq_false_iff_eq`.
rw [bne, beq_iff_eq a b]
cases a == b <;> decide
/-# Nat -/
theorem Bool.beq_to_eq (a b : Bool) : (a == b) = (a = b) := by simp
theorem Bool.not_beq_to_not_eq (a b : Bool) : (!(a == b)) = ¬(a = b) := by simp
/- # Nat -/
@[simp] theorem Nat.le_zero_eq (a : Nat) : (a 0) = (a = 0) :=
propext fun h => Nat.le_antisymm h (Nat.zero_le ..), fun h => by rw [h]; decide

View File

@@ -712,8 +712,17 @@ structure Child (cfg : StdioConfig) where
@[extern "lean_io_process_spawn"] opaque spawn (args : SpawnArgs) : IO (Child args.toStdioConfig)
/--
Block until the child process has exited and return its exit code.
-/
@[extern "lean_io_process_child_wait"] opaque Child.wait {cfg : @& StdioConfig} : @& Child cfg IO UInt32
/--
Check whether the child has exited yet. If it hasn't return none, otherwise its exit code.
-/
@[extern "lean_io_process_child_try_wait"] opaque Child.tryWait {cfg : @& StdioConfig} : @& Child cfg
IO (Option UInt32)
/-- Terminates the child process using the SIGTERM signal or a platform analogue.
If the process was started using `SpawnArgs.setsid`, terminates the entire process group instead. -/
@[extern "lean_io_process_child_kill"] opaque Child.kill {cfg : @& StdioConfig} : @& Child cfg IO Unit
@@ -814,6 +823,10 @@ def set (tk : CancelToken) : BaseIO Unit :=
def isSet (tk : CancelToken) : BaseIO Bool :=
tk.ref.get
-- separate definition as otherwise no unboxed version is generated
@[export lean_io_cancel_token_is_set]
private def isSetExport := @isSet
end CancelToken
namespace FS

View File

@@ -45,6 +45,13 @@ def dbgSleep {α : Type u} (ms : UInt32) (f : Unit → α) : α := f ()
@[extern "lean_ptr_addr"]
unsafe opaque ptrAddrUnsafe {α : Type u} (a : @& α) : USize
/--
Returns `true` if `a` is an exclusive object.
We say an object is exclusive if it is single-threaded and its reference counter is 1.
-/
@[extern "lean_is_exclusive_obj"]
unsafe opaque isExclusiveUnsafe {α : Type u} (a : @& α) : Bool
set_option linter.unusedVariables.funArgs false in
@[inline] unsafe def withPtrAddrUnsafe {α : Type u} {β : Type v} (a : α) (k : USize β) (h : u₁ u₂, k u₁ = k u₂) : β :=
k (ptrAddrUnsafe a)

View File

@@ -148,22 +148,26 @@ end InvImage
wf := InvImage.wf f h.wf
-- The transitive closure of a well-founded relation is well-founded
namespace TC
variable {α : Sort u} {r : α α Prop}
open Relation
theorem accessible {z : α} (ac : Acc r z) : Acc (TC r) z := by
induction ac with
| intro x acx ih =>
apply Acc.intro x
intro y rel
induction rel with
| base a b rab => exact ih a rab
| trans a b c rab _ _ ih₂ => apply Acc.inv (ih₂ acx ih) rab
theorem Acc.transGen (h : Acc r a) : Acc (TransGen r) a := by
induction h with
| intro x _ H =>
refine Acc.intro x fun y hy ?_
cases hy with
| single hyx =>
exact H y hyx
| tail hyz hzx =>
exact (H _ hzx).inv hyz
theorem wf (h : WellFounded r) : WellFounded (TC r) :=
fun a => accessible (apply h a)
end TC
theorem acc_transGen_iff : Acc (TransGen r) a Acc r a :=
Subrelation.accessible TransGen.single, Acc.transGen
theorem WellFounded.transGen (h : WellFounded r) : WellFounded (TransGen r) :=
fun a (h.apply a).transGen
@[deprecated Acc.transGen (since := "2024-07-16")] abbrev TC.accessible := @Acc.transGen
@[deprecated WellFounded.transGen (since := "2024-07-16")] abbrev TC.wf := @WellFounded.transGen
namespace Nat
-- less-than is well-founded
@@ -288,7 +292,7 @@ instance [ha : WellFoundedRelation α] [hb : WellFoundedRelation β] : WellFound
lex ha hb
-- relational product is a Subrelation of the Lex
def RProdSubLex (a : α × β) (b : α × β) (h : RProd ra rb a b) : Prod.Lex ra rb a b := by
theorem RProdSubLex (a : α × β) (b : α × β) (h : RProd ra rb a b) : Prod.Lex ra rb a b := by
cases h with
| intro h₁ h₂ => exact Prod.Lex.left _ _ h₁
@@ -320,7 +324,7 @@ section
variable {α : Sort u} {β : α Sort v}
variable {r : α α Prop} {s : (a : α), β a β a Prop}
def lexAccessible {a} (aca : Acc r a) (acb : (a : α) WellFounded (s a)) (b : β a) : Acc (Lex r s) a, b := by
theorem lexAccessible {a} (aca : Acc r a) (acb : (a : α) WellFounded (s a)) (b : β a) : Acc (Lex r s) a, b := by
induction aca with
| intro xa _ iha =>
induction (WellFounded.apply (acb xa) b) with

View File

@@ -8,11 +8,22 @@ import Lean.CoreM
namespace Lean
def Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration) : Except KernelException Environment :=
addDeclCore env (Core.getMaxHeartbeats opts).toUSize decl
register_builtin_option debug.skipKernelTC : Bool := {
defValue := false
group := "debug"
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
}
def Environment.addAndCompile (env : Environment) (opts : Options) (decl : Declaration) : Except KernelException Environment := do
let env addDecl env opts decl
def Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
(cancelTk? : Option IO.CancelToken := none) : Except KernelException Environment :=
if debug.skipKernelTC.get opts then
addDeclWithoutChecking env decl
else
addDeclCore env (Core.getMaxHeartbeats opts).toUSize decl cancelTk?
def Environment.addAndCompile (env : Environment) (opts : Options) (decl : Declaration)
(cancelTk? : Option IO.CancelToken := none) : Except KernelException Environment := do
let env addDecl env opts decl cancelTk?
compileDecl env opts decl
def addDecl (decl : Declaration) : CoreM Unit := do
@@ -20,7 +31,7 @@ def addDecl (decl : Declaration) : CoreM Unit := do
withTraceNode `Kernel (fun _ => return m!"typechecking declaration") do
if !( MonadLog.hasErrors) && decl.hasSorry then
logWarning "declaration uses 'sorry'"
match ( getEnv).addDecl ( getOptions) decl with
match ( getEnv).addDecl ( getOptions) decl ( read).cancelTk? with
| .ok env => setEnv env
| .error ex => throwKernelException ex

View File

@@ -37,7 +37,7 @@ def isAuxRecursor (env : Environment) (declName : Name) : Bool :=
def isAuxRecursorWithSuffix (env : Environment) (declName : Name) (suffix : String) : Bool :=
match declName with
| .str _ s => s == suffix && isAuxRecursor env declName
| .str _ s => (s == suffix || s.startsWith s!"{suffix}_") && isAuxRecursor env declName
| _ => false
def isCasesOnRecursor (env : Environment) (declName : Name) : Bool :=

View File

@@ -94,7 +94,7 @@ def emitCInitName (n : Name) : M Unit :=
def shouldExport (n : Name) : Bool :=
-- HACK: exclude symbols very unlikely to be used by the interpreter or other consumers of
-- libleanshared to avoid Windows symbol limit
!(`Lean.Compiler.LCNF).isPrefixOf n
!(`Lean.Compiler.LCNF).isPrefixOf n && !(`Lean.IR).isPrefixOf n && !(`Lean.Server).isPrefixOf n
def emitFnDeclAux (decl : Decl) (cppBaseName : String) (isExternal : Bool) : M Unit := do
let ps := decl.params

View File

@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Compiler.Options
import Lean.Compiler.ExternAttr
import Lean.Compiler.LCNF.PassManager
import Lean.Compiler.LCNF.Passes
import Lean.Compiler.LCNF.PrettyPrinter

View File

@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.PrettyPrinter
import Lean.PrettyPrinter.Delaborator.Options
import Lean.Compiler.LCNF.CompilerM
import Lean.Compiler.LCNF.Internalize

View File

@@ -211,12 +211,12 @@ instance : MonadTrace CoreM where
structure SavedState extends State where
/-- Number of heartbeats passed inside `withRestoreOrSaveFull`, not used otherwise. -/
passedHearbeats : Nat
passedHeartbeats : Nat
deriving Nonempty
def saveState : CoreM SavedState := do
let s get
return { toState := s, passedHearbeats := 0 }
return { toState := s, passedHeartbeats := 0 }
/--
Incremental reuse primitive: if `reusableResult?` is `none`, runs `act` and returns its result
@@ -236,14 +236,14 @@ itself after calling `act` as well as by reuse-handling code such as the one sup
(act : CoreM α) : CoreM (α × SavedState) := do
if let some (val, state) := reusableResult? then
set state.toState
IO.addHeartbeats state.passedHearbeats.toUInt64
IO.addHeartbeats state.passedHeartbeats.toUInt64
return (val, state)
let startHeartbeats IO.getNumHeartbeats
let a act
let s get
let stopHeartbeats IO.getNumHeartbeats
return (a, { toState := s, passedHearbeats := stopHeartbeats - startHeartbeats })
return (a, { toState := s, passedHeartbeats := stopHeartbeats - startHeartbeats })
/-- Restore backtrackable parts of the state. -/
def SavedState.restore (b : SavedState) : CoreM Unit :=
@@ -472,23 +472,30 @@ def Exception.isInterrupt : Exception → Bool
/--
Custom `try-catch` for all monads based on `CoreM`. We usually don't want to catch "runtime
exceptions" these monads, but on `CommandElabM`. See issues #2775 and #2744 as well as
`MonadAlwaysExcept`. Also, we never want to catch interrupt exceptions inside the elaborator.
exceptions" these monads, but on `CommandElabM` or, in specific cases, using `tryCatchRuntimeEx`.
See issues #2775 and #2744 as well as `MonadAlwaysExcept`. Also, we never want to catch interrupt
exceptions inside the elaborator.
-/
@[inline] protected def Core.tryCatch (x : CoreM α) (h : Exception CoreM α) : CoreM α := do
try
x
catch ex =>
if ex.isInterrupt || ex.isRuntime then
throw ex -- We should use `tryCatchRuntimeEx` for catching runtime exceptions
throw ex
else
h ex
/--
A variant of `tryCatch` that also catches runtime exception (see also `tryCatch` documentation).
Like `tryCatch`, this function does not catch interrupt exceptions, which are not considered runtime
exceptions.
-/
@[inline] protected def Core.tryCatchRuntimeEx (x : CoreM α) (h : Exception CoreM α) : CoreM α := do
try
x
catch ex =>
if ex.isInterrupt then
throw ex
h ex
instance : MonadExceptOf Exception CoreM where
@@ -512,4 +519,16 @@ instance : MonadRuntimeException CoreM where
@[inline] def mapCoreM [MonadControlT CoreM m] [Monad m] (f : forall {α}, CoreM α CoreM α) {α} (x : m α) : m α :=
controlAt CoreM fun runInBase => f <| runInBase x
/--
Returns `true` if the given message kind has not been reported in the message log,
and then mark it as reported. Otherwise, returns `false`.
We use this API to ensure we don't report the same kind of warning multiple times.
-/
def reportMessageKind (kind : Name) : CoreM Bool := do
if ( get).messages.reportedKinds.contains kind then
return false
else
modify fun s => { s with messages.reportedKinds := s.messages.reportedKinds.insert kind }
return true
end Lean

View File

@@ -223,8 +223,6 @@ def insertIfNew (m : HashMap α β) (a : α) (b : β) : HashMap α β × Option
instance : GetElem (HashMap α β) α (Option β) fun _ _ => True where
getElem m k _ := m.find? k
instance : LawfulGetElem (HashMap α β) α (Option β) fun _ _ => True where
@[inline] def contains (m : HashMap α β) (a : α) : Bool :=
match m with
| m, _ => m.contains a

View File

@@ -72,8 +72,6 @@ def get! [Inhabited α] (t : PersistentArray α) (i : Nat) : α :=
instance [Inhabited α] : GetElem (PersistentArray α) Nat α fun as i => i < as.size where
getElem xs i _ := xs.get! i
instance [Inhabited α] : LawfulGetElem (PersistentArray α) Nat α fun as i => i < as.size where
partial def setAux : PersistentArrayNode α USize USize α PersistentArrayNode α
| node cs, i, shift, a =>
let j := div2Shift i shift

View File

@@ -161,8 +161,6 @@ def find? {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β → α → Op
instance {_ : BEq α} {_ : Hashable α} : GetElem (PersistentHashMap α β) α (Option β) fun _ _ => True where
getElem m i _ := m.find? i
instance {_ : BEq α} {_ : Hashable α} : LawfulGetElem (PersistentHashMap α β) α (Option β) fun _ _ => True where
@[inline] def findD {_ : BEq α} {_ : Hashable α} (m : PersistentHashMap α β) (a : α) (b₀ : β) : β :=
(m.find? a).getD b₀

View File

@@ -80,6 +80,10 @@ protected def max : RBNode α β → Option (Sigma (fun k => β k))
def singleton (k : α) (v : β k) : RBNode α β :=
node red leaf k v leaf
def isSingleton : RBNode α β Bool
| node _ leaf _ _ leaf => true
| _ => false
-- the first half of Okasaki's `balance`, concerning red-red sequences in the left child
@[inline] def balance1 : RBNode α β (a : α) β a RBNode α β RBNode α β
| node red (node red a kx vx b) ky vy c, kz, vz, d
@@ -269,6 +273,9 @@ variable {α : Type u} {β : Type v} {σ : Type w} {cmp : αα → Ordering
def depth (f : Nat Nat Nat) (t : RBMap α β cmp) : Nat :=
t.val.depth f
def isSingleton (t : RBMap α β cmp) : Bool :=
t.val.isSingleton
@[inline] def fold (f : σ α β σ) : (init : σ) RBMap α β cmp σ
| b, t, _ => t.fold f b

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