Compare commits

...

765 Commits

Author SHA1 Message Date
Kim Morrison
2513be6a09 feat: HashSet.ofArray (unverified) 2024-09-17 16:42:31 +10:00
Kim Morrison
c25d206647 chore: Fin.ofNat' uses NeZero (#5356) 2024-09-16 07:13:18 +00:00
Violeta Hernández
078e9b6d77 doc: add documentation for groupBy.loop (#5349)
We add some documentation explaining the auxiliary function in the
definition of `groupBy`. This has been moved here from Mathlib PR
[16818](https://github.com/leanprover-community/mathlib4/pull/16818) by
request of @semorrison.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-09-16 05:56:44 +00:00
Kim Morrison
a745e33123 feat: BitVec.truncate lemmas (#5357)
These improve confluence of lemmas involving `truncate`.
2024-09-16 05:55:50 +00:00
Kim Morrison
7740a38a71 chore: remove @[simp] from Option.bind_map (#5354) 2024-09-16 04:44:38 +00:00
Kim Morrison
9568f305d8 chore: switch primes on List.getElem_take (#5294)
This will probably have fallout downstream, and as it is a direct name
switch I'm not going to provide any deprecations.
2024-09-16 03:40:42 +00:00
Kim Morrison
b1179d5cc3 chore: fix implicitness of List.getElem_mem (#5331) 2024-09-16 03:28:14 +00:00
Kim Morrison
e6145a6937 feat: simp lemmas for LawfulBEq (#5355) 2024-09-16 03:21:30 +00:00
Kim Morrison
d47ae99721 feat: List.head_mem_head? (#5353) 2024-09-16 03:05:17 +00:00
Kim Morrison
0aac83fe40 feat: List.attachWith lemmas (#5352) 2024-09-16 02:24:14 +00:00
Kim Morrison
8c6ac845b1 chore: cleanup after export Bool.and/or/not/xor 2024-09-16 12:45:51 +10:00
Kim Morrison
b714a96034 chore: update stage0 2024-09-16 12:45:51 +10:00
Kim Morrison
4e0f6b8b45 feat: export Bool.and/or/not/xor 2024-09-16 12:45:51 +10:00
Kim Morrison
979c5a4d6a chore: update stage0 2024-09-16 12:45:51 +10:00
Kim Morrison
2079bdcbca feat: deprecate _root_.or/and/not/xor 2024-09-16 12:45:51 +10:00
Kim Morrison
1a2217d47e feat: cleanup of List.getElem_append variants (#5303) 2024-09-16 02:01:37 +00:00
Kim Morrison
3ef67c468a feat: List.replicate lemmas (#5350) 2024-09-15 23:57:04 +00:00
Joachim Breitner
4c439c73a7 test: tracing and test case for #5347 (#5348)
not a fix, unfortunately, just recording the test.
2024-09-15 15:45:39 +00:00
thorimur
5eea8355ba fix: set check level correctly during workflow (#5344)
Fixes a workflow bug where the `check-level` was not always set
correctly. Arguments to a `gh` call used to determine the `check_level`
were accidentally outside of the relevant command substitution (`$(gh
...)`).

-----

This can be observed in [these
logs](https://github.com/leanprover/lean4/actions/runs/10859763037/job/30139540920),
where the check level (shown first under "configure build matrix") is
`2`, but the PR does not have the `release-ci` tag. As a "test", run the
script for "set check level" printed in those logs (with some lines
omitted):
```
check_level=0

labels="$(gh api repos/leanprover/lean4/pulls/5343) --jq '.labels'"
if echo "$labels" | grep -q "release-ci"; then
  check_level=2
elif echo "$labels" | grep -q "merge-ci"; then
  check_level=1
fi

echo "check_level=$check_level"
```
Note that this prints `check_level=2`, but changing `labels` to
`labels="$(gh api repos/leanprover/lean4/pulls/5343 --jq '.labels')"`
prints `check_level=0`.
2024-09-14 08:14:08 +00:00
thorimur
60bb451d45 feat: allow addition of release-ci label via comment (#5343)
Updates the PR labeling workflow to allow an external contributor to add
the `release-ci` label to their own PR via comment. This is allows users
on Windows and Intel-based macs to generate toolchains for local
testing. The pull request template is also updated to reflect this.

-----

See Zulip discussion
[here](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/No.20binary.20for.20lean.20PR.20testing.20locally).
2024-09-14 08:13:48 +00:00
Marc Huisinga
f989520d2b fix: invalid namespace completions (#5322)
This PR fixes an issue reported a while ago at
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.60Monad.2Emap.60.20is.20a.20namespace.3F/near/425662846
where `Monad.map` was incorrectly reported by the autocompletion as a
namespace.

The underlying issue is that `Monad.map` contains an internal
declaration `_default`. This PR ensures that no namespaces are
registered that only contain internal declarations.

This also means that `open`ing namespaces that only contain internal
declarations will now fail.

The Mathlib adaption for this is a minor change where a declaration
(i.e. a namespace that only contains internal declarations) was `open`ed
by accident.
2024-09-13 12:23:03 +00:00
Jeremy Tan Jie Rui
626dda9358 refactor: tag Iff.refl with @[refl] (#5329)
and remove `exact Off.rfl` from the `rfl` macro.


This upstreams a property found in
[`Mathlib.Init.Logic`](4e40837aec/Mathlib/Init/Logic.lean (L63)).
2024-09-13 11:55:36 +00:00
Sebastian Ullrich
5f789e63fa chore: remove confusing test 2024-09-13 13:04:57 +02:00
Sebastian Ullrich
438061a924 fix: inaccessible pattern vars reported as binders (#5337)
Fixes an unused variable false positive on some wildcard patterns

Fixes #1633, fixes #2830
2024-09-13 09:53:58 +00:00
Mario Carneiro
ec98c92ba6 feat: @[builtin_doc] attribute (part 2) (#3918)
This solves the issue where certain subexpressions are lacking syntax
hovers because the hover text is not "builtin" - it only shows up if the
`Parser` constant is imported in the environment. For top level syntaxes
this is not a problem because `builtin_term_parser` will automatically
add this doc information, but nested syntaxes don't get the same
treatment.

We could walk the expression and add builtin docs recursively, but this
is somewhat expensive and unnecessary given that it's a fixed list of
declarations in lean core. Moreover, there are reasons to want to
control which syntax nodes actually get hovers, and while a better
system for that is forthcoming, for now it can be achieved by
strategically not applying the `@[builtin_doc]` attribute.

Fixes #3842
2024-09-13 08:05:10 +00:00
Henrik Böving
2080fc0221 feat: (DHashMap|HashMap|HashSet).(getKey?|getKey|getKey!|getKeyD) (#5244) 2024-09-13 05:40:10 +00:00
Marc Huisinga
b34379554d feat: completion fallback (#5299)
When the elaborator doesn't provide us with any `CompletionInfo`, we
currently provide no completions whatsoever. But in many cases, we can
still provide some helpful identifier completions without elaborator
information. This PR adds a fallback mode for this situation.

There is more potential here, but this should be a good start.

In principle, this issue alleviates #5172 (since we now provide
completions in these contexts). I'll leave it up to an elaboration
maintainer whether we also want to ensure that the completion infos are
provided correctly in these cases.
2024-09-12 16:09:20 +00:00
Siddharth
273b7540b2 feat: toNat_sub_of_le (#5314)
This adds a simplification lemma for `(x - y).toNat` when the
subtraction is known to not overflow (i.e., `y ≤ x`).

We make a new section for this for two reasons:
1. Definitions of subtraction occur before the definition of
`BitVec.le_def`, so we cannot directly place this lemma at `sub`.
2. There are other theorems of this kind, for addition and
multiplication, which can morally live in the same section.
2024-09-12 13:19:39 +00:00
Lars - he/him
b875627198 feat: add ediv_nonneg_of_nonpos_of_nonpos to DivModLemmas (#5320)
The theorem 

```lean
namespace Int

theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a ≤ 0) (Hb : b ≤ 0) : 0 ≤ a / b := by
  match a, b with
  | ofNat a, b =>
    match Int.le_antisymm Ha (ofNat_zero_le a) with
    | h1 =>
    rw [h1, zero_ediv,]
    exact Int.le_refl 0
  | a, ofNat b =>
    match Int.le_antisymm Hb (ofNat_zero_le  b) with
    | h1 =>
    rw [h1, Int.ediv_zero]
    exact Int.le_refl 0
  | negSucc a, negSucc b =>
    rw [Int.div_def, ediv]
    have le_succ {a: Int} : a ≤ a+1 := (le_add_one (Int.le_refl a))
    have h2: 0 ≤ ((↑b:Int) + 1) := Int.le_trans (ofNat_zero_le b) le_succ
    have h3: (0:Int) ≤ ↑a / (↑b + 1) := (ediv_nonneg (ofNat_zero_le a) h2)
    exact Int.le_trans h3 le_succ
```
is nontrivial to prove from existing theorems and would be nice to add
as standard theorem in DivModLemmas.

See the zullip conversation
[here](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Adding.20theorem.20theorem.20ediv_nonneg'.20for.20negative.20a.20and.20b)

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-09-12 11:26:20 +00:00
Kim Morrison
adfd6c090e chore: add Nat.self_sub_mod lemma (#5306) 2024-09-12 03:36:50 +00:00
Kim Morrison
da0d309d65 feat: provide mergeSort comparator autoParam (#5302)
Write `mergeSort xs ys cmp` to provide an explicit comparator, or
otherwise `mergeSort xs ys` falls back to `LE` and `DecidablePred` via
an autoparam.
2024-09-12 01:50:01 +00:00
Kim Morrison
87fdd7809f feat: List.tail lemma (#5316) 2024-09-12 01:09:57 +00:00
Henrik Böving
8fd6e46a9c feat: more basic BitVec ordering theory for UInt (#5313) 2024-09-11 18:16:21 +00:00
Sebastian Ullrich
0602b805c8 fix: changing whitespace after module header may break subsequent commands (#5312)
`with` considered harmful when merging old and new state, let's always
be explicit in these cases
2024-09-11 13:00:42 +00:00
Kim Morrison
0b7debe376 chore: fix List.countP lemmas (#5311) 2024-09-11 10:09:37 +00:00
Kim Morrison
f5146c6edb chore: fix List.all/any lemmas (#5310) 2024-09-11 10:02:47 +00:00
Kim Morrison
461283ecf4 chore: restoring Option simp confluence (#5307) 2024-09-11 06:52:31 +00:00
Kim Morrison
27bf7367ca chore: rename Nat bitwise lemmas (#5305) 2024-09-11 06:29:00 +00:00
Kim Morrison
d4cc934149 chore: rename Int.div/mod to tdiv/tmod (#5301)
From the new doc-string:
```quote
In early versions of Lean, the typeclasses provided by `/` and `%`
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.

However we decided it was better to use `ediv` and `emod`,
as they are consistent with the conventions used in SMTLib, Mathlib,
and often mathematical reasoning is easier with these conventions.

At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
In September 2024, we decided to do this rename (with deprecations in place),
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
ever need to use these functions and their associated lemmas.
```
2024-09-11 06:15:44 +00:00
Kim Morrison
b88cdf6a3e chore: Array.not_mem_empty (#5304) 2024-09-11 06:13:24 +00:00
Kim Morrison
325a058893 feat: more List.findIdx theorems (#5300) 2024-09-11 04:53:59 +00:00
Henrik Böving
f869018447 feat: BitVec unsigned order theoretic results (#5297)
Proves that `<` and `<=` on `BitVec` are (strict) (total) partial
orders. This is required for the `UInt` as `BitVec` refactor.

This does open the question how to state these theorems "correctly" for
`BitVec`, we have both `<` living in `Prop` and `BitVec.ult` living in
`Bool`. We might of course say to always use `<` but: Once we start
adding `IntX` we need to prove the same results for `BitVec.slt` to
provide an equivalent API. So it would appear that it is unavoidable to
have a `= true` variant of these theorems there?

Question answered: Use `<` and `slt`.
2024-09-10 12:32:44 +00:00
Kim Morrison
c1da100997 chore: remove debug.byAsSorry 2024-09-10 19:30:09 +10:00
Kim Morrison
6c97c4ce37 chore: update stage0 2024-09-10 19:30:09 +10:00
Kim Morrison
c209d0d745 chore: upstream Zero and NeZero 2024-09-10 19:30:09 +10:00
Kim Morrison
5bc199ea1c chore: debug.byAsSorry on broken proofs 2024-09-10 19:30:09 +10:00
Arthur Adjedj
cb4a73a487 refactor: Lean.Elab.Deriving.FromToJson (#5292)
Refactors the derive handlers for `ToJson` and `FromJson` in preparation
for #3160.
This splits up the different parts of the handler according to how other
similar handlers are implemented while keeping the original logic
intact. This makes the changes necessary to adapt the file in #3160 much
easier.
2024-09-10 08:55:52 +00:00
Lean stage0 autoupdater
92e1f168b2 chore: update stage0 2024-09-10 08:04:39 +00:00
Marc Huisinga
a58520da16 fix: travelling auto-completion (#5257)
Fixes #4455, fixes #4705, fixes #5219

Also fixes a minor bug where a dot in brackets would report incorrect
completions instead of no completions.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-09-10 07:26:44 +00:00
Joachim Breitner
8f899bf5bd doc: code comments about reflection support (#5235)
I found that the kernel has special support for `e =?= true`, and will
in this case aggressively whnf `e`. This explains the following behavior
(for a `sqrt` function with fuel):

```lean
theorem foo : sqrt 100000000000000000002 == 10000000000 := rfl       -- fast
theorem foo : sqrt 100000000000000000002 =  10000000000 := rfl       -- slow
theorem foo : sqrt 100000000000000000002 =  10000000000 := by decide -- fast
```

The special support in the kernel only applies for closed `e` and `true`
on the RHS. It could be generlized (also open terms, also `false`, other
data type's constructors, different orientation). But maybe I should
wait for evidence that this generaziation really matters, or whether
all applications (proof by reflection) can be made to have this form.
2024-09-10 06:36:38 +00:00
Kim Morrison
7a5a08960a feat: cleanup of List.findIdx / List.take lemmas (#5293) 2024-09-10 06:17:38 +00:00
Lean stage0 autoupdater
5a9cfa0aec chore: update stage0 2024-09-10 05:59:09 +00:00
Kim Morrison
c79b09fdbd chore: restore Lake build 2024-09-10 15:24:23 +10:00
Kim Morrison
0b9a4bd65e chore: update stage0 2024-09-10 15:24:23 +10:00
Kim Morrison
e41e305479 chore: rename Array.data to Array.toList 2024-09-10 15:24:23 +10:00
Kim Morrison
b1a03a471f chore: disable Lake build 2024-09-10 15:24:23 +10:00
Sebastian Ullrich
6aa0c46b04 chore: fix Nix update-stage0 (#5290) 2024-09-09 16:27:24 +00:00
Kim Morrison
7eedf6467f feat: List.mem_ite_nil_left and analogues (#5289) 2024-09-09 14:08:01 +00:00
Kim Morrison
64b35ba555 chore: use boolean predicates in List.filter (#5287) 2024-09-09 12:15:04 +00:00
Marc Huisinga
ab7aed2930 feat: use incrementality for completion in tactic blocks (#5205)
This PR enables the use of incrementality for completion in tactic
blocks. Consider the following example:
```lean
example : True := by
  have : True := T
  sleep 10000
```

Before this PR, in order to respond to a completion request after `T`,
`sleep 10000` has to complete first since the command must be fully
elaborated. After this PR, the completion request is responded to
immediately.
2024-09-09 12:08:37 +00:00
Kim Morrison
a9e6c41b54 feat: allow simplifying dite_not/decide_not with only Decidable (¬p) (#5263)
These lemmas are mostly useful for ensuring confluence of `simp`, but
rarely useful in proofs. However they don't seem to have any negative
impact.
2024-09-09 11:46:20 +00:00
Sebastian Ullrich
1a857aa4f8 fix: do not link to Lake_shared with absolute path (#5286)
batteries CI fell over this but there was no PR comment
2024-09-09 11:11:00 +00:00
Sebastian Ullrich
c2761dc270 feat: Lake shared library (#5143)
Fixes #2436 #5050

Next step: when libLake_shared is in stage 0, --load-dynlib it when
building stage 1 Lake
2024-09-09 09:05:54 +00:00
Kim Morrison
ec7ae59473 feat: List.count lemmas (#5285) 2024-09-09 07:04:57 +00:00
Kim Morrison
c96fbdda44 chore: remove @[simp] from List.head_mem and similar (#5271)
These attributes do not seem particularly useful after all.
2024-09-09 06:05:06 +00:00
Kim Morrison
48db0f2d32 chore: turn off Inhabited (Sum α β) instances (#5284)
Alternative to #5270.
2024-09-09 01:10:20 +00:00
Kyle Miller
7a7440f59b feat: have IR checker suggest noncomputable (#4729)
Currently, `ll_infer_type` is responsible for telling the user about
`noncomputable` when a definition depends on one without executable
code. However, this is imperfect because type inference does not check
every subexpression. This leads to errors later on that users find to be
hard to interpret.

Now, `Lean.IR.checkDecls` has a friendlier error message when it
encounters constants without compiled definitions, suggesting to
consider using `noncomputable`. While this function is an internal IR
consistency check, it is also reasonable to have it give an informative
error message in this particular case. The suggestion to use
`noncomputable` is limited to just unknown constants.

Some alternatives would be to either (1) create another checker just for
missing constants, (2) change `ll_infer_type` to always visit every
subexpression no matter if they are necessary for inferring the type, or
(3) investigate whether `tests/lean/run/1785.lean` is due to a deeper
issue.

Closes #1785
2024-09-07 22:00:21 +00:00
Kyle Miller
c9239bfaa8 feat: let unfold do zeta-delta reduction of local definitions (#4834)
This is "upstreaming" mathlib's `unfold_let` tactic by incorporating its
functionality into `unfold`. Now `unfold` can, in addition to unfolding
global definitions, unfold local definitions. The PR also updates the
`conv` version of the tactic.

An improvement over `unfold_let` is that it beta reduces unfolded local
functions.

Two features not present in `unfold` are that (1) `unfold_let` with no
arguments does zeta delta reduction of *all* local definitions, and also
(2) `unfold_let` can interleave unfoldings (in contrast, `unfold a b c`
is exactly the same as `unfold a; unfold b; unfold c`).

Closes RFC #4090
2024-09-07 21:48:08 +00:00
Kyle Miller
8fcec4049b fix: make pretty printer escape identifiers that are tokens (#4979)
For example, if `forall` is a variable, it now pretty prints as
`«forall»`.

Closes #4686
2024-09-07 21:28:44 +00:00
Kyle Miller
e5e577865f doc: mention that inferType does not ensure type correctness (#5087)
This also adds links to the implementations of `whnf` and `inferType` to
make it easier to navigate this part of the code base.
2024-09-07 20:46:53 +00:00
Kim Morrison
7432a6f01f feat: more List.attach lemmas (#5277) 2024-09-07 05:29:40 +00:00
Kim Morrison
fcfead8cde feat: lemmas about List.attach (#5273)
#5272 should be merged first; this contains some material from that PR.
2024-09-06 22:14:56 +00:00
Kim Morrison
e5eea67020 chore: reverse direction of List.tail_map (#5275) 2024-09-06 11:55:50 +00:00
Kim Morrison
943dec48c4 feat: remove @[simp] from Option.pmap/pbind and add simp lemmas (#5272) 2024-09-06 11:39:29 +00:00
Kim Morrison
eba0cbaeb0 chore: remove HashMap's duplicated Pairwise and Sublist (#5269) 2024-09-06 09:28:51 +00:00
Kim Morrison
3ec55d3d49 chore: Nat.testBit_add_one should not be a global simp lemma (#5262) 2024-09-06 00:43:38 +00:00
Kim Morrison
c8c35ad3b9 chore: missing lemma about Fin.ofNat' (#5250) 2024-09-06 00:23:07 +00:00
Kim Morrison
7a6fa85ed1 chore: fix binders on ite_eq_left_iff (#5268) 2024-09-05 23:45:14 +00:00
Kim Morrison
f18ecd4493 chore: protect some Nat bitwise theorems (#5267) 2024-09-05 23:32:41 +00:00
Kyle Miller
74cf53f2b1 fix: preserve order of overapplied arguments in elab_as_elim procedure (#5266)
When an eliminator was overapplied with more than one additional
argument, elaboration produced an incorrect term because the list of
processed arguments was being reversed. Now these arguments are not
reversed.
2024-09-05 19:44:16 +00:00
Kim Morrison
76ea33c4c6 chore: review of List API (#5264) 2024-09-05 13:08:31 +00:00
Kim Morrison
1b099521c1 feat: Nat bitwise lemmas (#5261) 2024-09-05 06:36:21 +00:00
Kim Morrison
7c364543a3 chore: review of List API (#5260) 2024-09-05 06:27:08 +00:00
Henrik Böving
795edcf8ad perf: reduce allocations in Json.compress (#5222)
1. Remove the need to allocate an intermediate `String` for literally
every character in a JSON `String`.
2. Use a single `String` buffer in the entire `Json.compress` machinery.
3. Use `toListAppend`

Number 1 is doing most of the lifting in the perf diff, the rest are
some minor but measurable improvements.
2024-09-04 19:52:10 +00:00
Kim Morrison
d08051cf0b chore: variables appearing on both sides of an iff should be implicit (#5254) 2024-09-04 08:33:24 +00:00
Kim Morrison
f1b2850aa4 chore: split Init.Data.Array.Lemmas for better bootstrapping (#5255)
This allows significantly reducing the imports of `Init.Data.List.Impl`.
2024-09-04 08:33:13 +00:00
Marc Huisinga
d55f55d575 fix: include identifier before cursor in document highlight request (#5237)
Fixes #3023. Also fixes a similar off-by-one in the file worker
definition request.
2024-09-04 08:05:54 +00:00
Alex Keizer
fa8439a663 refactor: make extractLsb' the primitive bv_decide understands, rather than extractLsb (#5211)
We change the `bv_decide` to understand `BitVec.extractLsb'` as a
primitive, and add a normalization lemma for `extractLsb`.
It's important to pick the primed version as a primitive, because it is
not always possible to rewrite `extractLsb'` back into `extractLsb` (see
#5007 for that direction, and the relevant side-conditions).

That is, with this PR, `bv_decide` is able to bitblast both versions of
extracting bits.
2024-09-04 08:02:04 +00:00
Kim Morrison
05fe436bda chore: don't use simp_arith when simp will do (#5256) 2024-09-04 07:56:25 +00:00
Kim Morrison
a926d0ced0 chore: change BitVec.intMin/Max from abbrev to def (#5252)
I don't think we gain anything from having them as `abbrev` here, and
the simpNF linter complains:

```
-- Init.Data.BitVec.Lemmas
#check @BitVec.toNat_intMin /- simp can prove this:
  by simp only [BitVec.toNat_twoPow]
One of the lemmas above could be a duplicate.
If that's not the case try reordering lemmas or adding @[priority].
 -/
#check @BitVec.toNat_intMax /- Left-hand side simplifies from
  (BitVec.intMax w).toNat
to
  (2 ^ w - 1 % 2 ^ w + 2 ^ (w - 1)) % 2 ^ w
using
  simp only [@BitVec.toNat_sub, @BitVec.ofNat_eq_ofNat, BitVec.toNat_ofNat, BitVec.toNat_twoPow, Nat.add_mod_mod]
Try to change the left-hand side to the simplified term!
 -/
 ```
2024-09-04 05:29:58 +00:00
Kim Morrison
8c0c154ebf chore: remove Int simp lemmas that can't fire (#5253)
```
#lint only simpNF in all
```
reports (amongst others):

```
-- Init.Data.Int.Order
#check @Int.toNat_of_nonneg /- Left-hand side simplifies from
  ↑a.toNat
to
  max a 0
using
  simp only [Int.ofNat_toNat]
Try to change the left-hand side to the simplified term!
 -/
#check Int.toNat_sub_toNat_neg /- Left-hand side simplifies from
  ↑n.toNat - ↑(-n).toNat
to
  max n 0 - max (-n) 0
using
  simp only [Int.ofNat_toNat]
Try to change the left-hand side to the simplified term!
 -/
```
2024-09-04 05:29:51 +00:00
Kim Morrison
52bc8dcb40 chore: remove redundant simp annotations (#5251) 2024-09-04 04:36:09 +00:00
Kim Morrison
c219303270 chore: remove @[simp] from some BitVec lemmas (#5249)
I think it would be reasonable, but for now unnecessary, to add @[simp]
to `toNat_of_zero_length` and the subsequent three lemmas.
2024-09-04 03:08:03 +00:00
Kim Morrison
05ba835925 feat: simp lemmas for BitVec, improving confluence (#5248) 2024-09-04 03:03:46 +00:00
Kim Morrison
9587c67781 feat: BitVec.getElem_zeroExtend (#5247) 2024-09-04 02:29:51 +00:00
Kim Morrison
744b68358e chore: cleanup imports of Array.Lemmas (#5246) 2024-09-04 01:48:14 +00:00
Kim Morrison
318e455d96 chore: avoid importing List.Basic without List.Impl (#5245)
This doesn't completely resolve the danger (only relevant in `prelude`
files) of importing `Init.Data.List.Basic` but not `Init.Data.List.Impl`
and thereby not having `@[csimp]` lemmas installed for some list
operations.

I'm going to address this better while working on `Array`.
2024-09-04 01:25:50 +00:00
Joachim Breitner
4eea57841d refactor: rfl tactic: do not use Kernel.isDefEq (#3772)
Sebastian mentioned that the use of the kernel defeq was to work around
a performance issue that was fixed since. Let's see if we can do
without.

This is also a semantic change: Ground terms (no free vars, no mvars)
are reduced at
“all” transparency even if the the transparency setting is default. This
was the case
even before 03f6b87647 switched to the
kernel defeq
checking for performance. It seems that this is rather surprising
behavior from the user
point of view. The fallout on batteries and mathlib is rather limited,
only a few
`rfl` proofs seem to have (inadvertently or not) have relied on this.

The speedcenter reports no significant regressions on core or mathlib.
2024-09-03 19:51:14 +00:00
Kim Morrison
a5162ca748 feat: add @[simp] to Nat.add_eq_zero_iff (#5241) 2024-09-03 09:05:04 +00:00
Kim Morrison
b053403238 chore: improve naming for List.mergeSort lemmas (#5242) 2024-09-03 06:42:33 +00:00
Kim Morrison
66688e10ce chore: remove BitVec simps with complicated RHS (#5240) 2024-09-03 06:27:05 +00:00
Kim Morrison
4a2458b51d feat: gaps in Bool lemmas (#5228) 2024-09-03 04:33:43 +00:00
Leonardo de Moura
e9e858a448 chore: use Expr.numObjs instead of lean_expr_size_shared (#5239)
Remark: declarations like `sizeWithSharing` must be in `IO` since they
are not functions.

The commit also uses the more efficient `ShareCommon.shareCommon'`.
2024-09-02 21:26:00 +00:00
Mac Malone
df9cdcd1b7 feat: lake: Reservoir-related configuration for packages (#4770)
Adds additional fields to the package configuration which will be used
by Reservoir:

* `version`: The version of the package. Follows Lean's model of
`<major>.<minor>.<patch>[-<specialDescr>]`.
* `versionTags`: A pattern matching the set of Git tags Reservoir should
consider package version revisions.
* `description`: A short description for the package. Takes precedence
over the GitHub's description.
* `keywords`: An array of package keywords that will be used to group
packages into categories on Reservoir. Takes precedence over labels on
the repository.
* `homepage`: A URL to a website for the package. Takes precedence over
GitHub's homepage.
* `license`: An SPFX license identifier for the package's license (not
verified to be well-formed).
* `licenseFiles`: An array of (relative) files the contain license
information (e.g., `#["LICENSE", "NOTICE"]` for Apache 2.0).
* `readmeFile`: Relative path to the package's readme (enables
non-standard README locations).
* `reservoir`: Reservoir will use this setting to determine whether to
include packages in its index.

Also adds two new CLI commands:

* `lake reservoir-config`: Used by Reservoir to extract a package's
configuration.
* `lake check-build`: Determines whether the package has any default
build targets configured.

The Reservoir configuration also makes uses of the exiting `name` and
`platformIndependent` fields.
2024-09-02 16:07:08 +00:00
Markus Himmel
830b1191b3 doc: correct docstrings for integer division and modulus (#5230)
Fixes #5204.
2024-09-02 09:33:12 +00:00
Sebastian Ullrich
2117b89cd5 feat: pp.exprSizes debugging option (#5218) 2024-09-02 07:29:23 +00:00
Kim Morrison
4f04112446 chore: remove unused lemmas from a non-terminal simp (#5229)
These non-terminal `simp` calls broke on a branch, but can be fixed on
both `master` and that branch by removing a lemma.
2024-09-02 06:34:56 +00:00
Kyle Miller
db3631ba1f chore: release notes for 4.11.0 (#5221) 2024-09-02 00:19:41 +00:00
Leonardo de Moura
5214154ea9 perf: maximize term sharing at instantiateMVarDeclMVars (#5226)
The `instantiateMVarDeclMVars` is used by `runTactic`.

This commit address a performance issue at `LinearCategory`.
2024-09-01 23:00:48 +00:00
Leonardo de Moura
1b40ec5366 perf: cache visited exprs at CheckAssignmentQuick (#5225) 2024-09-01 22:04:40 +00:00
Kyle Miller
d31066646d feat: make #check and #reduce typecheck terms (#5079)
These commands were trusting that elaboration resulted in type-correct
terms, but users testing custom elaborators have found it to be
surprising that they do not do typechecking. This adds a `Meta.check`
step.
2024-08-31 02:39:38 +00:00
Henrik Böving
88c193d71c fix: out of bounds access when the CNF is too small (#5220)
As reported by @alexkeizer to me.
2024-08-30 22:30:03 +00:00
Sebastian Ullrich
e04a40ddc1 doc: include: currently applies to theorems only (#5206)
Fixes #5184
2024-08-30 12:51:50 +00:00
Henrik Böving
648239c6ec fix: BitVec benchmark after renaming of getLsb (#5217)
fallout from https://github.com/leanprover/lean4/pull/5200
2024-08-30 09:08:24 +00:00
Kim Morrison
a50ed83560 feat: List.erase_range (#5215) 2024-08-30 06:46:42 +00:00
Kim Morrison
bb87a3314d chore: move @[csimp] lemmas earlier where possible (#5214) 2024-08-30 06:42:05 +00:00
Kim Morrison
a47c590a91 chore: make some AIG simps local (#5212)
These had leaked out and were being noticed by the confluence tool.
Better to just make them local.
2024-08-30 02:50:53 +00:00
Kim Morrison
0a0405f4fb chore: List.getElem_drop, add @[simp] and switch primes (#5210)
This is a breaking change, as it reverses the meaning of
`List.getElem_drop` and `List.getElem_drop'`.
2024-08-30 02:48:59 +00:00
Kim Morrison
16aa80306e feat: Nat.bitwise lemmas (#5209) 2024-08-30 02:37:11 +00:00
Kim Morrison
a24370b049 chore: reverse direction of Int.toNat_sub (#5208)
The previous direction conflicted with `toNat_pred`, and this version is
equally helpful for confluence.
2024-08-30 02:25:53 +00:00
Kim Morrison
6b62fed82e feat: proposed change to BitVec API (#5200)
This renames `BitVec.getLsb` to `getLsbD` (`D` for "default" value, i.e.
false), and introduces `getLsb?` and `getLsb'` (which we can rename to
`getLsb` after a deprecation cycle).

(Similarly for `getMsb`.)

Also adds a `GetElem` class so we can use `x[i]` and `x[i]?` notation. 

Later, we will turn
```
theorem getLsbD_eq_getElem?_getD (x : BitVec w) (i : Nat) (h : i < w) :
    x.getLsbD i = x[i]?.getD false
```
on as a `@[simp]` lemma.

This PR doesn't attempt to demonstrate the benefits, but I think both
arguments are going to get easier, and this will bring the BitVec API
closer in line to List/Array, etc.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2024-08-30 02:00:57 +00:00
Joachim Breitner
f30ff6ae79 refactor: put new eqns options into backward namespace (#5207)
in #4154 and #5129 the rules for equational lemmas have changed, and new
options were introduced that can be used to revert to the pre-4.12
behavior. Hopefully nobody really needs these options besides for
backwards compatibility, therefore we put these options in the
`backward` option name space.

So the previous behavior can be achieved by setting
```lean
set_option backward.eqns.nonrecursive false
set_option backward.eqns.deepRecursiveSplit false
```
2024-08-29 17:03:51 +00:00
Joachim Breitner
50a009f811 fix: recursion over predicates: add some whnf sprinkles (#5136)
This fixes #4540.

---------

Co-authored-by: Richard Kiss <him@richardkiss.com>
2024-08-29 16:55:54 +00:00
Joachim Breitner
a993934839 feat: generate f.eq_unfold lemmas (#5141)
With this, lean produces the following zoo of rewrite rules:
```
Option.map.eq_1      : Option.map f none = none
Option.map.eq_2      : Option.map f (some x) = some (f x)
Option.map.eq_def    : Option.map f p = match o with | none => none | (some x) => some (f x)
Option.map.eq_unfold : Option.map = fun f p => match o with | none => none | (some x) => some (f x)
```

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

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

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

---

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

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

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

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

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

chore: derive from testBit_two_pow

chore: convert first to prop and then decide

chore: move intMax down as well

chore: add simp set

Add simp-set into this PR

chore: fix simp extension

Move file to src/Lean to fix build

Add prelude

update date

Add university of cambridge as copyright holder

improve naming

use whitespace uniformly

use decide (n = m)

Drop the 'Nat.' namespace

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

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

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

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

Fix build

add some theorems

Revert "add some theorems"

This reverts commit fb97bc2007e371854b40badb3d6014da034c1f5e.

WIP

Shorten proof

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

finish proofs

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

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

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

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

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

---

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

close #5046 


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

---------

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

Closes `RFC` #4932

---------

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

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

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

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

### Breaking change

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

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

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

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

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

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

### Breaking channge

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

Remedies:

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

### Interaction with simp

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

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

### Implementation notes

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

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

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

---------

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

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

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

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

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

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

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


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

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-08-19 09:11:38 +00:00
Joachim Breitner
51f01d8c8a feat: expose index option to dsimp tactic (#5071)
makes the option introduced in #4202 also available when using `dsimp`
2024-08-19 07:57:16 +00:00
Matthew Toohey
b486c6748b fix: correct typo in invalid reassignment error (#5080)
Corrects a small typo in the error message for when a user attempts to
mutate something which cannot be mutated.
2024-08-18 08:10:07 +00:00
Kim Morrison
38288ae07a feat: upstream List.Perm (#5069) 2024-08-17 04:11:35 +00:00
Kim Morrison
b939fef2cf chore: fix implicitness in refl/rfl lemma binders (#5077) 2024-08-16 22:31:06 +00:00
Arthur Adjedj
eb15c08ea0 fix: instantiate mvars of indices before instantiating fvars (#4717)
When elaborating the headers of mutual indexed inductive types, mvars
have to be synthesized and instantiated before replacing the fvars
present there. Otherwise, some fvars present in uninstantiated mvars may
be missed and lead to an error later.
Closes #3242 (again)
2024-08-16 15:19:48 +00:00
Joachim Breitner
72f2e7aab1 feat: make structure type clickable in “not a field” error (#5072) 2024-08-16 09:06:18 +00:00
Joachim Breitner
cd21687884 feat: simp debug trace tag to use “dpre” in rlfOnly mode (#5073)
to distinguish from `pre`.
2024-08-16 08:56:38 +00:00
Joachim Breitner
a08ef5ffa2 fix: remove partially copied code comment (#5070) 2024-08-16 08:42:30 +00:00
Joachim Breitner
53e6e99a29 refactor: generalize addMatcherInfo (#5068)
works in any `MonadEnv`.
2024-08-16 06:24:32 +00:00
Kim Morrison
59ca274296 chore: minimize some imports (#5067) 2024-08-16 06:18:11 +00:00
Henrik Böving
ac4927de46 feat: List.foldlRecOn (#5039)
As discussed with @semorrison, feel free to do whatever to the branch.

---------

Co-authored-by: Kim Morrison <scott.morrison@gmail.com>
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
Co-authored-by: Kim Morrison <kim@tqft.net>
2024-08-15 23:26:06 +00:00
Sebastian Ullrich
0ecbcfdcc3 chore: remove stray markPersistent (#5056)
It is conditionally applied a few lines below
2024-08-15 15:41:42 +00:00
Sebastian Ullrich
4d4d485c19 chore: avoid rebuilding leanmanifest in each build (#5057) 2024-08-15 14:55:36 +00:00
Joachim Breitner
d1174e10e6 feat: always run clean_wf, even before decreasing_by (#5016)
Previously, the tactic state shown at `decreasing_by` would leak lots of
details about the translation, and mention `invImage`, `PSigma` etc.
This is not nice.
  
So this introduces `clean_wf`, which is like `simp_wf` but using
`simp`'s `only` mode, and runs this unconditionally. This should clean
up the goal to a reasonable extent.
  
Previously `simp_wf` was an unrestricted `simp […]` call, but we
probably don’t want arbitrary simplification to happen at this point, so
this now became `simp only` call. For backwards compatibility,
`decreasing_with` begins with `try simp`. The `simp_wf` tactic
is still available to not break too much existing code; it’s docstring
suggests to no longer use it.

With `set_option cleanDecreasingByGoal false` one can disable the use of
`clean_wf`. I hope this is only needed for debugging and understanding.
  
Migration advise: If your `decreasing_by` proof begins with `simp_wf`,
either remove that (if the proof still goes through), or replace with
`simp`.
  
I am a bit anxious about running even `simp only` unconditionally here,
as it may do more than some user might want, e.g. because of options
like `zetaDelta := true`. We'll see if we need to reign in this tactic
some more.

I wonder if in corner cases the `simp_wf` tactic might be able to close
the goal, and if that is a problem. If so, we may have to promote simp’s
internal `mayCloseGoal` parameter to a simp configuration option and use
that here.
  
fixes #4928
2024-08-15 14:42:15 +00:00
Sebastian Ullrich
a43356591c chore: CI: fix 32bit stage 0 builds (#5052)
Let's link stage 0 against libuv in both cases, even if for Emscripten
we won't for stage 1
2024-08-15 12:35:25 +00:00
Sebastian Ullrich
082ed944d8 fix: Windows stage 0 2024-08-15 14:50:56 +02:00
Kim Morrison
36d71f8253 feat: more List.find?/findSome?/findIdx? theorems (#5053) 2024-08-15 11:53:35 +00:00
Lean stage0 autoupdater
3c07e48a33 chore: update stage0 2024-08-15 12:12:52 +00:00
Markus Himmel
8a6eec0047 doc: add release note drafts for LibUV and hash map deprecation (#5049) 2024-08-15 09:10:02 +00:00
Kim Morrison
213a7221f6 feat: more List.Sublist theorems (#5048) 2024-08-15 05:38:25 +00:00
Sebastian Ullrich
42fcfcbad6 fix: macOS: install name of libleanshared_1 (#5034) 2024-08-15 05:22:34 +00:00
Kim Morrison
2ba7c995a6 chore: upstream List.Pairwise lemmas (#5047) 2024-08-15 02:59:05 +00:00
Kim Morrison
7e72f9ab85 chore: add libuv to the required packages heading in macos docs (#5045) 2024-08-15 01:33:58 +00:00
Kim Morrison
326dbd1e15 chore: upstream #time command (#5044) 2024-08-15 00:17:48 +00:00
Markus Himmel
6bc98af67b chore: reduce usage of refine' (#5042) 2024-08-14 15:14:44 +00:00
Sebastian Ullrich
f883fc0db6 chore: clean up cmdline snapshots logic (#5043) 2024-08-14 15:10:37 +00:00
Sebastian Ullrich
20a7fe89b5 perf: mark entire reported info tree as persistent (#5040)
As we can definitely not free it until .ilean generation at the very end
2024-08-14 13:39:35 +00:00
Joachim Breitner
ac64cfd70a fix: array_get_dec etc. tactics to solve more cases (#5037)
Using `Nat.lt_trans` is too restrictive, and using `Nat.lt_of_lt_of_le`
should make this tactic prove more goals.

This fixes a regression probably introduced by #3991; at least in some
cases before that `apply sizeOf_get` would have solved the goal here.
And it’s true that this is now subsumed by `simp`, but because of the
order that `macro_rules` are tried, the too restrictive variant with
`Nat.lt_trans` would be tried before `simp`, without backtracking.

Fixes #5027
2024-08-14 12:41:14 +00:00
Henrik Böving
958ad2b54b feat: upstream LeanSAT's bitblaster (#5013)
Step 3/~7 in upstreaming LeanSAT.

A few thoughts:
- Why is this not in `Std.Sat`? LeanSAT's bitblaster operates on a
limited internal language. For example it has no idea that signed
comparision operators even exist. This is because it relies on a
normalization pass before being given the goal. For this reason I would
not classify the bitblaster as an API that we should publicly advertise
at this abstraction level
- Sometimes I slightly rebuild parts of the LawfulOperator
infrastructure for operators that work non-tail-recursively. This is
because they do not return an `Entrypoint` but instead an
`ExtendingEntrypoint` in order to even be defined in the first place
(casting Ref's and all that). Given the fact that this barely happens
and I never actually commit to rebuilding the full API I'm hoping that
this is indeed a fine decision?
- The single explicit `decreasing_by` that has a simp only which
*almost* looks like `simp_wf` is missing a singular lemma from `simp_wf`
because it doesn't terminate otherwise.
- I am not using functional induction because it basically always fails
at some generalization step, that is also the reason that there is lots
of explicit `generalize` and manually recursive proofs.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-08-14 09:54:10 +00:00
Lean stage0 autoupdater
bd5f8ef242 chore: update stage0 2024-08-14 09:45:24 +00:00
Sebastian Ullrich
337db03717 fix: report info trees on cmdline for .ileans (#5018)
In #4976, I forgot that we do need info trees eventually on the cmdline
for .ilean generation. Unfortunately, not reporting them incrementally
would require an API change, so let's see what the impact of incremental
reporting is
2024-08-14 08:59:29 +00:00
Markus Himmel
3efd0e4e1f chore: fix inconsistent style in internal hash map lemmas (#5033) 2024-08-14 07:49:11 +00:00
Kim Morrison
8c96d213f3 chore: use local instance in Lsp.Diagnostics (#5031)
I'm experimenting with changing the signature of `Ord.arrayOrd`; rather
than make a local synonym here, let's make a local instance so it
doesn't interact with the experiments.
2024-08-14 05:04:32 +00:00
Kim Morrison
154385fdb9 chore: remove dead code in Lake.Util.Compare (#5030)
While exploring refactors of `List.lt` I ran into errors here, in code
that is entirely unused. Propose cleaning up to get things out of my
way!
2024-08-14 04:59:20 +00:00
Kim Morrison
9e39dc8100 feat: new+old lemmas about List.Sublist (#5029)
Some upstreamed from mathlib, some new.
2024-08-14 04:13:57 +00:00
Markus Himmel
dcadfd1c89 chore: remove oldSectionVars from hash map lemmas (#5023) 2024-08-14 03:04:33 +00:00
Tobias Grosser
7c5d8661f4 feat: add BitVec.toNat_[udiv|umod] and [udiv|umod]_eq (#4997)
Co-authored-by: Siddharth <siddu.druid@gmail.com>
Co-authored-by: Atticus Kuhn <52258164+AtticusKuhn@users.noreply.github.com>
2024-08-14 03:03:31 +00:00
Alex Keizer
bff30fe98e feat: express BitVec.extractLsb' in terms of extractLsb (#5007)
Adds a lemma to rewrite `BitVec.extractLsb'` to `extractLsb` plus a
cast.
Note that `extractLsb'` with a length of 0 returns `BitVec 0`, while
`extractLsb` will never return an empty bitvector (because of the `+ 1`
in it `hi - lo + 1`). Hence, this lemma needs a side condition that the
length is non-zero.

Also adds `getLsb_extractLsb'`

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2024-08-14 03:01:58 +00:00
Jeremy Tan Jie Rui
ac2dabdedf chore: use in Fin.ne_of_val_ne (#5011)
Instead of a `Not (Eq …)` term use the proper `≠` in `Fin.ne_of_val_ne`,
to make it symmetric with `Fin.val_ne_of_ne`, and move the former to the
same place as the latter.

This answers a query of @eric-wieser at

https://github.com/leanprover-community/mathlib4/pull/15762#discussion_r1714990412
2024-08-14 01:34:47 +00:00
Joachim Breitner
7283e2c14e chore: pr-release: pass --retry to curl (#5025)
Since https://github.com/curl/curl/pull/4465 curl adheres to the
`Retry-After` header, so maybe this fixes the issues with
```
jq: error (at <stdin>:5): Cannot index string with string "body"
```
that sometimes make this workflow fail.
2024-08-13 16:19:43 +00:00
Joachim Breitner
f500af99e8 chore: ci.yaml: build MacOS Aarch64 release for PRs by default (#5022)
should make https://github.com/leanprover-community/mathlib4/pull/13301
unnecessary, which has a fair number of bad side-effects
2024-08-13 15:34:44 +00:00
Joachim Breitner
861ef27503 refactor: state WellFoundedRelation Nat using <, not Nat.lt (#5012)
as that’s the simp normal form.
2024-08-13 13:37:42 +00:00
Joachim Breitner
11be29e68c chore: pr-release: adjust lakefile editing sed to new git syntax (#5014) 2024-08-13 12:03:51 +00:00
Henrik Böving
74f9dea701 feat: use save-always in cache action (#5010)
Follows up on
https://github.com/leanprover/lean4/pull/5003#issuecomment-2284813940
2024-08-13 09:27:15 +00:00
Sebastian Ullrich
041b80a4f5 chore: speedcenter: reduce number of runs for "fast" benchmarks from 10 to 3 (#5009) 2024-08-13 09:06:06 +00:00
Sebastian Ullrich
5bc6496a7c chore: more libuv search patterns for the speedcenter (#5008) 2024-08-13 10:40:09 +02:00
Sebastian Ullrich
f3e7b455bb perf: avoid MT marking environment in language processor (#5004)
#4976 moved resolution of a promise to an earlier point, but that led to
object being marked MT earlier, so we need to move the code that
minimizes those objects earlier too to revert the performance
regression.
2024-08-12 19:15:38 +00:00
Kyle Miller
7cd406f335 fix: check is valid structure projection when pretty printing (#4982)
For structure projections, the pretty printer assumed that the
expression was type correct. Now it checks that the object being
projected is of the correct type. Such terms appear in type mismatch
errors.

Also, fixes and improves `#print` for structures. The types of
projections now use MessageData (so are now hoverable), and the type of
`self` is now the correct type.

Closes #4670
2024-08-12 15:52:17 +00:00
Henrik Böving
ecb35795eb chore: upgrade cache action to silence warnings (#5003)
According to the release notes of cache this should not break anything
as they merely upgraded the node version.
2024-08-12 15:46:53 +00:00
Henrik Böving
dc3eccdf26 feat: Std.Sat.AIG (#4953)
Step 2/~7 in upstreaming LeanSAT.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
Co-authored-by: Markus Himmel <markus@lean-fro.org>
Co-authored-by: Kim Morrison <scott.morrison@gmail.com>
2024-08-12 14:58:38 +00:00
Markus Himmel
c237c1f9fb feat: link LibUV (#4963) 2024-08-12 12:33:24 +00:00
Sebastian Ullrich
dd4e26f247 feat: output panics into Lean's redirected stderr (#4952)
...unless we are about to kill the process anyway (which is not the
default)

Ensures panics are visible as regular messages in the language server
and properly ordered in relation to other messages on the cmdline
2024-08-12 12:15:15 +00:00
Sebastian Ullrich
c5114c971a fix: Windows needs more LEAN_EXPORTs 2024-08-12 14:14:42 +02:00
Sebastian Ullrich
adc799584c fix: split libleanshared on Windows to avoid symbol limit 2024-08-12 14:14:42 +02:00
Sebastian Ullrich
bbb448cdf6 chore: update stage0 2024-08-12 14:14:42 +02:00
Sebastian Ullrich
9d0302e749 chore: remove LEAN_EXPORT denylist workaround 2024-08-12 14:14:42 +02:00
Sebastian Ullrich
8d12dd87a4 fix: disable incremental body elaboration in presence of where (#5001) 2024-08-12 10:42:19 +00:00
Henrik Böving
da9d44df2d feat: handle \r on all operating systems in IO.FS.lines (#4973)
Closes: #4573
2024-08-12 09:51:50 +00:00
David Thrane Christiansen
5c182bd540 fix: reduce default max depth for ext tactic (#4996)
The prior default of 1000000 could not be achieved in practice, because
the stack would overflow after around 5000 recursive invocations. This
meant that a poorly-chosen @[ext] lemma could crash Lean.

Talking to Mathlib users, it seems that 10 would be a very large number
in practice, so a default limit of 100 should not change successful
uses. But it does make it much easier to diagnose and recover from poor
choices of @[ext] lemmas.
2024-08-12 07:26:32 +00:00
David Thrane Christiansen
ecd3aa4b5d fix: handle SIGBUS when looking for stack overflows (#4971)
Without this change, a stack overflow on Mac OS during tactic execution
can lead to the message:

    terminated by signal SIGBUS (Misaligned address error)

This comes from `lean_alloc_small`. With the change, the process instead
terminates with the more accurate and actionable:

    Stack overflow detected. Aborting.
2024-08-12 07:08:22 +00:00
Kim Morrison
12ca422d86 chore: upstream List.findIdx lemmas (#4995) 2024-08-12 04:11:00 +00:00
Kim Morrison
0a7af630a5 chore: when a linter crashes, prefix its name (#4967)
Helpful for diagnosing which linter is failing, c.f. [recent
problem](https://leanprover.zulipchat.com/#narrow/stream/428973-nightly-testing/topic/quote4/near/457349304)
in quote4.
2024-08-12 02:36:42 +00:00
Kim Morrison
fc5615880e chore: add deprecation for Array.get_modify (#4957) 2024-08-12 02:02:18 +00:00
François G. Dorais
23d898c86b feat: TransGen r is transitive (#4960)
Closes #4959
2024-08-12 02:01:52 +00:00
Tobias Grosser
dfe493d9d8 feat: add BitVec.[sshiftRight/shiftLeft]_*_distrib (#4951)
After having added already `BitVec.ushiftRight_*_distrib`in
https://github.com/leanprover/lean4/pull/4667 for ushiftRight, this PR
now completes the `*_distrib` theorems for shift.
2024-08-12 02:00:58 +00:00
François G. Dorais
759ece7f9e fix: naming convention for UInt lemmas (#4514)
Closes #4513

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-08-12 01:03:21 +00:00
Leonardo de Moura
89c3079072 chore: fix typo in hash code for Expr equality test (#4990)
We observed a small performance improvement at
https://github.com/leanprover/LNSym/blob/proof_size_expt/Proofs/SHA512/Experiments/Sym30.lean
Before: 2.65s
After: 2.60s
2024-08-12 00:47:08 +00:00
Tobias Grosser
37f9063c3e feat: add BitVec.neg_neg (#4977)
.. as well as neg_neq_iff_neq_neg.

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
2024-08-12 00:34:46 +00:00
Kim Morrison
8364c3e178 chore: begin development cycle for v4.12.0 (#4986) 2024-08-12 00:33:05 +00:00
Kim Morrison
215b4a6a8d fix: omega regression (#4989)
This is a better fix to the problem reported at
https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/nat.20fighting,
which itself had a problem as reported at
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/omega.20regression/near/456539091.
2024-08-12 00:24:24 +00:00
Leonardo de Moura
7d7447563d fix: panic at reducePow (#4988)
closes #4947
2024-08-12 00:20:29 +00:00
Leonardo de Moura
2436562d57 fix: regular mvar assignments take precedence over delayed ones (#4987)
closes #4920
2024-08-12 00:14:38 +00:00
Tobias Grosser
4236d8a85b feat: add Std.[Associative|Commutative] instances for BitVec.[and|or|xor] (#4981) 2024-08-11 09:40:07 +00:00
Kyle Miller
5f31e938c1 feat: @[app_delab] (#4976)
Adds `@[app_delab ident]` as a macro for `@[delab app.ident]`. Resolves
the identifier when expanding the macro, saving needing to use the fully
qualified identifiers that `@[delab]` requires. Also, unlike `@[delab]`,
throws an error if the identifier cannot be resolved.

Closes #4899
2024-08-10 16:54:39 +00:00
Kyle Miller
95bf6793aa fix: make cdot anonymous function notation handle ambiguous notation (#4833)
Fixes an issue where each alternative in choice nodes would get their
own arguments. Now cdot function expansion is aware of choice nodes.

Also modifies the variable naming so that multi-argument functions like
`(· + ·)` expand as `fun x1 x2 => x1 + x2` rather than `fun x x_1 => x +
x_1`.

Closes #4832
2024-08-09 21:16:51 +00:00
Lean stage0 autoupdater
30cf3bb3bf chore: update stage0 2024-08-09 16:16:48 +00:00
Sebastian Ullrich
238885436b fix: resolve parser promise before starting elaboration (#4974)
A regression introduced in #3106 that meant we could actually not
interrupt elaboration of previous document versions but would always
wait on them
2024-08-09 15:38:13 +00:00
David Thrane Christiansen
30a52b794a feat: add [ext] attribute to Array.ext (#4970)
This fixes a minor papercut.
2024-08-09 12:40:39 +00:00
Sebastian Ullrich
5da9038fb4 chore: adapt stdlib to new variable behavior 2024-08-09 11:50:54 +02:00
Sebastian Ullrich
3a588e7547 chore: update stage0 2024-08-09 11:50:54 +02:00
Sebastian Ullrich
9f76cb9aa5 feat: new variable command 2024-08-09 11:50:54 +02:00
Lean stage0 autoupdater
88a7f5c592 chore: update stage0 2024-08-09 08:46:18 +00:00
Kim Morrison
dd6ed124ba chore: upstream Thunk.ext (#4969) 2024-08-09 05:49:13 +00:00
Alok Singh
6dd502321f chore: add parallelism fallback for macOS on build (#4647)
1 less thing to think about.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-08-08 14:26:06 +00:00
David Thrane Christiansen
bcbd7299e9 fix: export more symbols needed by Verso (#4956)
This enables the Verso LSP server extensions to work.
2024-08-08 13:31:34 +00:00
Markus Himmel
b144107ed5 chore: deprecate Lean.HashMap and Lean.HashSet (#4954)
This restores all of the imports of `Lean.Data.HashMap` and
`Lean.Data.HashSet` so that users actually see the deprecation warnings
instead of a "declaration not found" error.
2024-08-08 12:46:10 +00:00
Sebastian Ullrich
c24d2186fc doc: stderrAsMessages is now the default on the cmdline as well (#4955) 2024-08-08 10:28:22 +00:00
Sebastian Ullrich
7ef2d2fea2 chore: CI: remove rebase command
We'll try using the buttons provided by GitHub for now
2024-08-08 11:17:52 +02:00
Henrik Böving
7776852d22 feat: revamp file IO, this time Windows compatible (#4950)
This implements a naive version of `getline` because Windows does not
have `getline`. Given the fact that `FILE` has buffered IO, calling
`fgetc` in a loop is not as big of a performance hazard as it might seem
at first glance.

The proper solution to this would of course be to have our own buffered
IO so we are fully in charge of the buffer. In this situation we could
check the entire buffer for a newline at once instead of char by char.
However that is not going to happen for the near future so I propose we
stay with this implementation. If reading individual lines of a file
does truly end up being the performance bottle neck we have already
won^^.
2024-08-07 22:33:37 +02:00
Markus Himmel
63c4de5fea chore: update stage0 2024-08-07 18:24:42 +02:00
Markus Himmel
3b14642c42 chore: build Lake again 2024-08-07 18:24:42 +02:00
Markus Himmel
d52da36e68 chore: update stage0 2024-08-07 18:24:42 +02:00
Markus Himmel
bf82965eec chore: avoid builing Lake 2024-08-07 18:24:42 +02:00
Markus Himmel
4bac74c4ac chore: switch to Std.HashMap and Std.HashSet almost everywhere 2024-08-07 18:24:42 +02:00
Henrik Böving
8b9d27de31 chore: Revert "feat: Revamp file reading and writing" (#4948)
Reverts leanprover/lean4#4906
2024-08-07 16:00:45 +00:00
Henrik Böving
d15f0335a9 feat: setup Std.Sat with definitions of SAT and CNF (#4933)
Step 1 out of approximately 7 to upstream LeanSAT.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
Co-authored-by: Markus Himmel <markus@lean-fro.org>
2024-08-07 15:44:46 +00:00
Sebastian Ullrich
240ebff549 chore: Windows needs more LEAN_EXPORTs (#4941) 2024-08-07 17:13:13 +02:00
Sebastian Ullrich
a29bca7f00 chore: CI: placate linter 2024-08-07 16:52:18 +02:00
Tobias Grosser
313f6b3c74 chore: name variables in Data/BitVec consistently (#4930)
This change canonicalizes the BitVec variable names to `x y z : BitVec`
instead of alternative namings such as `s t : BitVec` or `a b : BitVec`.
Variable names that carry semantic meaning such as `(msbs : BitVec w)
(lsb : Bool)` remain untouched.

This is purely a naming change to make our bitvector proofs more
consistent and polish the (auto-generated) documentation as a very small
step towards polishing the documentation of the BitVec library in Lean.

---------

Co-authored-by: AnotherAlexHere <153999274+AnotherAlexHere@users.noreply.github.com>
2024-08-07 13:43:15 +00:00
Markus Himmel
43fa46412d feat: deprecated variants of hash map query methods (#4943)
#4917 will expose users of the `Lean` API to the renaming of the hash
map query methods. This PR aims to make the transition easier by adding
deprecated functions with the old names.
2024-08-07 13:36:19 +00:00
Henrik Böving
234704e304 feat: upstream utilities around Array, Bool and Prod from LeanSAT (#4945)
Co-authored-by: Kim Morrison <kim@tqft.net>
2024-08-07 12:32:40 +00:00
Sebastian Ullrich
12a714a6f9 chore: CI: fix rebase command 2024-08-07 14:27:53 +02:00
Sebastian Ullrich
cdc7ed0224 chore: CI: fix rebase command 2024-08-07 14:21:43 +02:00
Sebastian Ullrich
217abdf97a chore: CI: fix rebase command 2024-08-07 14:15:18 +02:00
Sebastian Ullrich
490a2b4bf9 chore: CI: fix rebase command 2024-08-07 14:05:00 +02:00
Sebastian Ullrich
84d45deb10 chore: CI: fix rebase 2024-08-07 14:02:57 +02:00
Sebastian Ullrich
f46d216e18 chore: CI: !rebase PR comment command 2024-08-07 13:53:17 +02:00
Tobias Grosser
cc42a17931 feat: add ushiftRight_*_distrib theorems (#4667) 2024-08-07 10:43:54 +00:00
Siddharth
e106be19dd feat: sshiftRight bitblasting (#4889)
We follow the same strategy as
https://github.com/leanprover/lean4/pull/4872,
https://github.com/leanprover/lean4/pull/4571, and implement bitblasting
theorems for `sshiftRight`.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-08-07 10:33:56 +00:00
Sebastian Ullrich
1efd6657d4 test: unflakify test cases (#4940)
With the recent unification of server and cmdline processing,
`IO.Process` tests that previously broke the server because they
directly wrote to stdout are now flaky on the cmdline because
elaboration and reporting are happening in separate threads. By removing
direct writes to stdout, the race condition is removed and the file can
actually be edited in the language server as well again.
2024-08-07 09:34:29 +00:00
Henrik Böving
473b34561d feat: Revamp file reading and writing (#4906)
This PR:
- changes the implementation of `readBinFile` and `readFile` to only
require two system calls (`stat` + `read`) instead of one `read` per
1024 byte chunk.
- fixes a bug where `Handle.getLine` would get tripped up by a NUL
character in the line and cut the string off. This is caused by the fact
that the original implementation uses `strlen` and `lean_mk_string`
which is the backer of `mk_string` does so as well.
- fixes a bug where `Handle.putStr` and thus by extension `writeFile`
would get tripped up by a NUL char in the line and cut the string off.
Cause here is the use of `fputs` when a NUL char is possible.

Closes: #4891 
Closes: #3546
Closes: #3741
2024-08-07 07:39:15 +00:00
Marc Huisinga
574066b30b fix: language server windows issues (#4821)
This PR resolves two language server bugs that especially affect Windows
users:
1. Editing the header could result in the watchdog not correctly
restarting the file worker (#3786, #3787), which would lead to the file
seemingly being processed forever.
- The cause of this issue was a race condition in the watchdog that was
accidentally introduced as far back as #1884: In specific circumstances,
the watchdog will attempt forwarding a message to the file worker after
the process has exited due to a changed header, but before the file
worker exiting has been noticed by the watchdog (which will then restart
the file worker). In this case, the watchdog would mark the file worker
as having crashed and not look at its exit code to restart the file
worker, but instead treat it like a crashed file worker that will only
be restarted when editing the file again. Not inspecting the exit code
of the file worker when it crashed from forwarding a message from the
file worker is necessary since we do not restart the file worker until
another notification from the client arrives, and so we would read the
same crash exit code over and over again in the main loop of the
watchdog if we did not remove it from our list of file workers that we
listen to.
- This PR resolves this issue by distinguishing between "crashes when
forwarding messages to the file worker" and "crashes when forwarding
messages from the file worker". In the former case, we still inspect the
exit code of the file worker and potentially restart it if the imports
changed, whereas in the latter case, we stop inspecting the exit code of
the file worker. This is correct because the latter case is exactly the
one where we need to stop inspecting the exit code but where a crash
cannot occur as a result of a changed header, whereas the former case is
exactly the one where we still need to inspect the exit code after a
crash to ensure that we restart the file worker in case it exited
because the header changed.
- At some point in the future, it would be nice to revamp the
concurrency model of the watchdog entirely now that we have all those
fancy concurrency primitives that were not available four years ago when
the watchdog was first written.

2. On an especially slow Windows machine, we found that starting the
language server would sometimes not succeed at all because reading from
the stdin pipe in the watchdog produced an EINVAL error, which was in
turn caused by an NT "pipe empty" error.
- After lots of debugging, @Kha found that Lake accidentally passes its
stdin to Git because it does not explicitly set the `stdin` field to
`null` when spawning the process.
- Changing this fixes the issue, which suggests that Git may mutate the
pipe we pass to it to be non-blocking, which then causes a "pipe empty"
error in the watchdog when we also attempt to read from that same pipe.
- I'm still very uncertain why we only saw this issue on one
particularly slow machine and not across the whole eco system.

This PR also resolves an issue where we would not correctly emit
messages that we received while the file worker is being restarted to
the corresponding file worker after the restart.

Closes #3786, closes #3787.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-08-07 06:19:33 +00:00
Kim Morrison
1e6d617aad chore: minor fixes to release checklist (#4937) 2024-08-07 01:09:35 +00:00
Leonardo de Moura
c17a4ddc94 perf: skip betaReduceLetRecApps if it is not needed (#4936) 2024-08-07 00:57:35 +00:00
Leonardo de Moura
5be4f5e30c perf: skip eraseRecAppSyntaxExpr if it is not needed (#4935) 2024-08-07 00:29:50 +00:00
Leonardo de Moura
3c5ac9496f perf: expr_eq_fn (#4934)
This PR contains a collection of small optimizations that improve the
equality test for `Expr`.
2024-08-07 00:02:14 +00:00
Kyle Miller
6c1f8a8a63 fix: ensure autoparam errors have correct positions (#4926)
Autoparam tactic scripts have no source positions, which until recently
made it so that any errors or messages would be logged at the current
ref, which was the application or structure instance being elaborated.
However, with the new incrementality features the ref is now carefully
managed to avoid leakage of outside data. This inhibits the elaborator's
ref from being used for the tactic's ref, causing messages to be placed
at the beginning of the file rather than on the syntax that triggered
the autoparam.

To fix this, now the elaborators insert the ref's source position
everywhere into the autoparam tactic script.

If in the future messages for synthetic tactics appear at the tops of
files in other contexts, we should consider an approach where
`Lean.Elab.Term.withReuseContext` uses something like `replaceRef` to
set the ref while disabling incrementality when the tactic does not
contain source position information.

Closes #4880
2024-08-06 22:27:51 +00:00
Matthew Robert Ballard
7bea3c1508 chore: wrap diagnostic results in MessageData.traces (#4897)
Currently, the messages in the diagnostic summaries are created by
appending interpolated strings. We wrap these in `.trace`'s, and the
results are better formatted when expanding child nodes in the info
view. Particularly, the latter diagnostic summaries remain on their own
lines flush to the left instead of on the same line directly adjacent to
the last child node.
2024-08-06 19:10:51 +00:00
Leonardo de Moura
a27d4a9519 chore: reduce stack space usage at instantiate_mvars_fn (#4931) 2024-08-06 17:38:59 +00:00
Leonardo de Moura
4a2fb6e922 chore: fix typo (#4929) 2024-08-06 15:27:20 +00:00
Henrik Böving
b7db82894b feat: generalized Parsec (#4774)
For experimentation by @the-sofi-uwu.

I also have an efficient number parser in LeanSAT that I am planning to
upstream after we have sufficiently bikeshed this change.
2024-08-06 15:17:23 +00:00
Sebastian Ullrich
35e1554ef7 chore: ignore stale leanpkg tests (#4925) 2024-08-06 08:19:33 +00:00
Leonardo de Moura
14d59b3599 feat: theorem diagnostics (#4924)
When `set_option diagnostics true`, for each theorem with size >
`diagnostics.threshold.proofSize`, display proof size, and the number of
applications for each constant symbol.
2024-08-06 01:01:28 +00:00
Leonardo de Moura
a8e480cd52 chore: profile instantiateMVars at MutualDef.lean (#4923)
`instantiateMVars` can be a performance bottleneck when assembling the
final proof term.

For example, it takes approx. 1 second at

https://github.com/leanprover/LNSym/blob/proof_size_expt/Proofs/SHA512/Experiments/Sym30.lean
2024-08-05 19:15:56 +00:00
Leonardo de Moura
d07239d1bd perf: use lean_instantiate_expr_mvars at instantiateExprMVars (#4922)
This PR completes #4915
2024-08-05 19:02:54 +00:00
Leonardo de Moura
590de785cc chore: cleanup betaRev (#4921) 2024-08-05 18:02:20 +00:00
Lean stage0 autoupdater
d671d0d61a chore: update stage0 2024-08-05 17:53:40 +00:00
Leonardo de Moura
8e476e9d22 perf: instantiateExprMVars (#4915)
TODO: 
- Support for `zeta := true` at `apply_beta`.
- Investigate test failure. 
- Break PR in pieces because of bootstrapping issues. The current PR
updates a stage0 file to workaround the issue.

Motivation: significant performance improvement at
https://github.com/leanprover/LNSym/blob/proof_size_expt/Proofs/SHA512/Experiments/Sym30.lean

With M1 Pro:
- Before: 4.56 secs
- After: 3.16 secs

Successfully built stage2 using this PR
2024-08-05 17:15:22 +00:00
Sebastian Ullrich
a3d144a362 feat: new snapshot architecture on the cmdline (#3106)
This is #3014 with cad5cce reverted for testing.
2024-08-05 15:57:42 +00:00
Sebastian Ullrich
87d41e6326 chore: missing include on Windows (#4919) 2024-08-05 15:50:43 +00:00
Kim Morrison
d6cb2432c6 chore: remove unnecessary steps from release checklist (#4914) 2024-08-05 01:57:30 +00:00
Kim Morrison
c0ffc85d75 chore: require docs in BitVec (#4913) 2024-08-05 01:12:04 +00:00
Leonardo de Moura
f62359acc7 perf: use lean_instantiate_level_mvars (#4912)
implemented in C/C++.
Next step: same for `instantiateExprMVars`
2024-08-04 23:21:57 +00:00
Shuhao Song
2d09c96caf chore: cli help text: comma-separate alternative option forms (#4911)
The help message of Lean command line contains
```
--o=oname -o create olean file
```
This may lead to misunderstanding that the command needs both argument
`--o=oname` and `-o`, i. e. `lean --o=test.o -o test.lean`. In the help
message of GNU coreutils, such as `ls`, it is `-a, --all ...`, which
might be better.
Some discussion is on Zulip thread
[https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/The.20help.20message.20of.20Lean.20command.20line](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/The.20help.20message.20of.20Lean.20command.20line).
2024-08-04 22:54:34 +00:00
Lean stage0 autoupdater
21b4377d36 chore: update stage0 2024-08-04 19:11:50 +00:00
Leonardo de Moura
1e9d96be22 perf: add lean_instantiate_level_mvars (#4910)
The new code is not active yet because of bootstrapping issues.
It requires an `update_stage0`.
2024-08-04 18:31:44 +00:00
Leonardo de Moura
647a5e9492 perf: use NatPow Int instead of HPow Int Nat Int (#4903)
This modification improves the performance of the example in issue
#4861. It no longer times out but is still expensive.

Here is the analysis of the performance issue: Given `(x : Int)`, to
elaborate `x ^ 1`, a few default instances have to be tried.

First, the homogeneous instance is tried and fails since `Int` does not
implement `Pow Int`. Then, the `NatPow` instance is tried, and it also
fails. The same process is performed for each term of the form `p ^ 1`.
There are seveal of them at #4861. After all of these fail, the lower
priority default instance for numerals is tried, and `x ^ 1` becomes `x
^ (1 : Nat)`. Then, `HPow Int Nat Int` can be applied, and the
elaboration succeeds. However, this process has to be repeated for every
single term of the form `p ^ 1`. The elaborator tries all homogeneous
`HPow` and `NatPow` instances for all `p ^ 1` terms before trying the
lower priority default instance `OfNat`.

This commit ensures `Int` has a `NatPow` instance instead of `HPow Int
Nat Int`. This change shortcuts the process, but it still first tries
the homogeneous `HPow` instance, fails, and then tries `NatPow`. The
elaboration can be made much more efficient by writing `p ^ (1 : Nat)`.
2024-08-03 00:35:04 +00:00
Clement Courbet
9c4028aab4 perf: avoid expr copies in replace_rec_fn::apply (#4702)
Those represent ~13% of the time spent in `save_result`,
even though `r` is a temporary in all cases but one.

See #4698 for details.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2024-08-02 18:32:36 +00:00
Clement Courbet
2c002718e0 perf: fix implementation of move constructors and move assignment ope… (#4700)
…rators

Right now those constructors result in a copy instead of the desired
move. We've measured that expr copying and assignment by itself uses
around 10% of total runtime on our workloads.

See #4698 for details.
2024-08-02 17:55:03 +00:00
Sebastian Ullrich
b07384acbb feat: accept user-defined options on the cmdline (#4741)
Initial options are now re-parsed and validated after importing. Cmdline
option assignments prefixed with `weak.` are silently discarded if the
option name without the prefix does not exist.

Fixes #3403
2024-08-02 12:24:56 +00:00
Sebastian Ullrich
efc99b982e chore: deprecate Nix-based build, remove interactive components (#4895)
Users who prefer the flake build should maintain it externally
2024-08-02 09:57:34 +00:00
Siddharth
ee430b6c80 feat: getLsb_replicate (#4873)
This allows bitblasting `BitVec.replicate`.

I changed the definition of `BitVec.replicate` to use `BitVec.cast` in
order to make the proof smoother, since it's an easier time simplifying
away terms with `BitVec.cast`.

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2024-08-01 23:07:19 +00:00
Alok Singh
a8740f5ed9 doc: add docstring to IO.FS.realpath (#4648)
Based on `IO.FS.rename` template.
2024-08-01 14:00:54 +00:00
Sebastian Ullrich
5e6a3cf5f9 chore: revert "fix: make import resolution case-sensitive on all platforms" (#4896)
Reverts leanprover/lean4#4538 because of unexpected overhead
2024-08-01 13:55:37 +00:00
Sebastian Ullrich
0ed1cf7244 fix: LEAN_EXPORT in sharecommon (#4893) 2024-08-01 13:03:25 +02:00
Lean stage0 autoupdater
e83f78d5af chore: update stage0 2024-08-01 06:54:29 +00:00
David Thrane Christiansen
32b9de8c77 fix: export symbols needed by Verso (#4884)
Verso needed a symbol that was unexported - this exposes it again.
2024-08-01 04:56:27 +00:00
Leonardo de Moura
a856016b9d perf: precise cache for expr_eq_fn (#4890)
This performance issue was exposed by the benchmarks at
https://github.com/leanprover/LNSym/tree/proof_size_expt/Proofs/SHA512/Experiments
2024-08-01 02:56:41 +00:00
Siddharth
c517688f1d feat: ushiftRight bitblasting (#4872)
This adds theorems `ushiftRight_rec_zero`, `ushiftRight_rec_succ`,
`ushiftRight_rec_eq`, and `shiftRight_eq_shiftRight_rec`.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2024-07-31 21:44:06 +00:00
Leonardo de Moura
db594425bf refactor: sharecommon (#4887)
This PR also fixes a missing borrow annotation.
2024-07-31 19:13:12 +00:00
Kim Morrison
dcea47db02 chore: shorten suggestion about diagnostics (#4882)
This message is often incorporated into source files via `#guard_msgs`.
This change ensures it won't go over the 100 character ruler, and I
think is equally grammatical. :-)
2024-07-31 17:56:43 +00:00
Siddharth
f869902a4b feat: Nat simprocs for simplifying bit expressions (#4874)
This came up in the context of simplifying proof states for
https://github.com/leanprover/LNSym.
2024-07-31 17:26:05 +00:00
Sebastian Ullrich
d5a8c9647f fix: make import resolution case-sensitive on all platforms (#4538)
Co-authored-by: Mac Malone <tydeu@hatpress.net>
2024-07-31 16:48:14 +00:00
Sebastian Ullrich
d19bab0c27 feat: include command (#4883)
To be implemented in #4814
2024-07-31 13:25:54 +00:00
Sebastian Ullrich
6a4159c4a7 refactor: split out Lean.Language.Lean.Types (#4881) 2024-07-31 09:50:12 +00:00
Kim Morrison
8acdafd5b3 chore: correct doc-string for Array.swap! (#4869) 2024-07-31 04:02:30 +00:00
Kim Morrison
688da9d8a7 chore: updates to release_checklist.md (#4876)
Co-authored-by: Kyle Miller <kmill31415@gmail.com>
2024-07-31 04:02:19 +00:00
Kyle Miller
d5e7dbad80 fix: make "use `set_option diagnostics true" message conditional on current setting (#4781)
It is confusing that the message suggesting to use the `diagnostics`
option is given even when the option is already set. This PR makes use
of lazy message data to make the message contingent on the option being
false.

It also tones down the promise that there is any diagonostic information
available, since sometimes there is nothing to report.

Suggested by Johan Commelin.
2024-07-31 03:53:09 +00:00
Kyle Miller
82f48740dc chore: copy release notes from releases/v4.10.0 (#4864)
This also updates the 4.9.0 release notes with backported changes.
2024-07-31 03:30:13 +00:00
Kyle Miller
a827759f1d fix: mistake in statement of List.take_takeWhile (#4875)
This theorem is meant to say that `List.take` and `List.takeWhile`
commute.
2024-07-31 03:29:34 +00:00
Kim Morrison
a4015ca36c chore: rename PSigma.exists (#4878) 2024-07-31 03:26:17 +00:00
Kim Morrison
81719f94c9 chore: fix binder explicitness in List.map_subset (#4877) 2024-07-31 03:03:52 +00: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
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
Leonardo de Moura
e3578c2f36 fix: discrepancy theorem vs example (#4493)
When the type of an `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 examples when
their type was a proposition.
This discrepancy often confused users.

Additionally, we considered extending the above behavior to definitions
when
1- When their type is a proposition. However, it still caused disruption
in Mathlib.
2- When their type is provided. That is, we would keep the current
behavior only if `: <type>` was omitted. This would make the elaborator
for `def` much closer to the one for `theorem`, but it 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
closes #4482 Remark: PR #4482 implements option 1 above. We may consider
it again in the future.
2024-06-24 01:18:41 +00:00
Kim Morrison
0f416c6a83 chore: mark releases as prerelease (#4544) 2024-06-24 01:04:04 +00:00
Markus Schmaus
5178c4b6da feat: change succ to + 1 (#4532)
The simp normal form of `succ` is `+ 1`, this changes additional
theorems to use that normal form.
2024-06-24 00:38:22 +00:00
Siddharth
bc6188a70a feat: BitVec.twoPow and lemmas, toward bitblasting multiplication for LeanSAT (#4417)
We add a new definition `BitVec.twoPow w i` to represent `(1#w <<< i)`.
This expression is used to test bits when building the multiplication
bitblaster.

Patch 1/?, being peeled from https://github.com/opencompl/lean4/pull/6.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2024-06-23 22:37:02 +00:00
Leonardo de Moura
33f7865bbb fix: cached results at synthInstance? (#4530)
Synthesized type class instances may introduce new metavariables, and we
should actually cache `AbstractMVarsResult`.

closes #2283
2024-06-23 17:54:35 +00:00
Lean stage0 autoupdater
968aff403b chore: update stage0 2024-06-23 10:09:59 +00:00
Joachim Breitner
1076ca1ead chore: unset parseQuotWithCurrentStage in stage1’s src/stdlib_flags.h (#4537) 2024-06-23 09:44:14 +00:00
Bhavik Mehta
43a9c73556 chore: fix typo and incorrect name in doc (#4404)
Fixes typo "reflexivitiy" to "reflexivity", and changes exact Eq.rfl to
exact rfl, since Eq.rfl does not exist.

(I got something confused wrt the bot message on #4367 and accidentally
closed that one, so making this one instead, which I think satisfies the
requirements it wanted.)

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-06-23 09:06:50 +00:00
Kim Morrison
a92e9c7944 chore: move @[simp] from pred_le to sub_one_le (#4522)
(We already have a simp lemma unfolding `pred` to `· - 1`.)

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-06-23 07:58:38 +00:00
Joachim Breitner
378b02921d refactor: port recOn construction to Lean (#4516)
this is the simplest of the constructions to be ported from C++ to Lean,
so I’ll PR this one first.

This begins to put each construction into its own file, as it was the
case with C++.

For validation I developed this in a separate repository at
https://github.com/nomeata/lean-constructions/tree/fad715e
and checked that all `.recOn` declarations found in Lean and Mathlib are
identical (per `==`) to the ones produced by the C code.
2024-06-23 07:36:27 +00:00
Bolton Bailey
5426a5c8b3 chore: Remove simp from Option.elim, replace with individal simp lemmas (#4504)
This PR removes the `simp` attribute from `Option.elim` and adds it to
two related simp lemmas, `Option.elim_none` and `Option.elim_some`.

This PR comes from some discussion
[here](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/optionEquivLeft_apply.20simp/near/438321459)
about `simps!` feeling too aggressive in unfolding this lemma.
2024-06-23 00:58:25 +00:00
Kim Morrison
d7da45cbe6 chore: fix explicitness of Prod.map lemmas (#4533) 2024-06-22 11:05:19 +00:00
Mac Malone
24d51b90cc fix: lake: remove module dynlib from platform-independent trace (#4478)
Fixes a bug where Lake incorrectly included the module dynlib in a
platform-independent trace. It was incorrectly excluded only external
native libraries from the trace. Also adds a test.
2024-06-22 01:24:23 +00:00
Mac Malone
0d529e18a6 feat: expose flags for the bundled C compiler (#4477)
Expose the C compiler and linker flags used with the bundled compiler
(clang) to Lean code. This is needed to skip the use of `leanc` in Lake.
2024-06-22 01:23:33 +00:00
Sebastian Ullrich
4808eb7c4b chore: Nix: fix cacheRoots 2024-06-21 23:35:38 +02:00
Sebastian Ullrich
5767a597d4 chore: update stage0 2024-06-21 22:04:02 +02:00
Sebastian Ullrich
e665a0d716 chore: Nix: fix update-stage0 2024-06-21 22:02:10 +02:00
Joachim Breitner
073b2cfc83 fix: cdot parser error message range (#4528)
as #4527 describes there is inconsistency between `by`, `case` and
`next` on the one hand who, if the goal isn’t closed, put squiggly
underlines on the first line, and `.`, which so far only squiggled the
dot (which is a very short symbol!)

With this change the same mechanism as used by `case`, namely
`withCaseRef`, is also used for `.`.

There is an argument for the status quo: The `.` tactic is more commonly
used
with further tactics on the same line, and thus there is now a higher
risk that
the user might think that the first tactic is broken. But 

* the same argument does apply to `by` and `case` where there was an
intentional
  choice to do it this way
* consistency and
* a squiggly line just under the short `.` is easy to miss, so it is
actually
better to underlining more here (at least until we have a better way to
  indicate incomplete proofs, which I have hopes for)

Fixes #4527, at least most of it.
2024-06-21 15:06:07 +00:00
David Thrane Christiansen
84e46162b5 feat: more infrastructure for tactic documentation (#4490)
This is the groundwork for a tactic index in generated documentation, as
there was in Lean 3. There are a few challenges to getting this to work
well in Lean 4:
* There's no natural notion of *tactic identity* - a tactic may be
specified by multiple syntax rules (e.g. the pattern-matching version of
`intro` is specified apart from the default version, but both are the
same from a user perspective)
* There's no natural notion of *tactic name* - here, we take the
pragmatic choice of using the first keyword atom in the tactic's syntax
specification, but this may need to be overridable someday.
* Tactics are extensible, but we don't want to allow arbitrary imports
to clobber existing tactic docstrings, which could become unpredictable
in practice.

For tactic identity, this PR introduces the notion of a *tactic
alternative*, which is a `syntax` specification that is really "the same
as" an existing tactic, but needs to be separate for technical reasons.
This provides a notion of tactic identity, which we can use as the basis
of a tactic index in generated documentation. Alternative forms of
tactics are specified using a new `@[tactic_alt IDENT]` attribute,
applied to the new tactic syntax. It is an error to declare a tactic
syntax rule to be an alternative of another one that is itself an
alternative. Documentation hovers now take alternatives into account,
and display the docs for the canonical name.

*Tactic tags*, created with the `register_tactic_tag` command, specify
tags that may be applied to tactics. This is intended to be used by
doc-gen and Verso. Tags may be applied using the `@[tactic_tag TAG1 TAG2
...]` attribute on a canonical tactic parser, which may be used in any
module to facilitate downstream projects introducing tags that apply to
pre-existing tactics. Tags may not be removed, but it's fine to
redundantly add them. The collection of tags, and the tactics to which
they're applied, can be seen using the `#print tactic tags` command.

*Extension documentation* provides a structured way to document
extensions to tactics. The resulting documentation is gathered into a
bulleted list at the bottom of the tactic's docstring. Extensions are
added using the `tactic_extension TAC` command. This can be used when
adding new interpretations of a tactic via `macro_rules`, when extending
some table or search index used by the tactic, or in any other way. It
is a command to facilitate its flexible use with various extension
mechanisms.
2024-06-21 12:49:30 +00:00
Kim Morrison
a1a245df40 chore: missing Prod.map lemmas (#4526) 2024-06-21 11:53:50 +00:00
Kim Morrison
07ee719761 chore: fix statement of List.filter_congr (#4525) 2024-06-21 11:36:07 +00:00
Kim Morrison
ee9996ec89 chore: fix statement of List.filter_congr (#4524) 2024-06-21 11:35:43 +00:00
Markus Schmaus
d2ae678fbf feat: change List.length_cons to use + 1 instead of succ (#4500)
The simp normal form of `succ` is `+ 1`, this changes `List.length_cons`
to use that normal form.
2024-06-21 11:25:07 +00:00
David Thrane Christiansen
2a00d6cf70 doc: more detailed docstring for PersistentEnvExtension (#4501)
Describes the intended modes of use, potential performance tradeoffs,
and data representation in more detail.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
Co-authored-by: Mac Malone <tydeu@hatpress.net>
2024-06-21 08:34:04 +00:00
Sebastian Ullrich
d020a9c5a6 feat: introduce Std (#4499)
Situated between `Init` and `Lean`, provides functionality not in the
prelude to both Lean's implementation and external users
2024-06-21 07:08:45 +00:00
Kim Morrison
301a89aba4 feat: lemmas about List.map (#4521) 2024-06-21 06:40:30 +00:00
Mac Malone
f32780d863 refactor: lake: more robust trace reading (#4518)
The recent change of the trace format exposed some unexpected issues
with Lake's tracing handling. This aims to fix that.

Lake will now perform a rebuild if the trace file is invalid/unreadable.
However, it will still fall back to modification times if the trace file
is missing. Also, Lake is now backwards compatible with the previous
pure numeric traces (and tolerates the absence of a `log` field in the
JSON trace).
2024-06-21 01:43:05 +00:00
Sebastian Ullrich
d6eab393f4 chore: fix benchmark 2024-06-20 18:18:41 +02:00
Sebastian Ullrich
1f732bb3b7 fix: missing unboxing in interpreter when loading initialized value (#4512)
Fixes #4457
2024-06-20 10:06:24 +00:00
Joe Hendrix
7d7f378e02 feat: complete Int div/mod simprocs (#3850)
This PR introduces complete simprocs for all the Int versions of
div/mod, and makes some small refactoring of Int lemmas and
library_search.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-06-20 04:42:31 +00:00
Kim Morrison
95db616cb6 chore: cherry-pick stray test from #3850 (#4509)
#3850 included a commit that added an extra test for `exact?`, but was
otherwise unrelated the to PR. It also removed a test. I've
cherry-picked that test over, and restored the deleted test, and next
will remove the commit from #3850.
2024-06-20 01:00:45 +00:00
Leonardo de Moura
45c5d009d6 fix: dsimp missing theorems for literals (#4467) 2024-06-20 00:35:53 +00:00
Leonardo de Moura
458835360f fix: [implemented_by] at functions defined by well-founded recursion (#4508)
closes #2899
2024-06-20 00:06:38 +00:00
Leonardo de Moura
3e05b0641b chore: fix test 2024-06-20 01:05:52 +02:00
Leonardo de Moura
53be53f5ae chore: update stage0 2024-06-20 01:05:52 +02:00
Leonardo de Moura
dac1dacc5b feat: add Rewrite.Config.newGoals field
It is not used yet. We need a update-stage0.
2024-06-20 01:05:52 +02:00
Leonardo de Moura
d3a7569c97 refactor: move ApplyNewGoals and ApplyConfig to Init 2024-06-20 01:05:52 +02:00
Leonardo de Moura
49f058cb76 feat: open _root_.<namespace> (#4505)
closes #3045
2024-06-19 21:59:46 +00:00
Joachim Breitner
bc047b8530 refactor: port mk_definition_inferring_unsafe to Lean (#4498)
this already can be used in two places, and will be used more as I port
more constructions. Hope the location in `Lean.Environment` is ok.
2024-06-19 18:26:19 +00:00
JovanGerb
c7c50a8bec chore: fix linter errors (#4502)
The linters in Batteries can be used to spot mistakes in Lean. See the
message on
[Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Go-to-def.20on.20typeclass.20fields.20and.20type-dependent.20notation/near/442613564).
These are the different linters with errors:

- unusedArguments:
There are many unused instance arguments, especially a redundant `[Monad
m]` is very common
- checkUnivs:
There was a problem with universes in a definition in
`Init.Control.StateCps`. I fixed it by adding a `variable` statement for
the implicit arguments in the file.
- defLemma:
many proofs are written as `def` instead of `theorem`, most notably
`rfl`. Because `rfl` is used as a match pattern, it must be a def. Is
this desirable?
The keyword `abbrev` is sometimes used for an alias of a theorem, which
also results in a def. I would want to replace it with the `alias`
keyword to fix this, but it isn't available.
- dupNamespace:
I fixed some of these, but left `Tactic.Tactic` and `Parser.Parser` as
they are as these seem intended.
- unusedHaveSuffices:
  I cleaned up a few proofs with unused `have` or `suffices`
- explicitVarsOfIff:
  I didn't fix any of these, because that would be a breaking change.
- simpNF:
I didn't fix any of these, because I think that requires knowing the
intended simplification order.
2024-06-19 18:24:08 +00:00
Leonardo de Moura
de269060d1 chore: fix tests 2024-06-19 20:21:34 +02:00
Leonardo de Moura
e8f768f9fd chore: cleanup PersistentHashMap.lean 2024-06-19 20:21:34 +02:00
Leonardo de Moura
0783d0fcbe chore: re-enable #reduce elaborator 2024-06-19 20:21:34 +02:00
Leonardo de Moura
2518105bd9 chore: update stage0 2024-06-19 20:21:34 +02:00
Leonardo de Moura
9096d6fc71 fix: remove PersistentHashMap.size
It is buggy and was unnecessary overhead.

closes #3029
2024-06-19 20:21:34 +02:00
Mario Carneiro
0a1a855ba8 fix: validate UTF-8 at C++ -> Lean boundary (#3963)
Continuation of #3958. To ensure that lean code is able to uphold the
invariant that `String`s are valid UTF-8 (which is assumed by the lean
model), we have to make sure that no lean objects are created with
invalid UTF-8. #3958 covers the case of lean code creating strings via
`fromUTF8Unchecked`, but there are still many cases where C++ code
constructs strings from a `const char *` or `std::string` with unclear
UTF-8 status.

To address this and minimize accidental missed validation, the
`(lean_)mk_string` function is modified to validate UTF-8. The original
function is renamed to `mk_string_unchecked`, with several other
variants depending on whether we know the string is UTF-8 or ASCII and
whether we have the length and/or utf8 char count on hand. I reviewed
every function which leads to `mk_string` or its variants in the C code,
and used the appropriate validation function, defaulting to `mk_string`
if the provenance is unclear.

This PR adds no new error handling paths, meaning that incorrect UTF-8
will still produce incorrect results in e.g. IO functions, they are just
not causing unsound behavior anymore. A subsequent PR will handle adding
better error reporting for bad UTF-8.
2024-06-19 14:05:48 +00:00
Joachim Breitner
c4718a87ab refactor: constructions: modify environment in lean world (#4474)
this is a first step towards porting the code `constructions.cpp` to
Lean: It leaves the construction of the `Declaration` untouched, but
moves adding the declarations to the environment, and setting various
attributes, to the Lean world.

This allows the remaining logic (the construction of the `Declaration`)
to be implemented in Lean separately and easily compared to the C++
implementation, before we replace that too.

To that end, `Declaraion` gains an `BEq` instance.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
Co-authored-by: Arthur Adjedj <arthur.adjedj@ens-paris-saclay.fr>
2024-06-19 08:58:53 +00:00
Leonardo de Moura
357b52928f fix: global definition shadowing a local one when using dot-notation (#4497)
closes #3079
2024-06-19 05:52:45 +00:00
Mac Malone
bd45c0cd04 fix: lake: visit direct deps before adding manifest entries (#4485)
Fixes a bug in #4371 where the version of a package used by a dependency
would take precedence over that of a the same package as a direct
dependency if that package had a a manifest. This was because the direct
dependency's manifest entries were added before all the direct
dependencies were visited.
2024-06-19 02:49:59 +00:00
Mac Malone
f9952e8c39 refactor: touchup require syntax & docs (#4496)
A set of general tweaks of the `require` syntax and docs that provide a
base for #4495.

The sole significant behavioral change is that the `name` field of a
`require` in TOML now falls back to being interpreted as a simple string
name if the value is not a valid Lean identifier. This means that a
require for a package like `doc-gen4` can be written without French
quotes.
2024-06-19 02:49:57 +00:00
Kyle Miller
1b5b91cccf doc: add docstrings for dsimp configuration (#4258)
The dsimp configuration is a subset of the simp configuration, and so
it's a matter of copying the docstrings.
2024-06-19 00:05:25 +00:00
JovanGerb
74f1373706 chore: remove redundant if-else in isDefEqQuickOther (#4388)
I removed a redundant `if tFn.isMVar || sFn.isMVar then ... else return
LBool.undef` in the `else` clause of
```
if !tFn.isMVar && !sFn.isMVar then
  return LBool.undef
else
```
2024-06-18 23:44:29 +00:00
JovanGerb
c87205bc9b fix: eta reduce mvar assignments in isDefEq (#4387)
I made a modification to the `mkLambdaFVars` function, adding a
`etaReduce : Bool` parameter that determines whether a new lambda of the
form `fun x => f x` should be replaced by `f`. I then set this option to
true at `isDefEq` when processing metavariable assignments.

This means that many unnecessary eta unreduced expression are now
reduced. This is beneficial for users, so that they do not have to deal
with such unreduced expressions. It is also beneficial for performance,
leading to a 0.6% improvement in build instructions. Most notably,
`Mathlib.Algebra.DirectLimit`, previously a top 50 slowest file, has
sped up by 40%.

Quite a number of proof in mathlib broke. Many of these involve removing
a now unnecessary `simp only`. In other cases, a simp or rewrite doesn't
work anymore, such as a `simp_rw [mul_comm]` that was used to rewrite
`fun x => 2*x`, but now this term has turned into `HMul.hMul 2`.

Closes #4386
2024-06-18 23:41:40 +00:00
Leonardo de Moura
294b1d5839 chore: cleanup (#4494)
closes #4287
closes #4288
2024-06-18 23:39:16 +00:00
Leonardo de Moura
167771923e test: for isDefEq issue (#4492)
The issue has already been fixed in previous PRs.

closes #2461
2024-06-18 17:54:55 +00:00
Sebastian Ullrich
eb67654ae6 feat: incremental next and tactic if (#4459) 2024-06-18 12:36:59 +00:00
Joachim Breitner
6a8cb7ffa0 chore: nix shell: add gdb (#4476)
seems to work better when it comes from the same nixpkgs that we use for
building, it seems. And is plain convenient.
2024-06-18 11:07:20 +00:00
Kim Morrison
face4cef75 feat: complete API for List.replicate (#4487)
This is not the most exciting place to start, but I started here to:
* pick a function with little development in Batteries and Mathlib, so I
wouldn't have conflicts
* that is easy!
* to see how much effort it is to get fairly complete coverage
* and to set up some infrastructure to be used later, i.e.
`tests/lean/run/list_simp.lean`
2024-06-18 08:30:09 +00:00
Kim Morrison
6cad341764 chore: @[simp] List.getElem?_eq_getElem (#4488)
This is often helpful, and Mathlib doesn't mind.
2024-06-18 08:29:51 +00:00
Kim Morrison
2995e74133 chore: missing withSynthesize in #check_tactic (#4489)
Ran into this writing `#check_simp` tests for `List`. I guess it wasn't
a probably for `Bool` / `Prop`.
2024-06-18 05:24:45 +00:00
Kim Morrison
d768f46ba6 chore: @[simp] Nat.min_assoc (#4486) 2024-06-18 03:08:34 +00:00
Leonardo de Moura
97588301e1 fix: deprecated warning position at simp arguments (#4484)
closes #4452
2024-06-17 23:21:14 +00:00
Leonardo de Moura
fca87da2d4 fix: simp support for OfNat instances that are functions (#4481)
closes #4462
2024-06-17 22:01:25 +00:00
Leonardo de Moura
3c4d6ba864 feat: new #reduce elaborator
closes #4465
2024-06-17 23:27:34 +02:00
Leonardo de Moura
2c83e080f7 chore: update stage0 2024-06-17 23:27:34 +02:00
Leonardo de Moura
8f023b85c5 chore: move #reduce parser to Init/Notation.lean 2024-06-17 23:27:34 +02:00
Leonardo de Moura
06731f99d4 chore: missing instances (#4479)
cc @shigoel
2024-06-17 20:14:00 +00:00
Joachim Breitner
59a09fb4e7 feat: use priorities to ensure simp applies eqational lemmas in order (#4434)
This assigns priorities to the equational lemmas so that more specific
ones
are tried first before a possible catch-all with possible
side-conditions.

We assign very low priorities to match the simplifiers behavior when
unfolding
a definition, which happens in `simpLoop`’ `visitPreContinue` after
applying
rewrite rules.

Definitions with more than 100 equational theorems will use priority 1
for all
but the last (a heuristic, not perfect).

fixes #4173, to some extent.
2024-06-17 18:22:28 +00:00
Mac Malone
42c4a770c2 chore: lake: fix tests/init cleanup (#4468)
Forgot to update `tests/init/clean.sh` when I updated the test and
`.gitignore`.
2024-06-17 16:17:59 +00:00
Kim Morrison
d334e96275 chore: add forgotten deprecation (#4475) 2024-06-17 08:46:44 +00:00
Kim Morrison
e9caf40493 feat: cleanup @[simp] annotations for List (#4473)
These are mostly (sensible) `@[simp]` annotations that Mathlib adds.
2024-06-17 07:31:48 +00:00
Kim Morrison
a09726bb94 feat: lemmas about List.filter (#4472)
This upstreams some lemmas from Batteries, giving dumbed-down proofs, as
I do not (yet?) want to move up `List.Sublist`.
2024-06-17 06:53:16 +00:00
Markus Schmaus
1cf71e54cf feat: add missing theorems for + 1 and - 1 normal form (#4242)
`Nat.succ_eq_add_one` and `Nat.pred_eq_sub_one` are now simp lemmas. For
theorems about `Nat.succ` or `Nat.pred` without corresponding theorem
for `+ 1` or `- 1`, this adds the corresponding theorem.
2024-06-17 05:35:32 +00:00
Kim Morrison
2efcbfe803 feat: improvements to List.set and List.concat API (#4470) 2024-06-17 05:10:35 +00:00
Kim Morrison
03d01f4024 chore: reorganisation of List API (#4469)
This PR neither adds nor removes material, but improves the organization
of `Init/Data/List/*`.

These files are essentially completely re-ordered, to ensure that
material is developed in a consistent order between `List.Basic`,
`List.Impl`, `List.BasicAux`, and `List.Lemmas`.

Everything is organised in subsections, and I've added some module docs.
2024-06-17 04:21:53 +00:00
John Tristan
f237fb67eb doc: documenting Char and upstreaming extensionality from batteries (#4438)
* Basic documentation for characters
* Upstreamed two extensionality theorems from batteries

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-06-16 00:43:34 +00:00
Kim Morrison
e10a37d80d feat: switching List lookup normal forms to L[n] and L[n]? (#4400)
This is presumably going to have significant breakage downstream.
2024-06-15 07:35:36 +00:00
Joachim Breitner
fe0cb97c5d feat: use lazy MessageData for omega errors (#4360)
presumably this avoids unnecessary work when `omega` is used in tactic
combinators where the error message is never seen. Measurement did not
show
any significant changes, though.

With an artificial sleep in
```diff
diff --git a/src/Lean/Elab/Tactic/Omega/Frontend.lean b/src/Lean/Elab/Tactic/Omega/Frontend.lean
index fd297eef60..31ea3f6bd0 100644
--- a/src/Lean/Elab/Tactic/Omega/Frontend.lean
+++ b/src/Lean/Elab/Tactic/Omega/Frontend.lean
@@ -538,6 +538,7 @@ def formatErrorMessage (p : Problem) : OmegaM MessageData := do
     else
       let as ← atoms
       return .ofLazyM (es := as) do
+        IO.sleep 10000
         let mask ← mentioned as p.constraints
         let names ← varNames mask
         return m!"a possible counterexample may satisfy the constraints\n" ++
```
I can observe that `omega` is slow and `try omega` fast, so it seems to
work at least.
2024-06-14 20:21:37 +00:00
JovanGerb
c96797eb93 fix: show argument name in implicit argument error (#4426)
When an implicit argument cannot be inferred, the error should show the
name of the argument.

Showing the argument name in the error message for an uninstantiated
metavariable was introduced in da33f498f5,
but this implementation causes some argument names to get lost.
2024-06-14 18:08:42 +00:00
JovanGerb
4798c8418c perf: replace hasMVar by hasExprMVar in CollectMVars, FindMVar (#4451)
The modules `CollectMVars` and `FindMVars` only search for expression
metavariables and not level metavariables, so we should use
`Expr.hasExprMVar` instead of `Expr.hasMVar`.
2024-06-14 18:06:00 +00:00
David Thrane Christiansen
456ed44550 feat: add a linter for local vars that clash with their constructors (#4301)
This came up when watching new Lean users in a class situation. A number
of them were confused when they omitted a namespace on a constructor
name, and Lean treated the variable as a pattern that matches anything.

For example, this program is accepted but may not do what the user
thinks:
```
inductive Tree (α : Type) where
  | leaf
  | branch (left : Tree α) (val : α) (right : Tree α)

def depth : Tree α → Nat
  | leaf => 0
```
Adding a `branch` case to `depth` results in a confusing message.

With this linter, Lean marks `leaf` with:
```
Local variable 'leaf' resembles constructor 'Tree.leaf' - write '.leaf' (with a dot) or 'Tree.leaf' to use the constructor.
note: this linter can be disabled with `set_option linter.constructorNameAsVariable false`
```

Additionally, the error message that occurs when invalid names are
applied in patterns now suggests similar names. This means that:
```
def length (list : List α) : Nat :=
  match list with
  | nil => 0
  | cons x xs => length xs + 1
```
now results in the following warning on `nil`:
```
warning: Local variable 'nil' resembles constructor 'List.nil' - write '.nil' (with a dot) or 'List.nil' to use the constructor.
note: this linter can be disabled with `set_option linter.constructorNameAsVariable false`
```

and error on `cons`:
```
invalid pattern, constructor or constant marked with '[match_pattern]' expected

Suggestion: 'List.cons' is similar
```

The list of suggested constructors is generated before the type of the
pattern is known, so it's less accurate, but it truncates the list to
ten elements to avoid being overwhelming. This mostly comes up with
`mk`.
2024-06-14 13:03:09 +00:00
Sebastian Ullrich
237f392cc1 chore: update stage0 2024-06-14 12:35:45 +02:00
Sebastian Ullrich
5eb5fa49cf chore: Nix: fix update-stage0-commit 2024-06-14 12:35:14 +02:00
L
5d2403535a feat: default pp if pp expr/syntax/level without context (#4433)
This restores the behavior prior to
9f6bbfa106
for `MessageData.ofSyntax` `MessageData.ofExpr`, and
`MessageData.ofLevel` while staying within the new `.ofLazy` paradigm.

Also adds some documentation to help developers understand the missing
context issue.

Closes #4432

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-06-14 08:55:49 +00:00
Kim Morrison
2cf478cbbe chore: prefer · == a over a == · (#3056)
We recently discovered inconsistencies in Mathlib and Std over the
ordering of the arguments for `==`.

The most common usage puts the "more variable" term on the LHS, and the
"more constant" term on the RHS, however there are plenty of exceptions,
and they cause unnecessary pain when switching (particularly, sometimes
requiring otherwise unneeded `LawfulBEq` hypotheses).

This convention is consistent with the (obvious) preference for `x == 0`
over `0 == x` when one term is a literal.

We recently updated Std to use this convention
https://github.com/leanprover/std4/pull/430

This PR changes the two major places in Lean that use the opposite
convention, and adds a suggestion to the docstring for `BEq` about the
preferred convention.
2024-06-14 04:08:45 +00:00
Kim Morrison
b096e7d5f2 chore: make Name.isInternalDetail public, to remove duplication downstream (#4454)
This private function is duplicated downstream, so move it to the
`Lean.Name` namespace.
2024-06-14 01:55:52 +00:00
Mac Malone
1835dd123d feat: configuration DSL touchups (#4439)
This incorporates many general Lake DSL changes from #2439 and adds some
new related changes.

* Rework configuration names (e.g., `package <name>`)
  * String literals ca now be used instead of identifiers for names.
* The name syntax is now optional and can instead be set via the `name`
field.
* Avoid French quotes in `lake new` / `lake init` templates (except in
`lean_lib` names). This is not done for `lean_lib` because it needs a
proper identifier for its root. It could use a string and reparse it as
an identifier, but this seems liable to produce confusion.
* The `exe` templates now names it main module `Main` like the `std`
template.
* Improve `math` template error if `lean-toolchain` fails to download.
* Lake now logs a warning rather than an error on unknown configuration
fields. This increases the Lake DSL's cross-version compatibility.

Closes #3385.
2024-06-14 00:17:43 +00:00
Mac Malone
db74ee9e83 feat: lake: reliably cache logs and hashes (#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. `.hash`
files are deleted as part of the build and always regenerate with
`--rehash`.

Closes #2751.
2024-06-13 23:15:49 +00:00
Mac Malone
285a313078 chore: src/lake/lakefile.toml (#4446)
Use a TOML file for the Lake configuration of the `src/lake` directory
instead of a Lean file. This avoids having to load a version of the Lake
library to build Lake.
2024-06-13 16:56:32 +00:00
David Thrane Christiansen
8fef03d1cc feat: support Lake for building Lean core oleans (#3886)
This is from a ~~pair~~triple programming session with @tydeu and
@mhuisi.

If stage 1 is built with `-DUSE_LAKE=ON`, the CMake run will generate
`lakefile.toml` files for the root, `src`, and `tests`. These Lake
configuration files can then be used to build core oleans. While they do
not yet allow Lake to be used to build the Lean binaries. they do allow
Lake to be used for working interactively with the Lean source. In our
preliminary experiments, this allowed updates to `Init.Data.Nat` to be
noticed automatically when reloading downstream files, rather than
requiring a full manual compiler rebuild. This will make it easier to
work on the system.

As part of this change, Lake is added to stage 0. This allows Lake to
function in `src`, which uses the stage 0 toolchain.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-06-13 16:18:24 +00:00
Sebastian Ullrich
749bf9c279 fix: bad 'unknown package' error message (#4424)
This message is older than corresponding, better checks in Lake and
vscode-lean4

Fixes #4419

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2024-06-13 15:48:20 +00:00
Sebastian Ullrich
7b971b90c5 fix: redundant and wrong case in forEachModuleInDir (#4425)
Calling `f` on `dir.lean` for each `dir` is unnecessary as we call it on
each file anyway and the check was wrong because it ignored
`entry.root`.
2024-06-13 15:40:27 +00:00
Marc Huisinga
3119fd0240 fix: make watchdog more resilient against badly behaving clients (#4443)
This PR addresses some non-critical but annoying issues that sometimes
cause the language server to report an error:
- When using global search and replace in VS Code, the language client
sends `textDocument/didChange` notifications for documents that it never
told the server to open first. Instead of emitting an error and crashing
the language server when this occurs, we now instead ignore the
notification. Fixes #4435.
- When terminating the language server, VS Code sometimes still sends
request to the language server even after emitting a `shutdown` request.
The LSP spec explicitly forbids this, but instead of emitting an error
when this occurs, we now error requests and ignore all other messages
until receiving the final `exit` notification. Reported on Zulip several
times over the years but never materialized as an issue, e.g.
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Got.20.60shutdown.60.20request.2C.20expected.20an.20.60exit.60.20notification/near/441914289.
- Some language clients attempt to reply to the file watcher
registration request before completing the LSP initialization dance. To
fix this, we now only send this request after the initialization dance
has completed. Fixes #3904.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-06-13 13:48:36 +00:00
Sebastian Ullrich
16cad2b45c chore: CI: do not fetch complete history 2024-06-13 14:50:49 +02:00
Sebastian Ullrich
6d265b42b1 chore: CI: fix github.event.pull_request.merge_commit_sha sometimes not being available 2024-06-13 14:47:42 +02:00
Sebastian Ullrich
c5120c1d0d chore: CI: fix Wasm release-ci (#4442) 2024-06-13 11:58:49 +00:00
Sebastian Ullrich
37f8b0390d chore: CI: fix workflow change breaking unrebased PRs (#4441) 2024-06-13 09:56:31 +00:00
Sebastian Ullrich
bd3b466f2f chore: CI: fix speedcenter tests (#4440) 2024-06-13 10:53:47 +02:00
Mac Malone
f3274d375a refactor: lake: simplify load code (#4371)
Simplifies the Lake dependency resolution code. Largely split from
#3998.
2024-06-13 03:22:47 +00:00
Sebastian Ullrich
a8de4b3b06 chore: CI: use Namespace runners (#4427) 2024-06-12 16:06:41 +00:00
Sebastian Ullrich
8d3be96024 fix: tactics in terms in tactics may break incremental reporting (#4436)
A pending tactic mvar managed to escape into an unexpected context in
specific circumstances.

```lean
example : True := by
  · rw [show 0 = 0 by rfl]
```
* Term elaboration of the `show` creates a pending mvar for the `by rfl`
proof
* `rw` fails with an exception because the pattern does not occur in the
target
* `cdot` catches the exception and admits the goal
* `Term.runTactic` [synthesizes all pending mvars from the tactic's
execution](5f9dedfe5e/src/Lean/Elab/SyntheticMVars.lean (L350)),
including the `by rfl` proof. But this would not have happened without
`cdot` as the exception would have skipped that invocation!
* Now incrementality is confused because the nested `by rfl` proof is
unexpectedly run in the same context as the top-level proof, writing to
the wrong promise, and the error message is lost

Solution: disable incrementality for these pending mvars
2024-06-12 14:59:24 +00:00
hwatheod
bedcbfcfee chore: fix typo in trace.split.failure error message (#4431)
should be "failure" not "failures"

Co-authored-by: q r <qr@abc.local>
2024-06-12 05:57:29 +00:00
Leonardo de Moura
ce6ebd1044 feat: dsimprocs for ite and dite (#4430) 2024-06-11 23:36:18 +00:00
Leonardo de Moura
ab73ac9d15 fix: missing simproc for BitVec equality (#4428) 2024-06-11 22:05:28 +00:00
Leonardo de Moura
3bd39ed8b6 perf: a isDefEq friendly Fin.sub (#4421)
The performance issue at #4413 is due to our `Fin.sub` definition.
```
def sub : Fin n → Fin n → Fin n
  | ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + (n - b)) % n, mlt h⟩
```
Thus, the following runs out of stack space
```
example (a : UInt64) : a - 1 = a :=
  rfl
```
at the `isDefEq` test
```
(a.val.val + 18446744073709551615) % 18446744073709551616 =?= a.val.val
```

From the user's perspective, this timeout is unexpected since they are
using small numerals, and none of the other `Fin` basic operations (such
as `Fin.add` and `Fin.mul`) suffer from this problem.

This PR implements an inelegant solution for the performance issue. It
redefines `Fin.sub` as
```
def sub : Fin n → Fin n → Fin n
  | ⟨a, h⟩, ⟨b, _⟩ => ⟨((n - b) + a) % n, mlt h⟩
```
This approach is unattractive because it relies on the fact that
`Nat.add` is defined using recursion on the second argument.

The impact on this repo was small, but we want to evaluate the impact on
Mathlib.

closes #4413
2024-06-11 17:18:11 +00:00
Sebastian Ullrich
5f9dedfe5e chore: slightly more informative trace.Elab.snapshotTree 2024-06-11 10:44:04 +02:00
Kim Morrison
2a2b276ede chore: unify String.csize : Nat and Char.utf8Size : UInt32 as Char.size : Nat (#4357)
It seems:
* there was no actual need for the UInt32 valued version
* downstream we were getting duplicative lemmas about both
* so lets reduce the API surface area!

If anyone would prefer the remaining function is still called
`Char.utf8Size` I will happily change it. (`size` is hopefully still
unambiguous, and it's helpful to rename here so we can give a
deprecation warning that explains the type signature change.)

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2024-06-11 02:51:18 +00:00
Leonardo de Moura
ec775df6cc fix: rw should not include existing goal metavariables in the resulting subgoals (#4385)
closes #4381
2024-06-11 02:50:58 +00:00
Leonardo de Moura
c8e668a9ad fix: occurs check at metavariable types (#4420)
closes #4405
2024-06-11 00:16:19 +00:00
Leonardo de Moura
a1c8a941f0 fix: universe parameter order discrepancy between theorem and def (#4408)
Before this commit, the `theorem` and `def` declarations had different
universe parameter orders.
For example, the following `theorem`:
```
theorem f (a : α) (f : α → β) : f a = f a := by
  rfl
```
was elaborated as
```
theorem f.{u_2, u_1} : ∀ {α : Sort u_1} {β : Sort u_2} (a : α) (f : α → β), f a = f a :=
  fun {α} {β} a f => Eq.refl (f a)
```
However, if we declare `f` as a `def`, the expected order is produced.
```
def f.{u_1, u_2} : ∀ {α : Sort u_1} {β : Sort u_2} (a : α) (f : α → β), f a = f a :=
  fun {α} {β} a f => Eq.refl (f a)
```

This commit fixes this discrepancy.

@semorrison @jcommelin: This might be a disruptive change to Mathlib,
but it is better to fix the issue asap. I am surprised nobody has
complained about this issue before. I discovered it while trying to
reduce discrepancies between `theorem` and `def` elaboration.
2024-06-10 23:37:52 +00:00
L
6a7bed94d3 fix: kernel exception from fvars left from ?m a b instantiation (#4410)
Closes #4375

The following example raises `error: (kernel) declaration has free
variables '_example'`:
```lean
example: Nat → Nat :=
  let a : Nat := Nat.zero
  fun (_ : Nat) =>
    let b : Nat := Nat.zero
    (fun (_ : a = b) => 0) (Eq.refl a)
```

During elaboration of `0`, `elabNumLit` creates a synthetic mvar
`?_uniq.16` which gets abstracted by `elabFun` to `?_uniq.16 :=
?_uniq.50 _uniq.6 _uniq.12`. The `isDefEq` to `instOfNatNat 0` results
in:
```
?_uniq.50 :=
  fun (x._@.4375._hyg.13 : Nat) =>
    let b : Nat := Nat.zero
    fun (x._@.4375._hyg.23 : Eq.{1} Nat _uniq.4 b) =>
      instOfNatNat 0
```

This has a free variable `_uniq.4` which was `a`.

When the application of `?_uniq.50` to `#[#2, #0]` is instantiated, the
`let b : Nat := Nat.zero` blocks the beta-reduction and `_uniq.4`
remains in the expression.

fix: add `(useZeta := true)` here:

ea46bf2839/src/Lean/MetavarContext.lean (L567)
2024-06-10 19:02:27 +00:00
Henrik Böving
366f3ac272 feat: order the output of #print axioms (#4416)
Closes #4415
2024-06-10 09:17:05 +00:00
Sebastian Ullrich
ea46bf2839 fix: non-incremental command blocking further incremental reporting in macro (#4407)
As [reported on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/v4.2E9.2E0-rc1.20discussion/near/443356495).
2024-06-08 16:50:15 +00:00
Sebastian Ullrich
adfd438164 fix: incremental reuse leading to goals in front of the text cursor being shown (#4395)
As [reported on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/maybe.20a.20cache.20bug.3F).

We expected that for sound reuse of elaboration results, it is
sufficient to compare the old and new syntax tree's structure and atoms
including position info, but not the whitespace in between them.
However, we have at least one request handler, the goal view, that
inspects the whitespace after a tactic and thus could return incorrect
results on reuse. For now we implement the straightforward fix of
checking the whitespace as well. Alternatives like updating the
whitespace stored in the reused info tree are tbd.

This has the slight disadvantage that adding whitespace at the end of a
tactic will re-execute it (or the entire body, but not the header, if
the body is not a tactic block), but only up to typing the first
character of the next tactic or command.
2024-06-08 15:08:14 +00:00
Mac Malone
748eab9511 refactor: lake: inputBinFile / inputTextFile (#4384)
Deprecates `inputFile` and replaces it with `inputBinFile` and
`inputTextFile`. `inputTextFile` normalizes line endings, which helps
ensure text file traces are platform-independent.
2024-06-08 01:20:46 +00:00
Leonardo de Moura
fd4281a636 fix: misleading type at Option.forM (#4403)
The type uses `PUnit`, but the `pure ()` in the body was forcing the
implicit universe level at `PUnit` to be `1`.

We should probably elaborate `def`s like we elaborate theorems when the
resulting type is provided. This kind of mistake is hard to spot.
2024-06-07 23:33:15 +00:00
Markus Himmel
2d05ff8a48 perf: linearity in HashMap.(insert|erase) (#4372)
The speedcenter doesn't seem to care much, but in [my
benchmark](2e04b4f844/Hashmap/Benchmark/InsertReplace.lean)
for replace-heavy workloads, the fixed code is about 40% faster.
2024-06-07 22:41:39 +00:00
Leonardo de Moura
b02c1c56ab fix: improve split discriminant generalization strategy (#4401)
This commit also
- improves `split` error messages.
- adds `trace.split.failure` option.
- uses new convention for trace messages.

closes #4390
2024-06-07 21:35:09 +00:00
Kim Morrison
73348fb083 chore: make Array.reverse_data proof more robust (#4399)
This proof was breaking during a refactor, so making it more robust
first.
2024-06-07 19:17:03 +00:00
Henrik Böving
18264ae62e feat: getBitVecValue? understands BitVec.ofNatLt (#4391) 2024-06-07 17:43:08 +00:00
Sebastian Ullrich
7b72458392 chore: build Lake again 2024-06-07 13:59:22 +02:00
Sebastian Ullrich
bfcaaa3d9d chore: update stage0 2024-06-07 13:59:22 +02:00
Sebastian Ullrich
0768b508e6 chore: temporarily avoid building Lake 2024-06-07 13:59:22 +02:00
Sebastian Ullrich
d644b377bb chore: update stage0 2024-06-07 13:59:22 +02:00
Sebastian Ullrich
d85d3d5f3a fix: accidental ownership with specialization 2024-06-07 13:59:22 +02:00
Kim Morrison
745d77b068 chore: upstream @[simp] attribute (#4389)
Very minor, but progress towards deleting a downstream file.
2024-06-07 03:32:18 +00:00
Kyle Miller
63739a42f3 chore: clear releases_drafts for start of 4.10.0 (#4377) 2024-06-06 23:45:54 +00:00
Mac Malone
a99007ac75 perf: remove @[inline] from NameMap.find? (#4382)
This `@[inline]` causes Lean to respecialize `RBMap.find?` to `NameMap`
at each call site of `NameMap.find?`, creating lots of unnecessary
duplicate IR.
2024-06-06 22:53:14 +00:00
Henrik Böving
b9bfd30514 chore: remove partial TODO (#4380) 2024-06-06 18:04:55 +00:00
Leonardo de Moura
0a0f1d7cc7 fix: variable must execute pending tactics and elaboration problems (#4370)
closes #2226
closes #3214
2024-06-06 13:06:18 +00:00
Kim Morrison
ba97928fbf chore: begin development cycle for v4.10.0 (#4374) 2024-06-06 12:06:13 +00:00
2434 changed files with 75030 additions and 13699 deletions

View File

@@ -5,6 +5,7 @@
* Include the link to your `RFC` or `bug` issue in the description.
* If the issue does not already have approval from a developer, submit the PR as draft.
* The PR title/description will become the commit message. Keep it up-to-date as the PR evolves.
* A toolchain of the form `leanprover/lean4-pr-releases:pr-release-NNNN` for Linux and M-series Macs will be generated upon build. To generate binaries for Windows and Intel-based Macs as well, write a comment containing `release-ci` on its own line.
* If you rebase your PR onto `nightly-with-mathlib` then CI will test Mathlib against your PR.
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels yourself, by writing a comment containing one of these labels on its own line.
* Remove this section, up to and including the `---` before submitting.

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
@@ -103,7 +114,7 @@ jobs:
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
else
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }}) --jq '.labels'"
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels')"
if echo "$labels" | grep -q "release-ci"; then
check_level=2
elif echo "$labels" | grep -q "merge-ci"; then
@@ -122,9 +133,8 @@ jobs:
script: |
const level = ${{ steps.set-level.outputs.check-level }};
console.log(`level: ${level}`);
// use large runners outside PRs where available (original repo)
// disabled for now as this mostly just speeds up the test suite which is not a bottleneck
// let large = ${{ github.event_name != 'pull_request' && github.repository == 'leanprover/lean4' }} ? "-large" : "";
// use large runners where available (original repo)
let large = ${{ github.repository == 'leanprover/lean4' }};
let matrix = [
{
// portable release build: use channel with older glibc (2.27)
@@ -143,7 +153,7 @@ jobs:
},
{
"name": "Linux release",
"os": "ubuntu-latest",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"release": true,
"check-level": 0,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
@@ -155,7 +165,7 @@ jobs:
},
{
"name": "Linux",
"os": "ubuntu-latest",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"check-stage3": level >= 2,
"test-speedcenter": level >= 2,
"check-level": 1,
@@ -166,7 +176,7 @@ jobs:
"check-level": 2,
"CMAKE_PRESET": "debug",
// exclude seriously slow tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress'"
},
// TODO: suddenly started failing in CI
/*{
@@ -194,7 +204,7 @@ jobs:
"os": "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"check-level": 1,
"check-level": 0,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
@@ -216,21 +226,19 @@ jobs:
},
{
"name": "Linux aarch64",
"os": "ubuntu-latest",
"os": "nscloud-ubuntu-22.04-arm64-4x8",
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"check-level": 2,
"cross": true,
"cross_target": "aarch64-unknown-linux-gnu",
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*"
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*"
},
{
"name": "Linux 32bit",
"os": "ubuntu-latest",
// Use 32bit on stage0 and stage1 to keep oleans compatible
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86",
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/",
"cmultilib": true,
"release": true,
"check-level": 2,
@@ -241,7 +249,7 @@ jobs:
"name": "Web Assembly",
"os": "ubuntu-latest",
// Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32",
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32 -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/",
"wasm": true,
"cmultilib": true,
"release": true,
@@ -249,7 +257,7 @@ jobs:
"cross": true,
"shell": "bash -euxo pipefail {0}",
// Just a few selected tests because wasm is slow
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean\""
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_libuv\\.lean\""
}
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
@@ -281,28 +289,34 @@ jobs:
CXX: c++
MACOSX_DEPLOYMENT_TARGET: 10.15
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Install Nix
uses: cachix/install-nix-action@v18
with:
install_url: https://releases.nixos.org/nix/nix-2.12.0/install
uses: DeterminateSystems/nix-installer-action@main
if: runner.os == 'Linux' && !matrix.cmultilib
- name: Install MSYS2
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 libuv git: zip: unzip: diffutils: binutils: tree: zstd tar:"
if: runner.os == 'Windows'
- name: Install Brew Packages
run: |
brew install ccache tree zstd coreutils gmp
brew install ccache tree zstd coreutils gmp libuv
if: runner.os == 'macOS'
- name: Checkout
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 }}
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
# master (as the workflow files themselves are always taken from the merge)
# (needs to be after "Install *" to use the right shell)
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overriden)
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v12
with:
@@ -311,27 +325,23 @@ jobs:
if: matrix.wasm
- name: Install 32bit c libs
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib ccache
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386
if: matrix.cmultilib
- name: Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: .ccache
key: ${{ matrix.name }}-build-v3-${{ github.sha }}
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-build-v3
save-always: true
# open nix-shell once for initial setup
- name: Setup
run: |
# open nix-shell once for initial setup
true
if: runner.os == 'Linux'
- name: Set up core dumps
run: |
mkdir -p $PWD/coredumps
# store in current directory, for easy uploading together with binary
echo $PWD/coredumps/%e.%p.%t | sudo tee /proc/sys/kernel/core_pattern
ccache --zero-stats
if: runner.os == 'Linux'
- name: Set up NPROC
run: |
@@ -340,7 +350,6 @@ jobs:
run: |
mkdir build
cd build
ulimit -c unlimited # coredumps
# arguments passed to `cmake`
# this also enables githash embedding into stage 1 library
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
@@ -367,10 +376,18 @@ jobs:
fi
# contortion to support empty OPTIONS with old macOS bash
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
make -j$NPROC
make install
time make -j$NPROC
- name: Install
run: |
make -C build install
- name: Check Binaries
run: ${{ matrix.binary-check }} lean-*/bin/* || true
- name: Count binary symbols
run: |
for f in lean-*/bin/*; do
echo "$f: $(nm $f | grep " T " | wc -l) exported symbols"
done
if: matrix.name == 'Windows'
- name: List Install Tree
run: |
# omit contents of Init/, ...
@@ -386,7 +403,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 }}
@@ -398,8 +415,7 @@ jobs:
- name: Test
id: test
run: |
ulimit -c unlimited # coredumps
ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.check-level >= 1
- name: Test Summary
uses: test-summary/action@v2
@@ -412,51 +428,28 @@ jobs:
if: (!matrix.cross) && steps.test.conclusion != 'skipped'
- name: Build Stage 2
run: |
ulimit -c unlimited # coredumps
make -C build -j$NPROC stage2
if: matrix.test-speedcenter
- name: Check Stage 3
run: |
ulimit -c unlimited # coredumps
make -C build -j$NPROC stage3
make -C build -j$NPROC check-stage3
if: matrix.test-speedcenter
- name: Test Speedcenter Benchmarks
run: |
echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
# Necessary for some timing metrics but does not work on Namespace runners
# and we just want to test that the benchmarks run at all here
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
cd tests/bench
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
if: matrix.test-speedcenter
- name: Check rebootstrap
run: |
ulimit -c unlimited # coredumps
# clean rebuild in case of Makefile changes
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
if: matrix.name == 'Linux' && needs.configure.outputs.check-level >= 1
- name: CCache stats
run: ccache -s
- name: Show stacktrace for coredumps
if: ${{ failure() && runner.os == 'Linux' }}
run: |
for c in coredumps/*; do
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
echo bt | $GDB/bin/gdb -q $progbin $c || true
done
# has not been used in a long while, would need to be adapted to new
# shared libs
#- name: Upload coredumps
# uses: actions/upload-artifact@v3
# if: ${{ failure() && runner.os == 'Linux' }}
# with:
# name: coredumps-${{ matrix.name }}
# path: |
# ./coredumps
# ./build/stage0/bin/lean
# ./build/stage0/lib/lean/libleanshared.so
# ./build/stage1/bin/lean
# ./build/stage1/lib/lean/libleanshared.so
# ./build/stage2/bin/lean
# ./build/stage2/lib/lean/libleanshared.so
# This job collects results from all the matrix jobs
# This can be made the “required” job, instead of listing each
@@ -468,12 +461,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:
@@ -483,7 +488,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Release
@@ -491,8 +496,14 @@ jobs:
with:
files: artifacts/*/*
fail_on_unmatched_files: true
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.
@@ -502,12 +513,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
@@ -535,3 +546,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

@@ -1,6 +1,7 @@
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, or `WIP` labels,
# by commenting on the PR or issue.
# Other labels from this set are removed automatically at the same time.
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, `WIP`,
# or `release-ci` labels by commenting on the PR or issue.
# If any labels from the set {`awaiting-review`, `awaiting-author`, `WIP`} are added, other labels
# from that set are removed automatically at the same time.
name: Label PR based on Comment
@@ -10,7 +11,7 @@ on:
jobs:
update-label:
if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP'))
if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP') || contains(github.event.comment.body, 'release-ci'))
runs-on: ubuntu-latest
steps:
@@ -25,6 +26,7 @@ jobs:
const awaitingReview = commentLines.includes('awaiting-review');
const awaitingAuthor = commentLines.includes('awaiting-author');
const wip = commentLines.includes('WIP');
const releaseCI = commentLines.includes('release-ci');
if (awaitingReview || awaitingAuthor || wip) {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-review' }).catch(() => {});
@@ -41,3 +43,7 @@ jobs:
if (wip) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['WIP'] });
}
if (releaseCI) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['release-ci'] });
}

View File

@@ -13,18 +13,36 @@ concurrency:
cancel-in-progress: true
jobs:
# see ci.yml
configure:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.result }}
steps:
- name: Configure build matrix
id: set-matrix
uses: actions/github-script@v7
with:
script: |
let large = ${{ github.repository == 'leanprover/lean4' }};
let matrix = [
{
"name": "Nix Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x8" : "ubuntu-latest",
}
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
return matrix;
Build:
needs: [configure]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: nix run .#ciShell -- bash -euxo pipefail {0}
strategy:
matrix:
include:
- name: Nix Linux
os: ubuntu-latest
#- name: Nix macOS
# os: macos-latest
include: ${{fromJson(needs.configure.outputs.matrix)}}
# complete all jobs
fail-fast: false
name: ${{ matrix.name }}
@@ -32,18 +50,19 @@ 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 }}
- name: Set Up Nix Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: nix-store-cache
key: ${{ matrix.name }}-nix-store-cache-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-nix-store-cache
save-always: true
- name: Further Set Up Nix Cache
shell: bash -euxo pipefail {0}
run: |
@@ -60,13 +79,14 @@ jobs:
sudo mkdir -m0770 -p /nix/var/cache/ccache
sudo chown -R $USER /nix/var/cache/ccache
- name: Setup CCache Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: /nix/var/cache/ccache
key: ${{ matrix.name }}-nix-ccache-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-nix-ccache
save-always: true
- name: Further Set Up CCache Cache
run: |
sudo chown -R root:nixbld /nix/var/cache
@@ -85,7 +105,7 @@ jobs:
continue-on-error: true
- name: Build manual
run: |
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,test,inked} -o push-doc
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,inked} -o push-doc
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
# https://github.com/netlify/cli/issues/1809
cp -r --dereference ./result ./dist
@@ -128,5 +148,3 @@ jobs:
- name: Fixup CCache Cache
run: |
sudo chown -R $USER /nix/var/cache
- name: CCache stats
run: CCACHE_DIR=/nix/var/cache/ccache nix run .#nixpkgs.ccache -- -s

View File

@@ -163,7 +163,8 @@ jobs:
# so keep in sync
# Use GitHub API to check if a comment already exists
existing_comment="$(curl -L -s -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
existing_comment="$(curl --retry 3 --location --silent \
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-mathlib4-bot"))')"
@@ -234,7 +235,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 +292,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 +329,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" @ git ".\+",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

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

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"
@@ -47,7 +47,7 @@ jobs:
# uses: DeterminateSystems/magic-nix-cache-action@v2
- if: env.should_update_stage0 == 'yes'
name: Restore Build Cache
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: nix-store-cache
key: Nix Linux-nix-store-cache-${{ github.sha }}

6
.gitignore vendored
View File

@@ -4,8 +4,10 @@
*.lock
.lake
lake-manifest.json
build
!/src/lake/Lake/Build
/build
/src/lakefile.toml
/tests/lakefile.toml
/lakefile.toml
GPATH
GRTAGS
GSYMS

View File

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

View File

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

View File

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

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

File diff suppressed because it is too large Load Diff

1
debug.log Normal file
View File

@@ -0,0 +1 @@
[0829/202002.254:ERROR:crashpad_client_win.cc(868)] not connected

View File

@@ -1 +1,11 @@
# Characters
A value of type `Char`, also known as a character, is a [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value). It is represented using an unsigned 32-bit integer and is statically guaranteed to be a valid Unicode scalar value.
Syntactically, character literals are enclosed in single quotes.
```lean
#eval 'a' -- 'a'
#eval '' -- '∀'
```
Characters are ordered and can be decidably compared using the relational operators `=`, `<`, `≤`, `>`, `≥`.

View File

@@ -5,7 +5,7 @@ Some notes on how to debug Lean, which may also be applicable to debugging Lean
## Tracing
In `CoreM` and derived monads, we use `trace![traceCls] "msg with {interpolations}"` to fill the structured trace viewable with `set_option trace.traceCls true`.
In `CoreM` and derived monads, we use `trace[traceCls] "msg with {interpolations}"` to fill the structured trace viewable with `set_option trace.traceCls true`.
New trace classes have to be registered using `registerTraceClass` first.
Notable trace classes:
@@ -22,7 +22,9 @@ Notable trace classes:
In pure contexts or when execution is aborted before the messages are finally printed, one can instead use the term `dbg_trace "msg with {interpolations}"; val` (`;` can also be replaced by a newline), which will print the message to stderr before evaluating `val`. `dbgTraceVal val` can be used as a shorthand for `dbg_trace "{val}"; val`.
Note that if the return value is not actually used, the trace code is silently dropped as well.
In the language server, stderr output is buffered and shown as messages after a command has been elaborated, unless the option `server.stderrAsMessages` is deactivated.
By default, such stderr output is buffered and shown as messages after a command has been elaborated, which is necessary to ensure deterministic ordering of messages under parallelism.
If Lean aborts the process before it can finish the command or takes too long to do that, using `-DstderrAsMessages=false` avoids this buffering and shows `dbg_trace` output (but not `trace`s or other diagnostics) immediately.
## Debuggers

View File

@@ -5,7 +5,11 @@ 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/` on the `releases/v4.6.0` branch, 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 +17,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 +27,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 +92,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 +106,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,54 +132,52 @@ 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/
- 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.
- Delete anything already mentioned in the hand-written release notes above.
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/
- Verify that the release is marked as a prerelease (this should have been done automatically by the CI release job).
- 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 "update stage0" commits, and anything with a completely inscrutable commit message.
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
but for minor items don't put any work in expanding on commit messages.
- (How we want to release notes to look is evolving: please update this section if it looks wrong!)
- Next, we will move a curated list of downstream repos to the release candidate.
- This assumes that there is already a *reviewed* branch `bump/v4.7.0` on each repository
containing the required adaptations (or no adaptations are required).
The preparation of this branch is beyond the scope of this document.
- This assumes that for each repository either:
* There is already a *reviewed* branch `bump/v4.7.0` containing the required adaptations.
The preparation of this branch is beyond the scope of this document.
* The repository does not need any changes to move to the new version.
- For each of the target repositories:
- Checkout the `bump/v4.7.0` branch.
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
- `git merge origin/master`
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
back to `master` or `main`, and run `lake update` for those dependencies.
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
- `git commit`
- `git push`
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
or notify the maintainers that it is ready to go.
- Once this PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
- If the repository does not need any changes (i.e. `bump/v4.7.0` does not exist) then create
a new PR updating `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1` and running `lake update`.
- Otherwise:
- Checkout the `bump/v4.7.0` branch.
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
- `git merge origin/master`
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
back to `master` or `main`, and run `lake update` for those dependencies.
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
- `git commit`
- `git push`
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
or notify the maintainers that it is ready to go.
- Once the PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
- 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 +188,21 @@ 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 "release notes will be copied" text in the `v4.6.0` section of `RELEASES.md` with the
finalized release notes from the `releases/v4.6.0` branch.
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
```
Release candidate, release notes will be copied from the 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.
- Titled "chore: begin development cycle for v4.8.0"
## Time estimates:
Slightly longer than the corresponding steps for a stable release.
@@ -215,12 +227,30 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
* This can either be done by the person managing this process directly,
or by soliciting assistance from authors of files, or generally helpful people on Zulip!
* Each repo has a `bump/v4.7.0` which accumulates reviewed changes adapting to new versions.
* Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we:
* Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we will create a PR to `bump/v4.7.0`.
* For Mathlib, there is a script in `scripts/create-adaptation-pr.sh` that automates this process.
* For Batteries and Aesop it is currently manual.
* For all of these repositories, the process is the same:
* Make sure `bump/v4.7.0` is up to date with `master` (by merging `master`, no PR necessary)
* Create from `bump/v4.7.0` a `bump/nightly-2024-02-15` branch.
* In that branch, `git merge --squash nightly-testing` to bring across changes from `nightly-testing`.
* In that branch, `git merge nightly-testing` to bring across changes from `nightly-testing`.
* Sanity check changes, commit, and make a PR to `bump/v4.7.0` from the `bump/nightly-2024-02-15` branch.
* Solicit review, merge the PR into `bump/v4,7,0`.
* Solicit review, merge the PR into `bump/v4.7.0`.
* 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 later be copied to `master` when we begin a new development cycle.
* 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`).

1
doc/examples/compiler/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build

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

View File

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

138
doc/flake.lock generated
View File

@@ -18,12 +18,15 @@
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1656928814,
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@@ -35,13 +38,12 @@
"lean": {
"inputs": {
"flake-utils": "flake-utils",
"lean4-mode": "lean4-mode",
"nix": "nix",
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs",
"nixpkgs-old": "nixpkgs-old"
},
"locked": {
"lastModified": 0,
"narHash": "sha256-YnYbmG0oou1Q/GE4JbMNb8/yqUVXBPIvcdQQJHBqtPk=",
"narHash": "sha256-saRAtQ6VautVXKDw1XH35qwP0KEBKTKZbg/TRa4N9Vw=",
"path": "../.",
"type": "path"
},
@@ -50,22 +52,6 @@
"type": "path"
}
},
"lean4-mode": {
"flake": false,
"locked": {
"lastModified": 1659020985,
"narHash": "sha256-+dRaXB7uvN/weSZiKcfSKWhcdJVNg9Vg8k0pJkDNjpc=",
"owner": "leanprover",
"repo": "lean4-mode",
"rev": "37d5c99b7b29c80ab78321edd6773200deb0bca6",
"type": "github"
},
"original": {
"owner": "leanprover",
"repo": "lean4-mode",
"type": "github"
}
},
"leanInk": {
"flake": false,
"locked": {
@@ -83,22 +69,6 @@
"type": "github"
}
},
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"mdBook": {
"flake": false,
"locked": {
@@ -115,65 +85,13 @@
"type": "github"
}
},
"nix": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1657097207,
"narHash": "sha256-SmeGmjWM3fEed3kQjqIAO8VpGmkC2sL1aPE7kKpK650=",
"owner": "NixOS",
"repo": "nix",
"rev": "f6316b49a0c37172bca87ede6ea8144d7d89832f",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1653988320,
"narHash": "sha256-ZaqFFsSDipZ6KVqriwM34T739+KLYJvNmCWzErjAg7c=",
"lastModified": 1710889954,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2fa57ed190fd6c7c746319444f34b5917666e5c1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1657208011,
"narHash": "sha256-BlIFwopAykvdy1DYayEkj6ZZdkn+cVgPNX98QVLc0jM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2770cc0b1e8faa0e20eb2c6aea64c256a706d4f2",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
"type": "github"
},
"original": {
@@ -183,6 +101,23 @@
"type": "github"
}
},
"nixpkgs-old": {
"flake": false,
"locked": {
"lastModified": 1581379743,
"narHash": "sha256-i1XCn9rKuLjvCdu2UeXKzGLF6IuQePQKFt4hEKRU5oc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-19.03",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"alectryon": "alectryon",
@@ -194,6 +129,21 @@
"leanInk": "leanInk",
"mdBook": "mdBook"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View File

@@ -17,7 +17,7 @@
};
outputs = inputs@{ self, ... }: inputs.flake-utils.lib.eachDefaultSystem (system:
with inputs.lean.packages.${system}; with nixpkgs;
with inputs.lean.packages.${system}.deprecated; with nixpkgs;
let
doc-src = lib.sourceByRegex ../. ["doc.*" "tests(/lean(/beginEndAsMacro.lean)?)?"];
in {
@@ -44,21 +44,6 @@
mdbook build -d $out
'';
};
# We use a separate derivation instead of `checkPhase` so we can push it but not `doc` to the binary cache
test = stdenv.mkDerivation {
name ="lean-doc-test";
src = doc-src;
buildInputs = [ lean-mdbook stage1.Lean.lean-package strace ];
patchPhase = ''
cd doc
patchShebangs test
'';
buildPhase = ''
mdbook test
touch $out
'';
dontInstall = true;
};
leanInk = (buildLeanPackage {
name = "Main";
src = inputs.leanInk;

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

@@ -8,6 +8,7 @@ Requirements
- C++14 compatible compiler
- [CMake](http://www.cmake.org)
- [GMP (GNU multiprecision library)](http://gmplib.org/)
- [LibUV](https://libuv.org/)
Platform-Specific Setup
-----------------------
@@ -27,9 +28,9 @@ Setting up a basic parallelized release build:
git clone https://github.com/leanprover/lean4
cd lean4
cmake --preset release
make -C build/release -j$(nproc) # see below for macOS
make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)
```
You can replace `$(nproc)`, which is not available on macOS and some alternative shells, with the desired parallelism amount.
You can replace `$(nproc || sysctl -n hw.logicalcpu)` with the desired parallelism amount.
The above commands will compile the Lean library and binaries into the
`stage1` subfolder; see below for details.

View File

@@ -25,7 +25,7 @@ MSYS2 has a package management system, [pacman][pacman], which is used in Arch L
Here are the commands to install all dependencies needed to compile Lean on your machine.
```bash
pacman -S make python mingw-w64-x86_64-cmake mingw-w64-x86_64-clang mingw-w64-x86_64-ccache git unzip diffutils binutils
pacman -S make python mingw-w64-x86_64-cmake mingw-w64-x86_64-clang mingw-w64-x86_64-ccache mingw-w64-x86_64-libuv mingw-w64-x86_64-gmp git unzip diffutils binutils
```
You should now be able to run these commands:
@@ -64,6 +64,7 @@ they are installed in your MSYS setup:
- libgcc_s_seh-1.dll
- libstdc++-6.dll
- libgmp-10.dll
- libuv-1.dll
- libwinpthread-1.dll
The following linux command will do that:

View File

@@ -32,15 +32,16 @@ following to use `g++`.
cmake -DCMAKE_CXX_COMPILER=g++ ...
```
## Required Packages: CMake, GMP
## Required Packages: CMake, GMP, libuv
```bash
brew install cmake
brew install gmp
brew install libuv
```
## Recommended Packages: CCache
```bash
brew install ccache
```
```

View File

@@ -8,5 +8,5 @@ follow the [generic build instructions](index.md).
## Basic packages
```bash
sudo apt-get install git libgmp-dev cmake ccache clang
sudo apt-get install git libgmp-dev libuv1-dev cmake ccache clang
```

View File

@@ -5,14 +5,19 @@ See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
1. Install [VS Code](https://code.visualstudio.com/).
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
1. Launch VS Code and install the `Lean 4` extension by clicking on the 'Extensions' sidebar entry and searching for 'Lean 4'.
![installing the vscode-lean4 extension](images/code-ext.png)
![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)

130
flake.lock generated
View File

@@ -1,21 +1,5 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
@@ -34,75 +18,38 @@
"type": "github"
}
},
"lean4-mode": {
"flake": false,
"locked": {
"lastModified": 1709737301,
"narHash": "sha256-uT9JN2kLNKJK9c/S/WxLjiHmwijq49EgLb+gJUSDpz0=",
"owner": "leanprover",
"repo": "lean4-mode",
"rev": "f1f24c15134dee3754b82c9d9924866fe6bc6b9f",
"type": "github"
},
"original": {
"owner": "leanprover",
"repo": "lean4-mode",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": "flake-compat",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1711102798,
"narHash": "sha256-CXOIJr8byjolqG7eqCLa+Wfi7rah62VmLoqSXENaZnw=",
"owner": "NixOS",
"repo": "nix",
"rev": "a22328066416650471c3545b0b138669ea212ab4",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1709083642,
"narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=",
"lastModified": 1710889954,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b550fe4b4776908ac2a861124307045f8e717c8e",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.11",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-cadical": {
"locked": {
"lastModified": 1722221733,
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e",
"type": "github"
}
},
"nixpkgs-old": {
"flake": false,
"locked": {
@@ -120,44 +67,11 @@
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1710889954,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"lean4-mode": "lean4-mode",
"nix": "nix",
"nixpkgs": "nixpkgs_2",
"nixpkgs": "nixpkgs",
"nixpkgs-cadical": "nixpkgs-cadical",
"nixpkgs-old": "nixpkgs-old"
}
},

109
flake.nix
View File

@@ -1,96 +1,61 @@
{
description = "Lean interactive theorem prover";
description = "Lean development flake. Not intended for end users.";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# old nixpkgs used for portable release with older glibc (2.27)
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
inputs.nixpkgs-old.flake = false;
# for cadical 1.9.5; sync with CMakeLists.txt
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.nix.url = "github:NixOS/nix";
inputs.lean4-mode = {
url = "github:leanprover/lean4-mode";
flake = false;
};
# used *only* by `stage0-from-input` below
#inputs.lean-stage0 = {
# url = github:leanprover/lean4;
# inputs.nixpkgs.follows = "nixpkgs";
# inputs.flake-utils.follows = "flake-utils";
# inputs.nix.follows = "nix";
# inputs.lean4-mode.follows = "lean4-mode";
#};
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, nix, lean4-mode, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
# for `vscode-with-extensions`
config.allowUnfree = true;
};
pkgs = import nixpkgs { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old = import nixpkgs-old { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
cadical = if pkgs.stdenv.isLinux then
# use statically-linked cadical on Linux to avoid glibc versioning troubles
pkgsCadical.pkgsStatic.cadical.overrideAttrs { doCheck = false; }
else pkgsCadical.cadical;
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; inherit nix lean4-mode; };
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; };
devShellWithDist = pkgsDist: pkgs.mkShell.override {
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp ccache
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
# TODO: only add when proven to not affect the flakification
#pkgs.python3
];
# https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ];
# more convenient `ctest` output
CTEST_OUTPUT_ON_FAILURE = 1;
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
GMP = pkgsDist.gmp.override { withStatic = true; };
GLIBC = pkgsDist.glibc;
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
GDB = pkgsDist.gdb;
});
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp libuv ccache cadical
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb
tree # for CI
];
# https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ];
# more convenient `ctest` output
CTEST_OUTPUT_ON_FAILURE = 1;
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
GMP = pkgsDist.gmp.override { withStatic = true; };
LIBUV = pkgsDist.libuv.overrideAttrs (attrs: { configureFlags = ["--enable-static"]; });
GLIBC = pkgsDist.glibc;
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
GDB = pkgsDist.gdb;
});
in {
packages = lean-packages // rec {
debug = lean-packages.override { debug = true; };
stage0debug = lean-packages.override { stage0debug = true; };
asan = lean-packages.override { extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address" "-DLEANC_EXTRA_FLAGS=-fsanitize=address" "-DSMALL_ALLOCATOR=OFF" "-DSYMBOLIC=OFF" ]; };
asandebug = asan.override { debug = true; };
tsan = lean-packages.override {
extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=thread" "-DLEANC_EXTRA_FLAGS=-fsanitize=thread" "-DCOMPRESSED_OBJECT_HEADER=OFF" ];
stage0 = (lean-packages.override {
# Compressed headers currently trigger data race reports in tsan.
# Turn them off for stage 0 as well so stage 1 can read its own stdlib.
extraCMakeFlags = [ "-DCOMPRESSED_OBJECT_HEADER=OFF" ];
}).stage1;
};
tsandebug = tsan.override { debug = true; };
stage0-from-input = lean-packages.override {
stage0 = pkgs.writeShellScriptBin "lean" ''
exec ${inputs.lean-stage0.packages.${system}.lean}/bin/lean -Dinterpreter.prefer_native=false "$@"
'';
};
inherit self;
packages = {
# to be removed when Nix CI is not needed anymore
inherit (lean-packages) cacheRoots test update-stage0-commit ciShell;
deprecated = lean-packages;
};
defaultPackage = lean-packages.lean-all;
# The default development shell for working on lean itself
devShells.default = devShellWithDist pkgs;
devShells.oldGlibc = devShellWithDist pkgsDist-old;
devShells.oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
checks.lean = lean-packages.test;
}) // rec {
templates.pkg = {
path = ./nix/templates/pkg;
description = "A custom Lean package";
};
defaultTemplate = templates.pkg;
};
});
}

View File

@@ -1,13 +1,13 @@
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
stdenv, lib, cmake, gmp, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
stdenv, lib, cmake, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
... } @ args:
with builtins;
rec {
lib.warn "The Nix-based build is deprecated" rec {
inherit stdenv;
sourceByRegex = p: rs: lib.sourceByRegex p (map (r: "(/src/)?${r}") rs);
buildCMake = args: stdenv.mkDerivation ({
nativeBuildInputs = [ cmake ];
buildInputs = [ gmp llvmPackages.llvm ];
buildInputs = [ gmp libuv llvmPackages.llvm ];
# https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ];
dontStrip = (args.debug or debug);
@@ -17,7 +17,7 @@ rec {
'';
} // args // {
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
preConfigure = args.preConfigure or "" + ''
# ignore absence of submodule
sed -i 's!lake/Lake.lean!!' CMakeLists.txt
@@ -26,11 +26,7 @@ rec {
lean-bin-tools-unwrapped = buildCMake {
name = "lean-bin-tools";
outputs = [ "out" "leanc_src" ];
realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "cmake.*" "bin.*" "include.*" ".*\.in" "Leanc\.lean" ];
preConfigure = ''
touch empty.cpp
sed -i 's/add_subdirectory.*//;s/set(LEAN_OBJS.*/set(LEAN_OBJS empty.cpp)/' CMakeLists.txt
'';
realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "[a-z].*" ".*\.in" "Leanc\.lean" ];
dontBuild = true;
installPhase = ''
mkdir $out $leanc_src
@@ -45,11 +41,10 @@ rec {
leancpp = buildCMake {
name = "leancpp";
src = src + "/src";
buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "shell" ];
buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "leanshell" "leanmain" ];
installPhase = ''
mkdir -p $out
mv lib/ $out/
mv shell/CMakeFiles/shell.dir/lean.cpp.o $out/lib
mv runtime/libleanrt_initial-exec.a $out/lib
'';
};
@@ -87,7 +82,8 @@ rec {
leanFlags = [ "-DwarningAsError=true" ];
} // args);
Init' = build { name = "Init"; deps = []; };
Lean' = build { name = "Lean"; deps = [ Init' ]; };
Std' = build { name = "Std"; deps = [ Init' ]; };
Lean' = build { name = "Lean"; deps = [ Std' ]; };
attachSharedLib = sharedLib: pkg: pkg // {
inherit sharedLib;
mods = mapAttrs (_: m: m // { inherit sharedLib; propagatedLoadDynlibs = []; }) pkg.mods;
@@ -95,55 +91,61 @@ rec {
in (all: all // all.lean) rec {
inherit (Lean) emacs-dev emacs-package vscode-dev vscode-package;
Init = attachSharedLib leanshared Init';
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Init ]; };
Std = attachSharedLib leanshared Std' // { allExternalDeps = [ Init ]; };
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Std ]; };
Lake = build {
name = "Lake";
sharedLibName = "Lake_shared";
src = src + "/src/lake";
deps = [ Init Lean ];
};
Lake-Main = build {
name = "Lake.Main";
roots = [ "Lake.Main" ];
name = "LakeMain";
roots = [{ glob = "one"; mod = "LakeMain"; }];
executableName = "lake";
deps = [ Lake ];
linkFlags = lib.optional stdenv.isLinux "-rdynamic";
src = src + "/src/lake";
};
stdlib = [ Init Lean Lake ];
stdlib = [ Init Std Lean Lake ];
modDepsFiles = symlinkJoin { name = "modDepsFiles"; paths = map (l: l.modDepsFile) (stdlib ++ [ Leanc ]); };
depRoots = symlinkJoin { name = "depRoots"; paths = map (l: l.depRoots) stdlib; };
iTree = symlinkJoin { name = "ileans"; paths = map (l: l.iTree) stdlib; };
Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; roots = [ "Leanc" ]; };
stdlibLinkFlags = "-L${Init.staticLib} -L${Lean.staticLib} -L${Lake.staticLib} -L${leancpp}/lib/lean";
stdlibLinkFlags = "${lib.concatMapStringsSep " " (l: "-L${l.staticLib}") stdlib} -L${leancpp}/lib/lean";
libInit_shared = runCommand "libInit_shared" { buildInputs = [ stdenv.cc ]; libName = "libInit_shared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
-Wl,--whole-archive -lInit ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName
touch empty.c
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
'';
leanshared_1 = runCommand "leanshared_1" { buildInputs = [ stdenv.cc ]; libName = "leanshared_1${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
touch empty.c
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
'';
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 -Wl,-Bsymbolic \
${libInit_shared}/* -Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
-Wl,--whole-archive ${leancpp}/lib/temp/libleanshell.a -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
'';
mods = foldl' (mods: pkg: mods // pkg.mods) {} stdlib;
print-paths = Lean.makePrintPathsFor [] mods;
leanc = writeShellScriptBin "leanc" ''
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared} "$@"
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared_1} -L${leanshared} -L${Lake.sharedLib} "$@"
'';
lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } ''
mkdir -p $out/bin
${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${libInit_shared}/* ${leanshared}/* -o $out/bin/lean
${leanc}/bin/leanc ${leancpp}/lib/temp/libleanmain.a ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* -o $out/bin/lean
'';
# derivation following the directory layout of the "basic" setup, mostly useful for running tests
lean-all = stdenv.mkDerivation {
name = "lean-${desc}";
buildCommand = ''
mkdir -p $out/bin $out/lib/lean
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared}/* $out/lib/lean/
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* ${Lake.sharedLib}/* $out/lib/lean/
# put everything in a single final derivation so `IO.appDir` references work
cp ${lean}/bin/lean ${leanc}/bin/leanc ${Lake-Main.executable}/bin/lake $out/bin
# NOTE: `lndir` will not override existing `bin/leanc`
@@ -151,15 +153,13 @@ rec {
'';
meta.mainProgram = "lean";
};
cacheRoots = linkFarmFromDrvs "cacheRoots" [
cacheRoots = linkFarmFromDrvs "cacheRoots" ([
stage0 lean leanc lean-all iTree modDepsFiles depRoots Leanc.src
# .o files are not a runtime dependency on macOS because of lack of thin archives
Lean.oTree Lake.oTree
];
] ++ map (lib: lib.oTree) stdlib);
test = buildCMake {
name = "lean-test-${desc}";
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
buildInputs = [ gmp perl git ];
buildInputs = [ gmp libuv perl git cadical ];
preConfigure = ''
cd src
'';
@@ -170,7 +170,7 @@ rec {
ln -sf ${lean-all}/* .
'';
buildPhase = ''
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_init' -j$NIX_BUILD_CORES
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi' -j$NIX_BUILD_CORES
'';
installPhase = ''
mkdir $out
@@ -178,7 +178,7 @@ rec {
'';
};
update-stage0 =
let cTree = symlinkJoin { name = "cs"; paths = [ Init.cTree Lean.cTree ]; }; in
let cTree = symlinkJoin { name = "cs"; paths = map (lib: lib.cTree) (stdlib ++ [Lake-Main]); }; in
writeShellScriptBin "update-stage0" ''
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/lib/update-stage0"}
'';

View File

@@ -1,11 +1,11 @@
{ lean, lean-leanDeps ? lean, lean-final ? lean, leanc,
stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, lean-emacs, lean-vscode, nix, substituteAll, symlinkJoin, linkFarmFromDrvs,
stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, substituteAll, symlinkJoin, linkFarmFromDrvs,
runCommand, darwin, mkShell, ... }:
let lean-final' = lean-final; in
lib.makeOverridable (
{ name, src, fullSrc ? src, srcPrefix ? "", srcPath ? "$PWD/${srcPrefix}",
# Lean dependencies. Each entry should be an output of buildLeanPackage.
deps ? [ lean.Lean ],
deps ? [ lean.Init lean.Std lean.Lean ],
# Static library dependencies. Each derivation `static` should contain a static library in the directory `${static}`.
staticLibDeps ? [],
# Whether to wrap static library inputs in a -Wl,--start-group [...] -Wl,--end-group to ensure dependencies are resolved.
@@ -30,7 +30,7 @@ lib.makeOverridable (
pluginDeps ? [],
# `overrideAttrs` for `buildMod`
overrideBuildModAttrs ? null,
debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name,
debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name, sharedLibName ? libName,
srcTarget ? "..#stage0", srcArgs ? "(\${args[*]})", lean-final ? lean-final' }@args:
with builtins; let
# "Init.Core" ~> "Init/Core"
@@ -197,19 +197,6 @@ with builtins; let
then map (m: m.module) header.imports
else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" header.errors}";
in mkMod mod (map (dep: if modDepsMap ? ${dep} then modCandidates.${dep} else externalModMap.${dep}) deps)) modDepsMap;
makeEmacsWrapper = name: emacs: lean: writeShellScriptBin name ''
${emacs} --eval "(progn (setq lean4-rootdir \"${lean}\"))" "$@"
'';
makeVSCodeWrapper = name: lean: writeShellScriptBin name ''
PATH=${lean}/bin:$PATH ${lean-vscode}/bin/code "$@"
'';
printPaths = deps: writeShellScriptBin "print-paths" ''
echo '${toJSON {
oleanPath = [(depRoot "print-paths" deps)];
srcPath = ["."] ++ map (dep: dep.src) allExternalDeps;
loadDynlibPaths = map pathOfSharedLib (loadDynlibsOfDeps deps);
}}'
'';
expandGlob = g:
if typeOf g == "string" then [g]
else if g.glob == "one" then [g.mod]
@@ -224,7 +211,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))};
'';
@@ -245,59 +233,15 @@ in rec {
cTree = symlinkJoin { name = "${name}-cTree"; paths = map (mod: mod.c) (attrValues mods); };
oTree = symlinkJoin { name = "${name}-oTree"; paths = (attrValues objects); };
iTree = symlinkJoin { name = "${name}-iTree"; paths = map (mod: mod.ilean) (attrValues mods); };
sharedLib = mkSharedLib "lib${libName}" ''
sharedLib = mkSharedLib "lib${sharedLibName}" ''
${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \
${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}'';
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.libInit_shared}/* ${lean-final.leanshared}/*";
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.leanshared}/*";
in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } ''
mkdir -p $out/bin
leanc ${staticLibLinkWrapper (lib.concatStringsSep " " (objPaths ++ map (d: "${d}/*.a") allStaticLibDeps))} \
-o $out/bin/${executableName} \
${lib.concatStringsSep " " allLinkFlags}
'') {};
lean-package = writeShellScriptBin "lean" ''
LEAN_PATH=${modRoot}:$LEAN_PATH LEAN_SRC_PATH=$LEAN_SRC_PATH:${src} exec ${lean-final}/bin/lean "$@"
'';
emacs-package = makeEmacsWrapper "emacs-package" lean-package;
vscode-package = makeVSCodeWrapper "vscode-package" lean-package;
link-ilean = writeShellScriptBin "link-ilean" ''
dest=''${1:-.}
mkdir -p $dest/build/lib
ln -sf ${iTree}/* $dest/build/lib
'';
makePrintPathsFor = deps: mods: printPaths deps // mapAttrs (_: mod: makePrintPathsFor (deps ++ [mod]) mods) mods;
print-paths = makePrintPathsFor [] (mods' // externalModMap);
# `lean` wrapper that dynamically runs Nix for the actual `lean` executable so the same editor can be
# used for multiple projects/after upgrading the `lean` input/for editing both stage 1 and the tests
lean-bin-dev = substituteAll {
name = "lean";
dir = "bin";
src = ./lean-dev.in;
isExecutable = true;
srcRoot = fullSrc; # use root flake.nix in case of Lean repo
inherit bash nix srcTarget srcArgs;
};
lake-dev = substituteAll {
name = "lake";
dir = "bin";
src = ./lake-dev.in;
isExecutable = true;
srcRoot = fullSrc; # use root flake.nix in case of Lean repo
inherit bash nix srcTarget srcArgs;
};
lean-dev = symlinkJoin { name = "lean-dev"; paths = [ lean-bin-dev lake-dev ]; };
emacs-dev = makeEmacsWrapper "emacs-dev" "${lean-emacs}/bin/emacs" lean-dev;
emacs-path-dev = makeEmacsWrapper "emacs-path-dev" "emacs" lean-dev;
vscode-dev = makeVSCodeWrapper "vscode-dev" lean-dev;
devShell = mkShell {
buildInputs = [ nix ];
shellHook = ''
export LEAN_SRC_PATH="${srcPath}"
'';
};
})

View File

@@ -1,9 +1,6 @@
{ src, pkgs, nix, ... } @ args:
{ src, pkgs, ... } @ args:
with pkgs;
let
nix-pinned = writeShellScriptBin "nix" ''
${nix.packages.${system}.default}/bin/nix --experimental-features 'nix-command flakes' --extra-substituters https://lean4.cachix.org/ --option warn-dirty false "$@"
'';
# https://github.com/NixOS/nixpkgs/issues/130963
llvmPackages = if stdenv.isDarwin then llvmPackages_11 else llvmPackages_15;
cc = (ccacheWrapper.override rec {
@@ -42,40 +39,9 @@ let
inherit (lean) stdenv;
lean = lean.stage1;
inherit (lean.stage1) leanc;
inherit lean-emacs lean-vscode;
nix = nix-pinned;
}));
lean4-mode = emacsPackages.melpaBuild {
pname = "lean4-mode";
version = "1";
commit = "1";
src = args.lean4-mode;
packageRequires = with pkgs.emacsPackages.melpaPackages; [ dash f flycheck magit-section lsp-mode s ];
recipe = pkgs.writeText "recipe" ''
(lean4-mode
:repo "leanprover/lean4-mode"
:fetcher github
:files ("*.el" "data"))
'';
};
lean-emacs = emacsWithPackages [ lean4-mode ];
# updating might be nicer by building from source from a flake input, but this is good enough for now
vscode-lean4 = vscode-utils.extensionFromVscodeMarketplace {
name = "lean4";
publisher = "leanprover";
version = "0.0.63";
sha256 = "sha256-kjEex7L0F2P4pMdXi4NIZ1y59ywJVubqDqsoYagZNkI=";
};
lean-vscode = vscode-with-extensions.override {
vscodeExtensions = [ vscode-lean4 ];
};
in {
inherit cc lean4-mode buildLeanPackage llvmPackages vscode-lean4;
lean = lean.stage1;
stage0print-paths = lean.stage1.Lean.print-paths;
HEAD-as-stage0 = (lean.stage1.Lean.overrideArgs { srcTarget = "..#stage0-from-input.stage0"; srcArgs = "(--override-input lean-stage0 ..\?rev=$(git rev-parse HEAD) -- -Dinterpreter.prefer_native=false \"$@\")"; });
HEAD-as-stage1 = (lean.stage1.Lean.overrideArgs { srcTarget = "..\?rev=$(git rev-parse HEAD)#stage0"; });
nix = nix-pinned;
inherit cc buildLeanPackage llvmPackages;
nixpkgs = pkgs;
ciShell = writeShellScriptBin "ciShell" ''
set -o pipefail
@@ -83,5 +49,4 @@ in {
# prefix lines with cumulative and individual execution time
"$@" |& ts -i "(%.S)]" | ts -s "[%M:%S"
'';
vscode = lean-vscode;
} // lean.stage1.Lean // lean.stage1 // lean
} // lean.stage1

View File

@@ -0,0 +1,3 @@
* The `Lean` module has switched from `Lean.HashMap` and `Lean.HashSet` to `Std.HashMap` and `Std.HashSet`. `Lean.HashMap` and `Lean.HashSet` are now deprecated and will be removed in a future release. Users of `Lean` APIs that interact with hash maps, for example `Lean.Environment.const2ModIdx`, might encounter minor breakage due to the following breaking changes from `Lean.HashMap` to `Std.HashMap`:
* query functions use the term `get` instead of `find`,
* the notation `map[key]` no longer returns an optional value but expects a proof that the key is present in the map instead. The previous behavior is available via the `map[key]?` notation.

1
releases_drafts/libuv.md Normal file
View File

@@ -0,0 +1 @@
* #4963 [LibUV](https://libuv.org/) is now required to build Lean. This change only affects developers who compile Lean themselves instead of obtaining toolchains via `elan`. We have updated the official build instructions with information on how to obtain LibUV on our supported platforms.

View File

@@ -1,13 +0,0 @@
* 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`.
part of #3929

View File

@@ -1,12 +0,0 @@
Functions defined by well-founded recursion are now marked as
`@[irreducible]`, which should prevent expensive and often unfruitful
unfolding of such definitions.
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 temporariliy made
semireducible (using `unseal f in` before the command) or the function
definition itself can be marked as `@[semireducible]` to get the previous
behavor.
#4061

View File

@@ -15,4 +15,19 @@ for f in $(git ls-files src ':!:src/lake/*' ':!:src/Leanc.lean'); do
cp $f stage0/$f
fi
done
# special handling for Lake files due to its nested directory
# copy the README to ensure the `stage0/src/lake` directory is comitted
for f in $(git ls-files 'src/lake/Lake/*' src/lake/Lake.lean src/lake/LakeMain.lean src/lake/README.md ':!:src/lakefile.toml'); do
if [[ $f == *.lean ]]; then
f=${f#src/lake}
f=${f%.lean}.c
mkdir -p $(dirname stage0/stdlib/$f)
cp ${CP_C_PARAMS:-} $CSRCS/$f stage0/stdlib/$f
else
mkdir -p $(dirname stage0/$f)
cp $f stage0/$f
fi
done
git add stage0

View File

@@ -38,7 +38,7 @@ $CP $GLIBC/lib/*crt* llvm/lib/
$CP $GLIBC/lib/*crt* stage1/lib/
# runtime
(cd llvm; $CP --parents lib/clang/*/lib/*/{clang_rt.*.o,libclang_rt.builtins*} ../stage1)
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a stage1/lib/
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a $LIBUV/lib/libuv.a stage1/lib/
# LLVM 15 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
# but clang-15 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
# https://github.com/llvm/llvm-project/issues/54955
@@ -62,8 +62,8 @@ fi
# use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers),
# but do not change sysroot so users can still link against system libs
echo -n " -DLEANC_INTERNAL_FLAGS='-nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -Wl,--no-as-needed'"
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -luv -Wl,--no-as-needed'"
# do not set `LEAN_CC` for tests
echo -n " -DLEAN_TEST_VARS=''"

View File

@@ -9,6 +9,7 @@ set -uxo pipefail
# use full LLVM release for compiling C++ code, but subset for compiling C code and distribution
GMP=${GMP:-$(brew --prefix)}
LIBUV=${LIBUV:-$(brew --prefix)}
[[ -d llvm ]] || (mkdir llvm; gtar xf $1 --strip-components 1 --directory llvm)
[[ -d llvm-host ]] || if [[ "$#" -gt 1 ]]; then
@@ -46,8 +47,9 @@ echo -n " -DLEAN_EXTRA_CXX_FLAGS='${EXTRA_FLAGS:-}'"
if [[ -L llvm-host ]]; then
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang"
gcp $GMP/lib/libgmp.a stage1/lib/
gcp $LIBUV/lib/libuv.a stage1/lib/
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp'"
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -luv'"
else
echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/15.0.1 ${EXTRA_FLAGS:-}'"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"

View File

@@ -31,15 +31,15 @@ cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
# runtime
(cd llvm; cp --parents lib/clang/*/lib/*/libclang_rt.builtins* ../stage1)
# further dependencies
cp /clang64/lib/lib{m,bcrypt,mingw32,moldname,mingwex,msvcrt,pthread,advapi32,shell32,user32,kernel32,ucrtbase}.* /clang64/lib/libgmp.a llvm/lib/lib{c++,c++abi,unwind}.a stage1/lib/
cp /clang64/lib/lib{m,bcrypt,mingw32,moldname,mingwex,msvcrt,pthread,advapi32,shell32,user32,kernel32,ucrtbase}.* /clang64/lib/libgmp.a /clang64/lib/libuv.a llvm/lib/lib{c++,c++abi,unwind}.a stage1/lib/
echo -n " -DLEAN_STANDALONE=ON"
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang.exe -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER=$PWD/llvm/bin/clang++.exe -DCMAKE_CXX_COMPILER_WORKS=1 -DLEAN_CXX_STDLIB='-lc++ -lc++abi'"
echo -n " -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_CXX_COMPILER=clang++"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter /clang64/include/'"
echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang.exe"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -static-libgcc -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -fuse-ld=lld'"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -static-libgcc -Wl,-Bstatic -lgmp -luv -lunwind -Wl,-Bdynamic -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -lucrtbase'"
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -luv -lucrtbase'"
# do not set `LEAN_CC` for tests
echo -n " -DAUTO_THREAD_FINALIZATION=OFF -DSTAGE0_AUTO_THREAD_FINALIZATION=OFF"
echo -n " -DLEAN_TEST_VARS=''"

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 9)
set(LEAN_VERSION_MINOR 12)
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'")
@@ -73,6 +74,7 @@ option(USE_GMP "USE_GMP" ON)
# development-specific options
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
option(USE_LAKE "Use Lake instead of lean.mk for building core libs from language server" OFF)
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
@@ -241,6 +243,15 @@ if("${USE_GMP}" MATCHES "ON")
endif()
endif()
if(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Emscripten")
# LibUV
find_package(LibUV 1.0.0 REQUIRED)
include_directories(${LIBUV_INCLUDE_DIR})
endif()
if(NOT LEAN_STANDALONE)
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${LIBUV_LIBRARIES}")
endif()
# ccache
if(CCACHE AND NOT CMAKE_CXX_COMPILER_LAUNCHER AND NOT CMAKE_C_COMPILER_LAUNCHER)
find_program(CCACHE_PATH ccache)
@@ -299,11 +310,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")
@@ -322,7 +333,12 @@ if(NOT LEAN_STANDALONE)
endif()
# flags for user binaries = flags for toolchain binaries + Lake
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
set(LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(LEANC_SHARED_LINKER_FLAGS " ${TOOLCHAIN_SHARED_LINKER_FLAGS} -Wl,--as-needed -lLake_shared -Wl,--no-as-needed")
else()
set(LEANC_SHARED_LINKER_FLAGS " ${TOOLCHAIN_SHARED_LINKER_FLAGS} -lLake_shared")
endif()
if (LLVM)
string(APPEND LEANSHARED_LINKER_FLAGS " -L${LLVM_CONFIG_LIBDIR} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS} ${LLVM_CONFIG_SYSTEM_LIBS}")
@@ -367,15 +383,20 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -400,8 +421,8 @@ endif()
# executable or `leanshared`, plugins would try to look them up at load time (even though they
# are already loaded) and probably fail unless we set up LD_LIBRARY_PATH.
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# import library created by the `leanshared` target
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared")
# import libraries created by the stdlib.make targets
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
endif()
@@ -458,6 +479,22 @@ if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE)
string(APPEND LEANC_EXTRA_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
endif()
add_subdirectory(initialize)
add_subdirectory(shell)
# to be included in `leanshared` but not the smaller `leanshared_1` (as it would pull
# in the world)
add_library(leaninitialize STATIC $<TARGET_OBJECTS:initialize>)
set_target_properties(leaninitialize PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
OUTPUT_NAME leaninitialize)
add_library(leanshell STATIC util/shell.cpp)
set_target_properties(leanshell PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
OUTPUT_NAME leanshell)
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--whole-archive -lleanmanifest -Wl,--no-whole-archive")
endif()
if(${STAGE} GREATER 1)
# reuse C++ parts, which don't change
add_library(leanrt_initial-exec STATIC IMPORTED)
@@ -466,13 +503,17 @@ if(${STAGE} GREATER 1)
add_library(leanrt STATIC IMPORTED)
set_target_properties(leanrt PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a")
add_library(leancpp_1 STATIC IMPORTED)
set_target_properties(leancpp_1 PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/temp/libleancpp_1.a")
add_library(leancpp STATIC IMPORTED)
set_target_properties(leancpp PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
add_custom_target(copy-leancpp
COMMAND cmake -E copy_if_different "${PREV_STAGE}/runtime/libleanrt_initial-exec.a" "${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a"
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleanrt.a" "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a"
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a"
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/temp/libleancpp_1.a" "${CMAKE_BINARY_DIR}/lib/temp/libleancpp_1.a")
add_dependencies(leancpp copy-leancpp)
if(LLVM)
add_custom_target(copy-lean-h-bc
@@ -492,14 +533,23 @@ else()
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:constructions>)
add_subdirectory(library/compiler)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:compiler>)
add_subdirectory(initialize)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:initialize>)
add_library(leancpp STATIC ${LEAN_OBJS})
# leancpp without `initialize` (see `leaninitialize` above)
add_library(leancpp_1 STATIC ${LEAN_OBJS})
set_target_properties(leancpp_1 PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
OUTPUT_NAME leancpp_1)
add_library(leancpp STATIC ${LEAN_OBJS} $<TARGET_OBJECTS:initialize>)
set_target_properties(leancpp PROPERTIES
OUTPUT_NAME leancpp)
endif()
if((${STAGE} GREATER 0) AND CADICAL)
add_custom_target(copy-cadical
COMMAND cmake -E copy_if_different "${CADICAL}" "${CMAKE_BINARY_DIR}/bin/cadical${CMAKE_EXECUTABLE_SUFFIX}")
add_dependencies(leancpp copy-cadical)
endif()
# MSYS2 bash usually handles Windows paths relatively well, but not when putting them in the PATH
string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
@@ -507,25 +557,12 @@ string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
# (also looks nicer in the build log)
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")
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/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/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 -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
endif()
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# We do not use dynamic linking via leanshared for Emscripten to keep things
# simple. (And we are not interested in `Lake` anyway.) To use dynamic
# linking, we would probably have to set MAIN_MODULE=2 on `leanshared`,
# SIDE_MODULE=2 on `lean`, and set CMAKE_SHARED_LIBRARY_SUFFIX to ".js".
string(APPEND LEAN_EXE_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
string(APPEND LEAN_EXE_LINKER_FLAGS " ${LIB}/temp/libleanshell.a ${TOOLCHAIN_STATIC_LINKER_FLAGS} ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
endif()
# Build the compiler using the bootstrapped C sources for stage0, and use
@@ -539,7 +576,7 @@ add_custom_target(make_stdlib ALL
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
# for a parallelized nested build, but CMake doesn't let us do that.
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Lean
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean
VERBATIM)
# if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode
@@ -559,8 +596,13 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
)
add_custom_target(leanshared ALL
DEPENDS Init_shared leancpp
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared_1${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_custom_target(lake_shared ALL
DEPENDS leanshared
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libLake_shared${CMAKE_SHARED_LIBRARY_SUFFIX}
)
else()
add_custom_target(Init_shared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
@@ -570,23 +612,29 @@ else()
add_custom_target(leanshared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS Init_shared leancpp
DEPENDS Init_shared leancpp_1 leancpp leanshell leaninitialize
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
VERBATIM)
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
endif()
if(${STAGE} GREATER 0 AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(NOT EXISTS ${LEAN_SOURCE_DIR}/lake/Lake.lean)
message(FATAL_ERROR "src/lake does not exist. Please check out the Lake submodule using `git submodule update --init src/lake`.")
endif()
add_custom_target(lake ALL
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
add_custom_target(lake_lib ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS leanshared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake
VERBATIM)
add_custom_target(lake_shared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_lib
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make libLake_shared
VERBATIM)
add_custom_target(lake ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_shared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make lake
VERBATIM)
endif()
if(PREV_STAGE)
@@ -615,7 +663,9 @@ file(COPY ${LEAN_SOURCE_DIR}/bin/leanmake DESTINATION ${CMAKE_BINARY_DIR}/bin)
install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION bin)
add_subdirectory(shell)
if (${STAGE} GREATER 0 AND CADICAL)
install(PROGRAMS "${CADICAL}" DESTINATION bin)
endif()
add_custom_target(clean-stdlib
COMMAND rm -rf "${CMAKE_BINARY_DIR}/lib" || true)
@@ -658,3 +708,9 @@ endif()
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
if(USE_LAKE AND STAGE EQUAL 1)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../lakefile.toml)
endif()

View File

@@ -37,27 +37,13 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
f (ite P x y) = ite P (f x) (f y) :=
apply_dite f P (fun _ => x) (fun _ => y)
@[simp] theorem dite_eq_left_iff {P : Prop} [Decidable P] {B : ¬ P α} :
dite P (fun _ => a) B = a h, B h = a := by
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
@[simp] theorem dite_eq_right_iff {P : Prop} [Decidable P] {A : P α} :
(dite P A fun _ => b) = b h, A h = b := by
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
@[simp] theorem ite_eq_left_iff {P : Prop} [Decidable P] : ite P a b = a ¬P b = a :=
dite_eq_left_iff
@[simp] theorem ite_eq_right_iff {P : Prop} [Decidable P] : ite P a b = b P a = b :=
dite_eq_right_iff
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
@[simp] theorem dite_eq_ite [Decidable P] : (dite P (fun _ => a) fun _ => b) = ite P a b := rfl
-- We don't mark this as `simp` as it is already handled by `ite_eq_right_iff`.
theorem ite_some_none_eq_none [Decidable P] :
(if P then some x else none) = none ¬ P := by
simp only [ite_eq_right_iff]
simp only [ite_eq_right_iff, reduceCtorEq]
rfl
@[simp] theorem ite_some_none_eq_some [Decidable P] :
@@ -67,12 +53,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

@@ -134,6 +134,30 @@ The left-to-right direction, double negation elimination (DNE),
is classically true but not constructively. -/
@[simp] theorem not_not : ¬¬a a := Decidable.not_not
/-- Transfer decidability of `¬ p` to decidability of `p`. -/
-- This can not be an instance as it would be tried everywhere.
def decidable_of_decidable_not (p : Prop) [h : Decidable (¬ p)] : Decidable p :=
match h with
| isFalse h => isTrue (Classical.not_not.mp h)
| isTrue h => isFalse h
attribute [local instance] decidable_of_decidable_not in
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
@[simp low] protected theorem dite_not [hn : Decidable (¬p)] (x : ¬p α) (y : ¬¬p α) :
dite (¬p) x y = dite p (fun h => y (not_not_intro h)) x := by
cases hn <;> rename_i g
· simp [not_not.mp g]
· simp [g]
attribute [local instance] decidable_of_decidable_not in
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
@[simp low] protected theorem ite_not (p : Prop) [Decidable (¬ p)] (x y : α) : ite (¬p) x y = ite p y x :=
dite_not (fun _ => x) (fun _ => y)
attribute [local instance] decidable_of_decidable_not in
@[simp low] protected theorem decide_not (p : Prop) [Decidable (¬ p)] : decide (¬p) = !decide p :=
byCases (fun h : p => by simp_all) (fun h => by simp_all)
@[simp low] theorem not_forall {p : α Prop} : (¬ x, p x) x, ¬p x := Decidable.not_forall
theorem not_forall_not {p : α Prop} : (¬ x, ¬p x) x, p x := Decidable.not_forall_not
@@ -160,7 +184,7 @@ theorem not_iff : ¬(a ↔ b) ↔ (¬a ↔ b) := Decidable.not_iff
@[simp] theorem not_imp : ¬(a b) a ¬b := Decidable.not_imp_iff_and_not
@[simp] theorem imp_and_neg_imp_iff (p q : Prop) : (p q) (¬p q) q :=
@[simp] theorem imp_and_neg_imp_iff (p : Prop) {q : Prop} : (p q) (¬p q) q :=
Iff.intro (fun (a : _ _) => (Classical.em p).rec a.left a.right)
(fun a => And.intro (fun _ => a) (fun _ => a))

View File

@@ -28,7 +28,7 @@ Important instances include
* `Option`, where `failure := none` and `<|>` returns the left-most `some`.
* Parser combinators typically provide an `Applicative` instance for error-handling and
backtracking.
Error recovery and state can interact subtly. For example, the implementation of `Alternative` for `OptionT (StateT σ Id)` keeps modifications made to the state while recovering from failure, while `StateT σ (OptionT Id)` discards them.
-/
-- NB: List instance is in mathlib. Once upstreamed, add

View File

@@ -131,7 +131,7 @@ protected def adapt {ε' α : Type u} (f : ε → ε') : ExceptT ε m α → Exc
end ExceptT
@[always_inline]
instance (m : Type u Type v) (ε₁ : Type u) (ε₂ : Type u) [Monad m] [MonadExceptOf ε₁ m] : MonadExceptOf ε₁ (ExceptT ε₂ m) where
instance (m : Type u Type v) (ε₁ : Type u) (ε₂ : Type u) [MonadExceptOf ε₁ m] : MonadExceptOf ε₁ (ExceptT ε₂ m) where
throw e := ExceptT.mk <| throwThe ε₁ e
tryCatch x handle := ExceptT.mk <| tryCatchThe ε₁ x handle

View File

@@ -34,7 +34,7 @@ instance : Monad (ExceptCpsT ε m) where
bind x f := fun _ k₁ k₂ => x _ (fun a => f a _ k₁ k₂) k₂
instance : LawfulMonad (ExceptCpsT σ m) := by
refine' { .. } <;> intros <;> rfl
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
instance : MonadExceptOf ε (ExceptCpsT ε m) where
throw e := fun _ _ k => k e

View File

@@ -9,7 +9,7 @@ import Init.Meta
open Function
@[simp] theorem monadLift_self [Monad m] (x : m α) : monadLift x = x :=
@[simp] theorem monadLift_self {m : Type u Type v} (x : m α) : monadLift x = x :=
rfl
/--
@@ -153,7 +153,7 @@ namespace Id
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
instance : LawfulMonad Id := by
refine' { .. } <;> intros <;> rfl
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
end Id

View File

@@ -14,7 +14,7 @@ open Function
namespace ExceptT
theorem ext [Monad m] {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
theorem ext {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
simp [run] at h
assumption
@@ -50,7 +50,7 @@ theorem run_bind [Monad m] (x : ExceptT ε m α)
protected theorem seq_eq {α β ε : Type u} [Monad m] (mf : ExceptT ε m (α β)) (x : ExceptT ε m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl
protected theorem bind_pure_comp [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by
protected theorem bind_pure_comp [Monad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by
intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
@@ -188,23 +188,23 @@ 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 = _
simp
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
@[simp] theorem run_seqRight [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _
simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _
simp

View File

@@ -67,7 +67,7 @@ instance : MonadExceptOf Unit (OptionT m) where
throw := fun _ => OptionT.fail
tryCatch := OptionT.tryCatch
instance (ε : Type u) [Monad m] [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where
instance (ε : Type u) [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where
throw e := OptionT.mk <| throwThe ε e
tryCatch x handle := OptionT.mk <| tryCatchThe ε x handle

View File

@@ -32,7 +32,7 @@ instance : MonadControl m (ReaderT ρ m) where
restoreM x _ := x
@[always_inline]
instance ReaderT.tryFinally [MonadFinally m] [Monad m] : MonadFinally (ReaderT ρ m) where
instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)
@[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id

View File

@@ -87,7 +87,7 @@ protected def lift {α : Type u} (t : m α) : StateT σ m α :=
instance : MonadLift m (StateT σ m) := StateT.lift
@[always_inline]
instance (σ m) [Monad m] : MonadFunctor m (StateT σ m) := fun f x s => f (x s)
instance (σ m) : MonadFunctor m (StateT σ m) := fun f x s => f (x s)
@[always_inline]
instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := {

View File

@@ -14,16 +14,18 @@ def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type
namespace StateCpsT
variable {α σ : Type u} {m : Type u Type v}
@[always_inline, inline]
def runK {α σ : Type u} {m : Type u Type v} (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k
@[always_inline, inline]
def run {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s))
@[always_inline, inline]
def run' {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a)
@[always_inline]
@@ -33,7 +35,7 @@ instance : Monad (StateCpsT σ m) where
bind x f := fun δ s k => x δ s fun a s => f a δ s k
instance : LawfulMonad (StateCpsT σ m) := by
refine' { .. } <;> intros <;> rfl
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
@[always_inline]
instance : MonadStateOf σ (StateCpsT σ m) where
@@ -48,29 +50,29 @@ protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
instance [Monad m] : MonadLift m (StateCpsT σ m) where
monadLift := StateCpsT.lift
@[simp] theorem runK_pure {m : Type u Type v} (a : α) (s : σ) (k : α σ m β) : (pure a : StateCpsT σ m α).runK s k = k a s := rfl
@[simp] theorem runK_pure (a : α) (s : σ) (k : α σ m β) : (pure a : StateCpsT σ m α).runK s k = k a s := rfl
@[simp] theorem runK_get {m : Type u Type v} (s : σ) (k : σ σ m β) : (get : StateCpsT σ m σ).runK s k = k s s := rfl
@[simp] theorem runK_get (s : σ) (k : σ σ m β) : (get : StateCpsT σ m σ).runK s k = k s s := rfl
@[simp] theorem runK_set {m : Type u Type v} (s s' : σ) (k : PUnit σ m β) : (set s' : StateCpsT σ m PUnit).runK s k = k s' := rfl
@[simp] theorem runK_set (s s' : σ) (k : PUnit σ m β) : (set s' : StateCpsT σ m PUnit).runK s k = k s' := rfl
@[simp] theorem runK_modify {m : Type u Type v} (f : σ σ) (s : σ) (k : PUnit σ m β) : (modify f : StateCpsT σ m PUnit).runK s k = k (f s) := rfl
@[simp] theorem runK_modify (f : σ σ) (s : σ) (k : PUnit σ m β) : (modify f : StateCpsT σ m PUnit).runK s k = k (f s) := rfl
@[simp] theorem runK_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) (k : α σ m β) : (StateCpsT.lift x : StateCpsT σ m α).runK s k = x >>= (k . s) := rfl
@[simp] theorem runK_lift [Monad m] (x : m α) (s : σ) (k : α σ m β) : (StateCpsT.lift x : StateCpsT σ m α).runK s k = x >>= (k . s) := rfl
@[simp] theorem runK_monadLift {σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) (k : α σ m β)
@[simp] theorem runK_monadLift [Monad m] [MonadLiftT n m] (x : n α) (s : σ) (k : α σ m β)
: (monadLift x : StateCpsT σ m α).runK s k = (monadLift x : m α) >>= (k . s) := rfl
@[simp] theorem runK_bind_pure {α σ : Type u} [Monad m] (a : α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ) : (pure a >>= f).runK s k = (f a).runK s k := rfl
@[simp] theorem runK_bind_pure (a : α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ) : (pure a >>= f).runK s k = (f a).runK s k := rfl
@[simp] theorem runK_bind_lift {α σ : Type u} [Monad m] (x : m α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ)
@[simp] theorem runK_bind_lift [Monad m] (x : m α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ)
: (StateCpsT.lift x >>= f).runK s k = x >>= fun a => (f a).runK s k := rfl
@[simp] theorem runK_bind_get {σ : Type u} [Monad m] (f : σ StateCpsT σ m β) (s : σ) (k : β σ m γ) : (get >>= f).runK s k = (f s).runK s k := rfl
@[simp] theorem runK_bind_get (f : σ StateCpsT σ m β) (s : σ) (k : β σ m γ) : (get >>= f).runK s k = (f s).runK s k := rfl
@[simp] theorem runK_bind_set {σ : Type u} [Monad m] (f : PUnit StateCpsT σ m β) (s s' : σ) (k : β σ m γ) : (set s' >>= f).runK s k = (f ).runK s' k := rfl
@[simp] theorem runK_bind_set (f : PUnit StateCpsT σ m β) (s s' : σ) (k : β σ m γ) : (set s' >>= f).runK s k = (f ).runK s' k := rfl
@[simp] theorem runK_bind_modify {σ : Type u} [Monad m] (f : σ σ) (g : PUnit StateCpsT σ m β) (s : σ) (k : β σ m γ) : (modify f >>= g).runK s k = (g ).runK (f s) k := rfl
@[simp] theorem runK_bind_modify (f : σ σ) (g : PUnit StateCpsT σ m β) (s : σ) (k : β σ m γ) : (modify f >>= g).runK s k = (g ).runK (f s) k := rfl
@[simp] theorem run_eq [Monad m] (x : StateCpsT σ m α) (s : σ) : x.run s = x.runK s (fun a s => pure (a, s)) := rfl

View File

@@ -34,22 +34,22 @@ protected def lift (x : m α) : StateRefT' ω σ m α :=
instance [Monad m] : Monad (StateRefT' ω σ m) := inferInstanceAs (Monad (ReaderT _ _))
instance : MonadLift m (StateRefT' ω σ m) := StateRefT'.lift
instance (σ m) [Monad m] : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
@[inline]
protected def get [Monad m] [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
fun ref => ref.get
@[inline]
protected def set [Monad m] [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
fun ref => ref.set s
@[inline]
protected def modifyGet [Monad m] [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
protected def modifyGet [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
fun ref => ref.modifyGet f
instance [MonadLiftT (ST ω) m] [Monad m] : MonadStateOf σ (StateRefT' ω σ m) where
instance [MonadLiftT (ST ω) m] : MonadStateOf σ (StateRefT' ω σ m) where
get := StateRefT'.get
set := StateRefT'.set
modifyGet := StateRefT'.modifyGet
@@ -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

@@ -97,11 +97,18 @@ Users should prefer `unfold` for unfolding definitions. -/
syntax (name := delta) "delta" (ppSpace colGt ident)+ : conv
/--
* `unfold foo` unfolds all occurrences of `foo` in the target.
* `unfold id` unfolds all occurrences of definition `id` in the target.
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
Like the `unfold` tactic, this uses equational lemmas for the chosen definition
to rewrite the target. For recursive definitions,
only one layer of unfolding is performed. -/
Definitions can be either global or local definitions.
For non-recursive global definitions, this tactic is identical to `delta`.
For recursive global definitions, it uses the "unfolding lemma" `id.eq_def`,
which is generated for each recursive definition, to unfold according to the recursive definition given by the user.
Only one level of unfolding is performed, in contrast to `simp only [id]`, which unfolds definition `id` recursively.
This is the `conv` version of the `unfold` tactic.
-/
syntax (name := unfold) "unfold" (ppSpace colGt ident)+ : conv
/--

View File

@@ -36,6 +36,17 @@ and `flip (·<·)` is the greater-than relation.
theorem Function.comp_def {α β δ} (f : β δ) (g : α β) : f g = fun x => f (g x) := rfl
@[simp] theorem Function.const_comp {f : α β} {c : γ} :
(Function.const β c f) = Function.const α c := by
rfl
@[simp] theorem Function.comp_const {f : β γ} {b : β} :
(f Function.const α b) = Function.const α (f b) := by
rfl
@[simp] theorem Function.true_comp {f : α β} : ((fun _ => true) f) = fun _ => true := by
rfl
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false := by
rfl
attribute [simp] namedPattern
/--
@@ -154,9 +165,23 @@ inductive PSum (α : Sort u) (β : Sort v) where
@[inherit_doc] infixr:30 " ⊕' " => PSum
instance {α β} [Inhabited α] : Inhabited (PSum α β) := PSum.inl default
/--
`PSum α β` is inhabited if `α` is inhabited.
This is not an instance to avoid non-canonical instances.
-/
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := PSum.inl default
instance {α β} [Inhabited β] : Inhabited (PSum α β) := PSum.inr default
/--
`PSum α β` is inhabited if `β` is inhabited.
This is not an instance to avoid non-canonical instances.
-/
@[reducible] def PSum.inhabitedRight {α β} [Inhabited β] : Inhabited (PSum α β) := PSum.inr default
instance PSum.nonemptyLeft [h : Nonempty α] : Nonempty (PSum α β) :=
Nonempty.elim h (fun a => PSum.inl a)
instance PSum.nonemptyRight [h : Nonempty β] : Nonempty (PSum α β) :=
Nonempty.elim h (fun b => PSum.inr b)
/--
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
@@ -474,6 +499,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 }`. -/
@@ -642,7 +669,7 @@ instance : LawfulBEq String := inferInstance
/-! # Logical connectives and equality -/
@[inherit_doc True.intro] def trivial : True :=
@[inherit_doc True.intro] theorem trivial : True :=
theorem mt {a b : Prop} (h₁ : a b) (h₂ : ¬b) : ¬a :=
fun ha => h₂ (h₁ ha)
@@ -701,7 +728,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 +781,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' :=
@@ -787,17 +814,15 @@ theorem cast_heq {α β : Sort u} : (h : α = β) → (a : α) → HEq (cast h a
variable {a b c d : Prop}
theorem iff_iff_implies_and_implies (a b : Prop) : (a b) (a b) (b a) :=
theorem iff_iff_implies_and_implies {a b : Prop} : (a b) (a b) (b a) :=
Iff.intro (fun h => And.intro h.mp h.mpr) (fun h => Iff.intro h.left h.right)
theorem Iff.refl (a : Prop) : a a :=
@[refl] theorem Iff.refl (a : Prop) : a a :=
Iff.intro (fun h => h) (fun h => h)
protected theorem Iff.rfl {a : Prop} : a a :=
Iff.refl a
macro_rules | `(tactic| rfl) => `(tactic| exact Iff.rfl)
theorem Iff.of_eq (h : a = b) : a b := h Iff.rfl
theorem Iff.trans (h₁ : a b) (h₂ : b c) : a c :=
@@ -810,15 +835,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
@@ -883,7 +908,7 @@ theorem byContradiction [dec : Decidable p] (h : ¬p → False) : p :=
theorem of_not_not [Decidable p] : ¬ ¬ p p :=
fun hnn => byContradiction (fun hn => absurd hn hnn)
theorem not_and_iff_or_not (p q : Prop) [d₁ : Decidable p] [d₂ : Decidable q] : ¬ (p q) ¬ p ¬ q :=
theorem not_and_iff_or_not {p q : Prop} [d₁ : Decidable p] [d₂ : Decidable q] : ¬ (p q) ¬ p ¬ q :=
Iff.intro
(fun h => match d₁, d₂ with
| isTrue h₁, isTrue h₂ => absurd (And.intro h₁ h₂) h
@@ -1089,19 +1114,30 @@ 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
theorem Relation.TransGen.trans {α : Sort u} {r : α α Prop} {a b c} :
TransGen r a b TransGen r b c TransGen r a c := by
intro hab hbc
induction hbc with
| single h => exact TransGen.tail hab h
| tail _ h ih => exact TransGen.tail ih h
/-! # Subtype -/
namespace Subtype
theorem existsOfSubtype {α : Type u} {p : α Prop} : { x // p x } Exists (fun x => p x)
| a, h => a, h
@@ -1126,12 +1162,20 @@ end Subtype
section
variable {α : Type u} {β : Type v}
instance Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
/-- This is not an instance to avoid non-canonical instances. -/
@[reducible] def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
default := Sum.inl default
instance Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
/-- This is not an instance to avoid non-canonical instances. -/
@[reducible] def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
default := Sum.inr default
instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
Nonempty.elim h (fun a => Sum.inl a)
instance Sum.nonemptyRight [h : Nonempty β] : Nonempty (Sum α β) :=
Nonempty.elim h (fun b => Sum.inr b)
instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b =>
match a, b with
| Sum.inl a, Sum.inl b =>
@@ -1173,7 +1217,7 @@ def Prod.lexLt [LT α] [LT β] (s : α × β) (t : α × β) : Prop :=
s.1 < t.1 (s.1 = t.1 s.2 < t.2)
instance Prod.lexLtDec
[LT α] [LT β] [DecidableEq α] [DecidableEq β]
[LT α] [LT β] [DecidableEq α]
[(a b : α) Decidable (a < b)] [(a b : β) Decidable (a < b)]
: (s t : α × β) Decidable (Prod.lexLt s t) :=
fun _ _ => inferInstanceAs (Decidable (_ _))
@@ -1191,11 +1235,20 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
(f : α₁ α₂) (g : β₁ β₂) : α₁ × β₁ α₂ × β₂
| (a, b) => (f a, g b)
@[simp] theorem Prod.map_apply (f : α β) (g : γ δ) (x) (y) :
Prod.map f g (x, y) = (f x, g y) := rfl
@[simp] theorem Prod.map_fst (f : α β) (g : γ δ) (x) : (Prod.map f g x).1 = f x.1 := rfl
@[simp] theorem Prod.map_snd (f : α β) (g : γ δ) (x) : (Prod.map f g x).2 = g x.2 := rfl
/-! # Dependent products -/
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
theorem Exists.of_psigma_prop {α : Sort u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
| x, hx => x, hx
@[deprecated Exists.of_psigma_prop (since := "2024-07-27")]
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x) :=
Exists.of_psigma_prop
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₁
@@ -1318,7 +1371,7 @@ theorem Nat.succ.inj {m n : Nat} : m.succ = n.succ → m = n :=
theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) :=
Eq.propIntro Nat.succ.inj (congrArg Nat.succ)
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] (a b : α) : a == b a = b :=
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b :=
eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl
/-! # Prop lemmas -/
@@ -1357,6 +1410,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
@@ -1380,7 +1436,7 @@ theorem false_of_true_eq_false (h : True = False) : False := false_of_true_iff_
theorem true_eq_false_of_false : False (True = False) := False.elim
theorem iff_def : (a b) (a b) (b a) := iff_iff_implies_and_implies a b
theorem iff_def : (a b) (a b) (b a) := iff_iff_implies_and_implies
theorem iff_def' : (a b) (b a) (a b) := Iff.trans iff_def And.comm
theorem true_iff_false : (True False) False := iff_false_intro (·.mp True.intro)
@@ -1408,7 +1464,7 @@ theorem imp_true_iff (α : Sort u) : (α → True) ↔ True := iff_true_intro (f
theorem false_imp_iff (a : Prop) : (False a) True := iff_true_intro False.elim
theorem true_imp_iff (α : Prop) : (True α) α := imp_iff_right True.intro
theorem true_imp_iff {α : Prop} : (True α) α := imp_iff_right True.intro
@[simp high] theorem imp_self : (a a) True := iff_true_intro id
@@ -1528,13 +1584,13 @@ so you should consider the simpler versions if they apply:
* `Quot.recOnSubsingleton`, when the target type is a `Subsingleton`
* `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption
-/
protected abbrev rec
@[elab_as_elim] protected abbrev rec
(f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
(q : Quot r) : motive q :=
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)
@@ -1545,7 +1601,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))
@@ -1614,7 +1670,7 @@ protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s → Pro
/--
The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`,
then it lifts to a function on `Quotient s` such that `lift (mk a) f h = f a`.
then it lifts to a function on `Quotient s` such that `liftOn (mk a) f h = f a`.
-/
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α β) (c : (a b : α) a b f a = f b) : β :=
Quot.liftOn q f c
@@ -1862,7 +1918,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,9 @@ import Init.Data.Queue
import Init.Data.Channel
import Init.Data.Cast
import Init.Data.Sum
import Init.Data.BEq
import Init.Data.Subtype
import Init.Data.ULift
import Init.Data.PLift
import Init.Data.Zero
import Init.Data.NeZero

View File

@@ -6,7 +6,7 @@ Authors: Dany Fabian
prelude
import Init.Classical
import Init.Data.List
import Init.ByCases
namespace Lean.Data.AC
inductive Expr
@@ -260,7 +260,7 @@ theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm
simp [ContextInformation.isComm, Option.isSome] at h
match h₂ : ctx.comm with
| none =>
simp only [h₂] at h
simp [h₂] at h
| some val =>
simp [h₂] at h
exact val.down

View File

@@ -10,5 +10,8 @@ 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
import Init.Data.Array.Bootstrap

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.toList.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

@@ -16,10 +16,11 @@ universe u v w
namespace Array
variable {α : Type u}
@[deprecated Array.toList (since := "2024-09-10")] abbrev Array.data := @Array.toList
@[extern "lean_mk_array"]
def mkArray {α : Type u} (n : Nat) (v : α) : Array α := {
data := List.replicate n v
}
def mkArray {α : Type u} (n : Nat) (v : α) : Array α where
toList := List.replicate n v
/--
`ofFn f` with `f : Fin n → α` returns the list whose ith element is `f i`.
@@ -50,6 +51,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 +68,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 +109,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.
@@ -129,9 +135,8 @@ def swapAt! (a : Array α) (i : Nat) (v : α) : α × Array α :=
panic! ("index " ++ toString i ++ " out of bounds")
@[extern "lean_array_pop"]
def pop (a : Array α) : Array α := {
data := a.data.dropLast
}
def pop (a : Array α) : Array α where
toList := a.toList.dropLast
def shrink (a : Array α) (n : Nat) : Array α :=
let rec loop
@@ -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
@@ -481,7 +486,7 @@ def all (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool
Id.run <| as.allM p start stop
def contains [BEq α] (as : Array α) (a : α) : Bool :=
as.any fun b => a == b
as.any (· == a)
def elem [BEq α] (a : α) (as : Array α) : Bool :=
as.contains a
@@ -494,10 +499,10 @@ def elem [BEq α] (a : α) (as : Array α) : Bool :=
(true, r)
/-- Convert a `Array α` into an `List α`. This is O(n) in the size of the array. -/
-- This function is exported to C, where it is called by `Array.data`
-- This function is exported to C, where it is called by `Array.toList`
-- (the projection) to implement this functionality.
@[export lean_array_to_list]
def toList (as : Array α) : List α :=
@[export lean_array_to_list_impl]
def toListImpl (as : Array α) : List α :=
as.foldr List.cons []
/-- Prepends an `Array α` onto the front of a list. Equivalent to `as.toList ++ l`. -/
@@ -788,28 +793,32 @@ def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : ∀ (i : Nat), i
def toArrayLit (a : Array α) (n : Nat) (hsz : a.size = n) : Array α :=
List.toArray <| toListLitAux a n hsz n (hsz Nat.le_refl _) []
theorem ext' {as bs : Array α} (h : as.data = bs.data) : as = bs := by
theorem ext' {as bs : Array α} (h : as.toList = bs.toList) : as = bs := by
cases as; cases bs; simp at h; rw [h]
theorem toArrayAux_eq (as : List α) (acc : Array α) : (as.toArrayAux acc).data = acc.data ++ as := by
@[simp] theorem toArrayAux_eq (as : List α) (acc : Array α) : (as.toArrayAux acc).toList = acc.toList ++ as := by
induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append]
theorem data_toArray (as : List α) : as.toArray.data = as := by
simp [List.toArray, toArrayAux_eq, Array.mkEmpty]
@[simp] theorem toList_toArray (as : List α) : as.toArray.toList = as := by
simp [List.toArray, Array.mkEmpty]
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @toList_toArray
@[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size]
theorem toArrayLit_eq (as : Array α) (n : Nat) (hsz : as.size = n) : as = toArrayLit as n hsz := by
apply ext'
simp [toArrayLit, data_toArray]
simp [toArrayLit, toList_toArray]
have hle : n as.size := hsz Nat.le_refl _
have hge : as.size n := hsz Nat.le_refl _
have := go n hle
rw [List.drop_eq_nil_of_le hge] at this
rw [this]
where
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.data i ((id (α := as.data.length = n) h₁) h₂) :=
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.toList i ((id (α := as.toList.length = n) h₁) h₂) :=
rfl
go (i : Nat) (hi : i as.size) : toListLitAux as n hsz i hi (as.data.drop i) = as.data := by
go (i : Nat) (hi : i as.size) : toListLitAux as n hsz i hi (as.toList.drop i) = as.toList := by
induction i <;> simp [getLit_eq, List.get_drop_eq_drop, toListLitAux, List.drop, *]
def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size bs.size) (i : Nat) : Bool :=

View File

@@ -9,7 +9,7 @@ import Init.Data.Nat.Linear
import Init.NotationExtra
theorem Array.of_push_eq_push {as bs : Array α} (h : as.push a = bs.push b) : as = bs a = b := by
simp [push] at h
simp only [push, mk.injEq] at h
have h₁, h₂ := List.of_concat_eq_concat h
cases as; cases bs
simp_all
@@ -38,7 +38,7 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
· intro h; rw [h]
def Array.mapM' [Monad m] (f : α m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
go 0 mkEmpty as.size, rfl (by simp_arith)
go 0 mkEmpty as.size, rfl (by simp)
where
go (i : Nat) (acc : { bs : Array β // bs.size = i }) (hle : i as.size) : m { bs : Array β // bs.size = as.size } := do
if h : i = as.size then

View File

@@ -0,0 +1,120 @@
/-
Copyright (c) 2022 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.TakeDrop
/-!
## Bootstrapping theorems about arrays
This file contains some theorems about `Array` and `List` needed for `Init.Data.List.Impl`.
-/
namespace Array
theorem foldlM_eq_foldlM_toList.aux [Monad m]
(f : β α m β) (arr : Array α) (i j) (H : arr.size i + j) (b) :
foldlM.loop f arr arr.size (Nat.le_refl _) i j b = (arr.toList.drop j).foldlM f b := by
unfold foldlM.loop
split; split
· cases Nat.not_le_of_gt _ (Nat.zero_add _ H)
· rename_i i; rw [Nat.succ_add] at H
simp [foldlM_eq_foldlM_toList.aux f arr i (j+1) H]
rw (config := {occs := .pos [2]}) [ List.get_drop_eq_drop _ _ _]
rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
theorem foldlM_eq_foldlM_toList [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
arr.foldlM f init = arr.toList.foldlM f init := by
simp [foldlM, foldlM_eq_foldlM_toList.aux]
theorem foldl_eq_foldl_toList (f : β α β) (init : β) (arr : Array α) :
arr.foldl f init = arr.toList.foldl f init :=
List.foldl_eq_foldlM .. foldlM_eq_foldlM_toList ..
theorem foldrM_eq_reverse_foldlM_toList.aux [Monad m]
(f : α β m β) (arr : Array α) (init : β) (i h) :
(arr.toList.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f arr 0 i h init := by
unfold foldrM.fold
match i with
| 0 => simp [List.foldlM, List.take]
| i+1 => rw [ List.take_concat_get _ _ h]; simp [ (aux f arr · i)]; rfl
theorem foldrM_eq_reverse_foldlM_toList [Monad m] (f : α β m β) (init : β) (arr : Array α) :
arr.foldrM f init = arr.toList.reverse.foldlM (fun x y => f y x) init := by
have : arr = #[] 0 < arr.size :=
match arr with | [] => .inl rfl | a::l => .inr (Nat.zero_lt_succ _)
match arr, this with | _, .inl rfl => rfl | arr, .inr h => ?_
simp [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
theorem foldrM_eq_foldrM_toList [Monad m]
(f : α β m β) (init : β) (arr : Array α) :
arr.foldrM f init = arr.toList.foldrM f init := by
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
theorem foldr_eq_foldr_toList (f : α β β) (init : β) (arr : Array α) :
arr.foldr f init = arr.toList.foldr f init :=
List.foldr_eq_foldrM .. foldrM_eq_foldrM_toList ..
@[simp] theorem push_toList (arr : Array α) (a : α) : (arr.push a).toList = arr.toList ++ [a] := by
simp [push, List.concat_eq_append]
@[simp] theorem toListAppend_eq (arr : Array α) (l) : arr.toListAppend l = arr.toList ++ l := by
simp [toListAppend, foldr_eq_foldr_toList]
@[simp] theorem toListImpl_eq (arr : Array α) : arr.toListImpl = arr.toList := by
simp [toListImpl, foldr_eq_foldr_toList]
@[simp] theorem pop_toList (arr : Array α) : arr.pop.toList = arr.toList.dropLast := rfl
@[simp] theorem append_eq_append (arr arr' : Array α) : arr.append arr' = arr ++ arr' := rfl
@[simp] theorem append_toList (arr arr' : Array α) :
(arr ++ arr').toList = arr.toList ++ arr'.toList := by
rw [ append_eq_append]; unfold Array.append
rw [foldl_eq_foldl_toList]
induction arr'.toList generalizing arr <;> simp [*]
@[simp] theorem appendList_eq_append
(arr : Array α) (l : List α) : arr.appendList l = arr ++ l := rfl
@[simp] theorem appendList_toList (arr : Array α) (l : List α) :
(arr ++ l).toList = arr.toList ++ l := by
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing arr <;> simp [*]
@[deprecated foldlM_eq_foldlM_toList (since := "2024-09-09")]
abbrev foldlM_eq_foldlM_data := @foldlM_eq_foldlM_toList
@[deprecated foldl_eq_foldl_toList (since := "2024-09-09")]
abbrev foldl_eq_foldl_data := @foldl_eq_foldl_toList
@[deprecated foldrM_eq_reverse_foldlM_toList (since := "2024-09-09")]
abbrev foldrM_eq_reverse_foldlM_data := @foldrM_eq_reverse_foldlM_toList
@[deprecated foldrM_eq_foldrM_toList (since := "2024-09-09")]
abbrev foldrM_eq_foldrM_data := @foldrM_eq_foldrM_toList
@[deprecated foldr_eq_foldr_toList (since := "2024-09-09")]
abbrev foldr_eq_foldr_data := @foldr_eq_foldr_toList
@[deprecated push_toList (since := "2024-09-09")]
abbrev push_data := @push_toList
@[deprecated toListImpl_eq (since := "2024-09-09")]
abbrev toList_eq := @toListImpl_eq
@[deprecated pop_toList (since := "2024-09-09")]
abbrev pop_data := @pop_toList
@[deprecated append_toList (since := "2024-09-09")]
abbrev append_data := @append_toList
@[deprecated appendList_toList (since := "2024-09-09")]
abbrev appendList_data := @appendList_toList
end Array

View File

@@ -4,17 +4,17 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.Nat.MinMax
import Init.Data.Nat.Lemmas
import Init.Data.List.Lemmas
import Init.Data.Fin.Basic
import Init.Data.List.Impl
import Init.Data.List.Monadic
import Init.Data.List.Range
import Init.Data.Array.Mem
import Init.TacticsExtra
/-!
## Bootstrapping theorems about arrays
This file contains some theorems about `Array` and `List` needed for `Std.List.Basic`.
This file contains some theorems about `Array` and `List` needed for `Init.Data.List.Impl`.
-/
namespace Array
@@ -23,71 +23,34 @@ attribute [simp] data_toArray uset
@[simp] theorem singleton_def (v : α) : singleton v = #[v] := rfl
@[simp] theorem toArray_data : (a : Array α) a.data.toArray = a
| l => ext' (data_toArray l)
@[simp] theorem toArray_toList : (a : Array α) a.toList.toArray = a
| l => ext' (toList_toArray l)
@[simp] theorem data_length {l : Array α} : l.data.length = l.size := rfl
@[deprecated toArray_toList (since := "2024-09-09")]
abbrev toArray_data := @toArray_toList
@[simp] theorem toList_length {l : Array α} : l.toList.length = l.size := rfl
@[deprecated toList_length (since := "2024-09-09")]
abbrev data_length := @toList_length
@[simp] theorem mkEmpty_eq (α n) : @mkEmpty α n = #[] := rfl
@[simp] theorem size_toArray (as : List α) : as.toArray.size = as.length := by simp [size]
@[simp] theorem size_mk (as : List α) : (Array.mk as).size = as.length := by simp [size]
theorem getElem_eq_data_get (a : Array α) (h : i < a.size) : a[i] = a.data.get i, h := by
theorem getElem_eq_toList_getElem (a : Array α) (h : i < a.size) : a[i] = a.toList[i] := by
by_cases i < a.size <;> (try simp [*]) <;> rfl
theorem foldlM_eq_foldlM_data.aux [Monad m]
(f : β α m β) (arr : Array α) (i j) (H : arr.size i + j) (b) :
foldlM.loop f arr arr.size (Nat.le_refl _) i j b = (arr.data.drop j).foldlM f b := by
unfold foldlM.loop
split; split
· cases Nat.not_le_of_gt _ (Nat.zero_add _ H)
· rename_i i; rw [Nat.succ_add] at H
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
@[deprecated getElem_eq_toList_getElem (since := "2024-09-09")]
abbrev getElem_eq_data_getElem := @getElem_eq_toList_getElem
theorem foldlM_eq_foldlM_data [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
arr.foldlM f init = arr.data.foldlM f init := by
simp [foldlM, foldlM_eq_foldlM_data.aux]
theorem foldl_eq_foldl_data (f : β α β) (init : β) (arr : Array α) :
arr.foldl f init = arr.data.foldl f init :=
List.foldl_eq_foldlM .. foldlM_eq_foldlM_data ..
theorem foldrM_eq_reverse_foldlM_data.aux [Monad m]
(f : α β m β) (arr : Array α) (init : β) (i h) :
(arr.data.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f arr 0 i h init := by
unfold foldrM.fold
match i with
| 0 => simp [List.foldlM, List.take]
| i+1 => rw [ List.take_concat_get _ _ h]; simp [ (aux f arr · i)]; rfl
theorem foldrM_eq_reverse_foldlM_data [Monad m] (f : α β m β) (init : β) (arr : Array α) :
arr.foldrM f init = arr.data.reverse.foldlM (fun x y => f y x) init := by
have : arr = #[] 0 < arr.size :=
match arr with | [] => .inl rfl | a::l => .inr (Nat.zero_lt_succ _)
match arr, this with | _, .inl rfl => rfl | arr, .inr h => ?_
simp [foldrM, h, foldrM_eq_reverse_foldlM_data.aux, List.take_length]
theorem foldrM_eq_foldrM_data [Monad m]
(f : α β m β) (init : β) (arr : Array α) :
arr.foldrM f init = arr.data.foldrM f init := by
rw [foldrM_eq_reverse_foldlM_data, List.foldlM_reverse]
theorem foldr_eq_foldr_data (f : α β β) (init : β) (arr : Array α) :
arr.foldr f init = arr.data.foldr f init :=
List.foldr_eq_foldrM .. foldrM_eq_foldrM_data ..
@[simp] theorem push_data (arr : Array α) (a : α) : (arr.push a).data = arr.data ++ [a] := by
simp [push, List.concat_eq_append]
@[deprecated getElem_eq_toList_getElem (since := "2024-06-12")]
theorem getElem_eq_toList_get (a : Array α) (h : i < a.size) : a[i] = a.toList.get i, h := by
simp [getElem_eq_toList_getElem]
theorem foldrM_push [Monad m] (f : α β m β) (init : β) (arr : Array α) (a : α) :
(arr.push a).foldrM f init = f a init >>= arr.foldrM f := by
simp [foldrM_eq_reverse_foldlM_data, -size_push]
simp [foldrM_eq_reverse_foldlM_toList, -size_push]
@[simp] theorem foldrM_push' [Monad m] (f : α β m β) (init : β) (arr : Array α) (a : α) :
(arr.push a).foldrM f init (start := arr.size + 1) = f a init >>= arr.foldrM f := by
@@ -99,26 +62,20 @@ theorem foldr_push (f : α → β → β) (init : β) (arr : Array α) (a : α)
@[simp] theorem foldr_push' (f : α β β) (init : β) (arr : Array α) (a : α) :
(arr.push a).foldr f init (start := arr.size + 1) = arr.foldr f (f a init) := foldrM_push' ..
@[simp] theorem toListAppend_eq (arr : Array α) (l) : arr.toListAppend l = arr.data ++ l := by
simp [toListAppend, foldr_eq_foldr_data]
@[simp] theorem toList_eq (arr : Array α) : arr.toList = arr.data := by
simp [toList, foldr_eq_foldr_data]
/-- A more efficient version of `arr.toList.reverse`. -/
@[inline] def toListRev (arr : Array α) : List α := arr.foldl (fun l t => t :: l) []
@[simp] theorem toListRev_eq (arr : Array α) : arr.toListRev = arr.data.reverse := by
rw [toListRev, foldl_eq_foldl_data, List.foldr_reverse, List.foldr_self]
@[simp] theorem toListRev_eq (arr : Array α) : arr.toListRev = arr.toList.reverse := by
rw [toListRev, foldl_eq_foldl_toList, List.foldr_reverse, List.foldr_cons_nil]
theorem get_push_lt (a : Array α) (x : α) (i : Nat) (h : i < a.size) :
have : i < (a.push x).size := by simp [*, Nat.lt_succ_of_le, Nat.le_of_lt]
(a.push x)[i] = a[i] := by
simp only [push, getElem_eq_data_get, List.concat_eq_append, List.get_append_left, h]
simp only [push, getElem_eq_toList_getElem, List.concat_eq_append, List.getElem_append_left, h]
@[simp] theorem get_push_eq (a : Array α) (x : α) : (a.push x)[a.size] = x := by
simp only [push, getElem_eq_data_get, List.concat_eq_append]
rw [List.get_append_right] <;> simp [getElem_eq_data_get, Nat.zero_lt_one]
simp only [push, getElem_eq_toList_getElem, List.concat_eq_append]
rw [List.getElem_append_right] <;> simp [getElem_eq_toList_getElem, Nat.zero_lt_one]
theorem get_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size) :
(a.push x)[i] = if h : i < a.size then a[i] else x := by
@@ -129,61 +86,54 @@ theorem get_push (a : Array α) (x : α) (i : Nat) (h : i < (a.push x).size) :
theorem mapM_eq_foldlM [Monad m] [LawfulMonad m] (f : α m β) (arr : Array α) :
arr.mapM f = arr.foldlM (fun bs a => bs.push <$> f a) #[] := by
rw [mapM, aux, foldlM_eq_foldlM_data]; rfl
rw [mapM, aux, foldlM_eq_foldlM_toList]; rfl
where
aux (i r) :
mapM.map f arr i r = (arr.data.drop i).foldlM (fun bs a => bs.push <$> f a) r := by
mapM.map f arr i r = (arr.toList.drop i).foldlM (fun bs a => bs.push <$> f a) r := by
unfold mapM.map; split
· rw [ List.get_drop_eq_drop _ i _]
simp [aux (i+1), map_eq_pure_bind]; rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
simp only [aux (i + 1), map_eq_pure_bind, toList_length, List.foldlM_cons, bind_assoc,
pure_bind]
rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
termination_by arr.size - i
decreasing_by decreasing_trivial_pre_omega
@[simp] theorem map_data (f : α β) (arr : Array α) : (arr.map f).data = arr.data.map f := by
@[simp] theorem map_toList (f : α β) (arr : Array α) : (arr.map f).toList = arr.toList.map f := by
rw [map, mapM_eq_foldlM]
apply congrArg data (foldl_eq_foldl_data (fun bs a => push bs (f a)) #[] arr) |>.trans
have H (l arr) : List.foldl (fun bs a => push bs (f a)) arr l = arr.data ++ l.map f := by
apply congrArg toList (foldl_eq_foldl_toList (fun bs a => push bs (f a)) #[] arr) |>.trans
have H (l arr) : List.foldl (fun bs a => push bs (f a)) arr l = arr.toList ++ l.map f := by
induction l generalizing arr <;> simp [*]
simp [H]
@[deprecated map_toList (since := "2024-09-09")]
abbrev map_data := @map_toList
@[simp] theorem size_map (f : α β) (arr : Array α) : (arr.map f).size = arr.size := by
simp only [ data_length]
simp only [ toList_length]
simp
@[simp] theorem pop_data (arr : Array α) : arr.pop.data = arr.data.dropLast := rfl
@[simp] theorem append_eq_append (arr arr' : Array α) : arr.append arr' = arr ++ arr' := rfl
@[simp] theorem append_data (arr arr' : Array α) :
(arr ++ arr').data = arr.data ++ arr'.data := by
rw [ append_eq_append]; unfold Array.append
rw [foldl_eq_foldl_data]
induction arr'.data generalizing arr <;> simp [*]
@[simp] theorem appendList_eq_append
(arr : Array α) (l : List α) : arr.appendList l = arr ++ l := rfl
@[simp] theorem appendList_data (arr : Array α) (l : List α) :
(arr ++ l).data = arr.data ++ l := by
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing arr <;> simp [*]
@[simp] theorem appendList_nil (arr : Array α) : arr ++ ([] : List α) = arr := Array.ext' (by simp)
@[simp] theorem appendList_cons (arr : Array α) (a : α) (l : List α) :
arr ++ (a :: l) = arr.push a ++ l := Array.ext' (by simp)
theorem foldl_data_eq_bind (l : List α) (acc : Array β)
theorem foldl_toList_eq_bind (l : List α) (acc : Array β)
(F : Array β α Array β) (G : α List β)
(H : acc a, (F acc a).data = acc.data ++ G a) :
(l.foldl F acc).data = acc.data ++ l.bind G := by
(H : acc a, (F acc a).toList = acc.toList ++ G a) :
(l.foldl F acc).toList = acc.toList ++ l.bind G := by
induction l generalizing acc <;> simp [*, List.bind]
theorem foldl_data_eq_map (l : List α) (acc : Array β) (G : α β) :
(l.foldl (fun acc a => acc.push (G a)) acc).data = acc.data ++ l.map G := by
@[deprecated foldl_toList_eq_bind (since := "2024-09-09")]
abbrev foldl_data_eq_bind := @foldl_toList_eq_bind
theorem foldl_toList_eq_map (l : List α) (acc : Array β) (G : α β) :
(l.foldl (fun acc a => acc.push (G a)) acc).toList = acc.toList ++ l.map G := by
induction l generalizing acc <;> simp [*]
@[deprecated foldl_toList_eq_map (since := "2024-09-09")]
abbrev foldl_data_eq_map := @foldl_toList_eq_map
theorem size_uset (a : Array α) (v i h) : (uset a i v h).size = a.size := by simp
theorem anyM_eq_anyM_loop [Monad m] (p : α m Bool) (as : Array α) (start stop) :
@@ -194,9 +144,12 @@ theorem anyM_stop_le_start [Monad m] (p : α → m Bool) (as : Array α) (start
(h : min stop as.size start) : anyM p as start stop = pure false := by
rw [anyM_eq_anyM_loop, anyM.loop, dif_neg (Nat.not_lt.2 h)]
theorem mem_def (a : α) (as : Array α) : a as a as.data :=
theorem mem_def {a : α} {as : Array α} : a as a as.toList :=
fun | .mk h => h, Array.Mem.mk
@[simp] theorem not_mem_empty (a : α) : ¬(a #[]) := by
simp [mem_def]
/-! # get -/
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
@@ -215,7 +168,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]
@@ -233,11 +186,11 @@ theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default
@[simp] theorem getElem_set_eq (a : Array α) (i : Fin a.size) (v : α) {j : Nat}
(eq : i.val = j) (p : j < (a.set i v).size) :
(a.set i v)[j]'p = v := by
simp [set, getElem_eq_data_get, eq]
simp [set, getElem_eq_toList_getElem, eq]
@[simp] theorem getElem_set_ne (a : Array α) (i : Fin a.size) (v : α) {j : Nat} (pj : j < (a.set i v).size)
(h : i.val j) : (a.set i v)[j]'pj = a[j]'(size_set a i v pj) := by
simp only [set, getElem_eq_data_get, List.get_set_ne _ h]
simp only [set, getElem_eq_toList_getElem, List.getElem_set_ne h]
theorem getElem_set (a : Array α) (i : Fin a.size) (v : α) (j : Nat)
(h : j < (a.set i v).size) :
@@ -318,42 +271,87 @@ termination_by n - i
/-- # mkArray -/
@[simp] theorem mkArray_data (n : Nat) (v : α) : (mkArray n v).data = List.replicate n v := rfl
@[simp] theorem toList_mkArray (n : Nat) (v : α) : (mkArray n v).toList = List.replicate n v := rfl
@[deprecated toList_mkArray (since := "2024-09-09")]
abbrev mkArray_data := @toList_mkArray
@[simp] theorem getElem_mkArray (n : Nat) (v : α) (h : i < (mkArray n v).size) :
(mkArray n v)[i] = v := by simp [Array.getElem_eq_data_get]
(mkArray n v)[i] = v := by simp [Array.getElem_eq_toList_getElem]
/-- # mem -/
theorem mem_data {a : α} {l : Array α} : a l.data a l := (mem_def _ _).symm
theorem mem_toList {a : α} {l : Array α} : a l.toList a l := mem_def.symm
@[deprecated mem_toList (since := "2024-09-09")]
abbrev mem_data := @mem_toList
theorem not_mem_nil (a : α) : ¬ a #[] := nofun
theorem getElem_of_mem {a : α} {as : Array α} :
a as ( (n : Nat) (h : n < as.size), as[n]'h = a) := by
intro ha
rcases List.getElem_of_mem ha.val with i, hbound, hi
exists i
exists hbound
@[simp] theorem mem_dite_empty_left {x : α} [Decidable p] {l : ¬ p Array α} :
(x if h : p then #[] else l h) h : ¬ p, x l h := by
split <;> simp_all [mem_def]
@[simp] theorem mem_dite_empty_right {x : α} [Decidable p] {l : p Array α} :
(x if h : p then l h else #[]) h : p, x l h := by
split <;> simp_all [mem_def]
@[simp] theorem mem_ite_empty_left {x : α} [Decidable p] {l : Array α} :
(x if p then #[] else l) ¬ p x l := by
split <;> simp_all [mem_def]
@[simp] theorem mem_ite_empty_right {x : α} [Decidable p] {l : Array α} :
(x if p then l else #[]) p x l := by
split <;> simp_all [mem_def]
/-- # get lemmas -/
theorem lt_of_getElem {x : α} {a : Array α} {idx : Nat} {hidx : idx < a.size} (_ : a[idx] = x) :
idx < a.size :=
hidx
theorem getElem?_mem {l : Array α} {i : Fin l.size} : l[i] l := by
erw [Array.mem_def, getElem_eq_data_get]
erw [Array.mem_def, getElem_eq_toList_getElem]
apply List.get_mem
theorem getElem_fin_eq_data_get (a : Array α) (i : Fin _) : a[i] = a.data.get i := rfl
theorem getElem_fin_eq_toList_get (a : Array α) (i : Fin _) : a[i] = a.toList.get i := rfl
@[deprecated getElem_fin_eq_toList_get (since := "2024-09-09")]
abbrev getElem_fin_eq_data_get := @getElem_fin_eq_toList_get
@[simp] theorem ugetElem_eq_getElem (a : Array α) {i : USize} (h : i.toNat < a.size) :
a[i] = a[i.toNat] := rfl
theorem getElem?_eq_getElem (a : Array α) (i : Nat) (h : i < a.size) : a[i]? = a[i] :=
theorem getElem?_eq_getElem (a : Array α) (i : Nat) (h : i < a.size) : a[i]? = some a[i] :=
getElem?_pos ..
theorem get?_len_le (a : Array α) (i : Nat) (h : a.size i) : a[i]? = none := by
simp [getElem?_neg, h]
theorem getElem_mem_data (a : Array α) (h : i < a.size) : a[i] a.data := by
simp only [getElem_eq_data_get, List.get_mem]
theorem getElem_mem_toList (a : Array α) (h : i < a.size) : a[i] a.toList := by
simp only [getElem_eq_toList_getElem, List.getElem_mem]
theorem getElem?_eq_data_get? (a : Array α) (i : Nat) : a[i]? = a.data.get? i := by
@[deprecated getElem_mem_toList (since := "2024-09-09")]
abbrev getElem_mem_data := @getElem_mem_toList
theorem getElem?_eq_toList_get? (a : Array α) (i : Nat) : a[i]? = a.toList.get? i := by
by_cases i < a.size <;> simp_all [getElem?_pos, getElem?_neg, List.get?_eq_get, eq_comm]; rfl
theorem get?_eq_data_get? (a : Array α) (i : Nat) : a.get? i = a.data.get? i :=
getElem?_eq_data_get? ..
@[deprecated getElem?_eq_toList_get? (since := "2024-09-09")]
abbrev getElem?_eq_data_get? := @getElem?_eq_toList_get?
theorem get?_eq_toList_get? (a : Array α) (i : Nat) : a.get? i = a.toList.get? i :=
getElem?_eq_toList_get? ..
@[deprecated get?_eq_toList_get? (since := "2024-09-09")]
abbrev get?_eq_data_get? := @get?_eq_toList_get?
theorem get!_eq_get? [Inhabited α] (a : Array α) : a.get! n = (a.get? n).getD default := by
simp [get!_eq_getD]
@@ -362,7 +360,7 @@ theorem get!_eq_get? [Inhabited α] (a : Array α) : a.get! n = (a.get? n).getD
simp [back, back?]
@[simp] theorem back?_push (a : Array α) : (a.push x).back? = some x := by
simp [back?, getElem?_eq_data_get?]
simp [back?, getElem?_eq_toList_get?]
theorem back_push [Inhabited α] (a : Array α) : (a.push x).back = x := by simp
@@ -378,24 +376,27 @@ 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
@[simp] theorem toList_set (a : Array α) (i v) : (a.set i v).toList = a.toList.set i.1 v := rfl
@[deprecated toList_set (since := "2024-09-09")]
abbrev data_set := @toList_set
theorem get_set_eq (a : Array α) (i : Fin a.size) (v : α) :
(a.set i v)[i.1] = v := by
simp only [set, getElem_eq_data_get, List.get_set_eq]
simp only [set, getElem_eq_toList_getElem, List.getElem_set_self]
theorem get?_set_eq (a : Array α) (i : Fin a.size) (v : α) :
(a.set i v)[i.1]? = v := by simp [getElem?_pos, i.2]
@@ -414,7 +415,7 @@ theorem get_set (a : Array α) (i : Fin a.size) (j : Nat) (hj : j < a.size) (v :
@[simp] theorem get_set_ne (a : Array α) (i : Fin a.size) {j : Nat} (v : α) (hj : j < a.size)
(h : i.1 j) : (a.set i v)[j]'(by simp [*]) = a[j] := by
simp only [set, getElem_eq_data_get, List.get_set_ne _ h]
simp only [set, getElem_eq_toList_getElem, List.getElem_set_ne h]
theorem getElem_setD (a : Array α) (i : Nat) (v : α) (h : i < (setD a i v).size) :
(setD a i v)[i] = v := by
@@ -430,12 +431,15 @@ theorem swap_def (a : Array α) (i j : Fin a.size) :
a.swap i j = (a.set i (a.get j)).set j.1, by simp [j.2] (a.get i) := by
simp [swap, fin_cast_val]
theorem data_swap (a : Array α) (i j : Fin a.size) :
(a.swap i j).data = (a.data.set i (a.get j)).set j (a.get i) := by simp [swap_def]
theorem toList_swap (a : Array α) (i j : Fin a.size) :
(a.swap i j).toList = (a.toList.set i (a.get j)).set j (a.get i) := by simp [swap_def]
@[deprecated toList_swap (since := "2024-09-09")]
abbrev data_swap := @toList_swap
theorem get?_swap (a : Array α) (i j : Fin a.size) (k : Nat) : (a.swap i j)[k]? =
if j = k then some a[i.1] else if i = k then some a[j.1] else a[k]? := by
simp [swap_def, get?_set, getElem_fin_eq_data_get]
simp [swap_def, get?_set, getElem_fin_eq_toList_get]
@[simp] theorem swapAt_def (a : Array α) (i : Fin a.size) (v : α) :
a.swapAt i v = (a[i.1], a.set i v) := rfl
@@ -444,7 +448,10 @@ theorem get?_swap (a : Array α) (i j : Fin a.size) (k : Nat) : (a.swap i j)[k]?
theorem swapAt!_def (a : Array α) (i : Nat) (v : α) (h : i < a.size) :
a.swapAt! i v = (a[i], a.set i, h v) := by simp [swapAt!, h]
@[simp] theorem data_pop (a : Array α) : a.pop.data = a.data.dropLast := by simp [pop]
@[simp] theorem toList_pop (a : Array α) : a.pop.toList = a.toList.dropLast := by simp [pop]
@[deprecated toList_pop (since := "2024-09-09")]
abbrev data_pop := @toList_pop
@[simp] theorem pop_empty : (#[] : Array α).pop = #[] := rfl
@@ -452,7 +459,7 @@ theorem swapAt!_def (a : Array α) (i : Nat) (v : α) (h : i < a.size) :
@[simp] theorem getElem_pop (a : Array α) (i : Nat) (hi : i < a.pop.size) :
a.pop[i] = a[i]'(Nat.lt_of_lt_of_le (a.size_pop hi) (Nat.sub_le _ _)) :=
List.get_dropLast ..
List.getElem_dropLast ..
theorem eq_empty_of_size_eq_zero {as : Array α} (h : as.size = 0) : as = #[] := by
apply ext
@@ -476,7 +483,10 @@ theorem eq_push_of_size_ne_zero {as : Array α} (h : as.size ≠ 0) :
let _ : Inhabited α := as[0]
as.pop, as.back, eq_push_pop_back_of_size_ne_zero h
theorem size_eq_length_data (as : Array α) : as.size = as.data.length := rfl
theorem size_eq_length_toList (as : Array α) : as.size = as.toList.length := rfl
@[deprecated size_eq_length_toList (since := "2024-09-09")]
abbrev size_eq_length_data := @size_eq_length_toList
@[simp] theorem size_swap! (a : Array α) (i j) :
(a.swap! i j).size = a.size := by unfold swap!; split <;> (try split) <;> simp [size_swap]
@@ -500,27 +510,39 @@ theorem size_eq_length_data (as : Array α) : as.size = as.data.length := rfl
simp only [mkEmpty_eq, size_push] at *
omega
@[simp] theorem reverse_data (a : Array α) : a.reverse.data = a.data.reverse := by
@[simp] theorem toList_range (n : Nat) : (range n).toList = List.range n := by
induction n <;> simp_all [range, Nat.fold, flip, List.range_succ]
@[deprecated toList_range (since := "2024-09-09")]
abbrev data_range := @toList_range
@[simp]
theorem getElem_range {n : Nat} {x : Nat} (h : x < (Array.range n).size) : (Array.range n)[x] = x := by
simp [getElem_eq_toList_getElem]
set_option linter.deprecated false in
@[simp] theorem reverse_toList (a : Array α) : a.reverse.toList = a.toList.reverse := by
let rec go (as : Array α) (i j hj)
(h : i + j + 1 = a.size) (h₂ : as.size = a.size)
(H : k, as.data.get? k = if i k k j then a.data.get? k else a.data.reverse.get? k)
(k) : (reverse.loop as i j, hj).data.get? k = a.data.reverse.get? k := by
(H : k, as.toList.get? k = if i k k j then a.toList.get? k else a.toList.reverse.get? k)
(k) : (reverse.loop as i j, hj).toList.get? k = a.toList.reverse.get? k := by
rw [reverse.loop]; dsimp; split <;> rename_i h₁
· have := reverse.termination h₁
· have p := reverse.termination h₁
match j with | j+1 => ?_
simp at *
simp only [Nat.add_sub_cancel] at p
rw [(go · (i+1) j)]
· rwa [Nat.add_right_comm i]
· simp [size_swap, h₂]
· intro k
rw [ getElem?_eq_data_get?, get?_swap]
simp [getElem?_eq_data_get?, getElem_eq_data_get, List.get?_eq_get, H, Nat.le_of_lt h₁]
rw [ getElem?_eq_toList_get?, get?_swap]
simp only [H, getElem_eq_toList_get, List.get?_eq_get, Nat.le_of_lt h₁,
getElem?_eq_toList_get?]
split <;> rename_i h₂
· simp [ h₂, Nat.not_le.2 (Nat.lt_succ_self _)]
exact (List.get?_reverse' _ _ (Eq.trans (by simp_arith) h)).symm
· simp only [ h₂, Nat.not_le.2 (Nat.lt_succ_self _), Nat.le_refl, and_false]
exact (List.get?_reverse' (j+1) i (Eq.trans (by simp_arith) h)).symm
split <;> rename_i h₃
· simp [ h₃, Nat.not_le.2 (Nat.lt_succ_self _)]
exact (List.get?_reverse' _ _ (Eq.trans (by simp_arith) h)).symm
· simp only [ h₃, Nat.not_le.2 (Nat.lt_succ_self _), Nat.le_refl, false_and]
exact (List.get?_reverse' i (j+1) (Eq.trans (by simp_arith) h)).symm
simp only [Nat.succ_le, Nat.lt_iff_le_and_ne.trans (and_iff_left h₃),
Nat.lt_succ.symm.trans (Nat.lt_iff_le_and_ne.trans (and_iff_left (Ne.symm h₂)))]
· rw [H]; split <;> rename_i h₂
@@ -529,13 +551,17 @@ theorem size_eq_length_data (as : Array α) : as.size = as.data.length := rfl
exact (List.get?_reverse' _ _ h).symm
· rfl
termination_by j - i
simp only [reverse]; split
simp only [reverse]
split
· match a with | [] | [_] => rfl
· have := Nat.sub_add_cancel (Nat.le_of_not_le _)
refine List.ext <| go _ _ _ _ (by simp [this]) rfl fun k => ?_
split; {rfl}; rename_i h
simp [ show k < _ + 1 _ from Nat.lt_succ (n := a.size - 1), this] at h
rw [List.get?_eq_none.2 _, List.get?_eq_none.2 (a.data.length_reverse _)]
refine List.ext_get? <| go _ _ _ _ (by simp [this]) rfl fun k => ?_
split
· rfl
· rename_i h
simp only [ show k < _ + 1 _ from Nat.lt_succ (n := a.size - 1), this, Nat.zero_le,
true_and, Nat.not_lt] at h
rw [List.get?_eq_none.2 _, List.get?_eq_none.2 (a.toList.length_reverse _)]
/-! ### foldl / foldr -/
@@ -575,16 +601,19 @@ theorem foldr_induction
/-! ### map -/
@[simp] theorem mem_map {f : α β} {l : Array α} : b l.map f a, a l f a = b := by
simp only [mem_def, map_data, List.mem_map]
simp only [mem_def, map_toList, List.mem_map]
theorem mapM_eq_mapM_data [Monad m] [LawfulMonad m] (f : α m β) (arr : Array α) :
arr.mapM f = return mk ( arr.data.mapM f) := by
rw [mapM_eq_foldlM, foldlM_eq_foldlM_data, List.foldrM_reverse]
conv => rhs; rw [ List.reverse_reverse arr.data]
induction arr.data.reverse with
theorem mapM_eq_mapM_toList [Monad m] [LawfulMonad m] (f : α m β) (arr : Array α) :
arr.mapM f = return mk ( arr.toList.mapM f) := by
rw [mapM_eq_foldlM, foldlM_eq_foldlM_toList, List.foldrM_reverse]
conv => rhs; rw [ List.reverse_reverse arr.toList]
induction arr.toList.reverse with
| nil => simp; rfl
| cons a l ih => simp [ih]; simp [map_eq_pure_bind, push]
@[deprecated mapM_eq_mapM_toList (since := "2024-09-09")]
abbrev mapM_eq_mapM_data := @mapM_eq_mapM_toList
theorem mapM_map_eq_foldl (as : Array α) (f : α β) (i) :
mapM.map (m := Id) f as i b = as.foldl (start := i) (fun r a => r.push (f a)) b := by
unfold mapM.map
@@ -697,95 +726,119 @@ theorem mapIdx_spec (as : Array α) (f : Fin as.size → α → β)
unfold modify modifyM Id.run
split <;> simp
theorem getElem_modify {as : Array α} {x i} (h : i < as.size) :
(as.modify x f)[i]'(by simp [h]) = if x = i then f as[i] else as[i] := by
simp only [modify, modifyM, get_eq_getElem, Id.run, Id.pure_eq]
split
· simp only [Id.bind_eq, get_set _ _ _ h]; split <;> simp [*]
· rw [if_neg (mt (by rintro rfl; exact h) _)]
theorem getElem_modify_self {as : Array α} {i : Nat} (h : i < as.size) (f : α α) :
(as.modify i f)[i]'(by simp [h]) = f as[i] := by
simp [getElem_modify h]
theorem getElem_modify_of_ne {as : Array α} {i : Nat} (hj : j < as.size)
(f : α α) (h : i j) :
(as.modify i f)[j]'(by rwa [size_modify]) = as[j] := by
simp [getElem_modify hj, h]
@[deprecated getElem_modify (since := "2024-08-08")]
theorem get_modify {arr : Array α} {x i} (h : i < arr.size) :
(arr.modify x f).get i, by simp [h] =
if x = i then f (arr.get i, h) else arr.get i, h := by
simp [modify, modifyM, Id.run]; split
· simp [get_set _ _ _ h]; split <;> simp [*]
· rw [if_neg (mt (by rintro rfl; exact h) _)]
simp [getElem_modify h]
/-! ### filter -/
@[simp] theorem filter_data (p : α Bool) (l : Array α) :
(l.filter p).data = l.data.filter p := by
@[simp] theorem filter_toList (p : α Bool) (l : Array α) :
(l.filter p).toList = l.toList.filter p := by
dsimp only [filter]
rw [foldl_eq_foldl_data]
generalize l.data = l
suffices a, (List.foldl (fun r a => if p a = true then push r a else r) a l).data =
a.data ++ List.filter p l by
rw [foldl_eq_foldl_toList]
generalize l.toList = l
suffices a, (List.foldl (fun r a => if p a = true then push r a else r) a l).toList =
a.toList ++ List.filter p l by
simpa using this #[]
induction l with simp
| cons => split <;> simp [*]
@[deprecated filter_toList (since := "2024-09-09")]
abbrev filter_data := @filter_toList
@[simp] theorem filter_filter (q) (l : Array α) :
filter p (filter q l) = filter (fun a => p a q a) l := by
filter p (filter q l) = filter (fun a => p a && q a) l := by
apply ext'
simp only [filter_data, List.filter_filter]
simp only [filter_toList, List.filter_filter]
@[simp] theorem mem_filter : x filter p as x as p x := by
simp only [mem_def, filter_data, List.mem_filter]
simp only [mem_def, filter_toList, List.mem_filter]
theorem mem_of_mem_filter {a : α} {l} (h : a filter p l) : a l :=
(mem_filter.mp h).1
/-! ### filterMap -/
@[simp] theorem filterMap_data (f : α Option β) (l : Array α) :
(l.filterMap f).data = l.data.filterMap f := by
@[simp] theorem filterMap_toList (f : α Option β) (l : Array α) :
(l.filterMap f).toList = l.toList.filterMap f := by
dsimp only [filterMap, filterMapM]
rw [foldlM_eq_foldlM_data]
generalize l.data = l
have this : a : Array β, (Id.run (List.foldlM (m := Id) ?_ a l)).data =
a.data ++ List.filterMap f l := ?_
rw [foldlM_eq_foldlM_toList]
generalize l.toList = l
have this : a : Array β, (Id.run (List.foldlM (m := Id) ?_ a l)).toList =
a.toList ++ List.filterMap f l := ?_
exact this #[]
induction l
· simp_all [Id.run]
· simp_all [Id.run]
· simp_all [Id.run, List.filterMap_cons]
split <;> simp_all
@[simp] theorem mem_filterMap (f : α Option β) (l : Array α) {b : β} :
@[deprecated filterMap_toList (since := "2024-09-09")]
abbrev filterMap_data := @filterMap_toList
@[simp] theorem mem_filterMap {f : α Option β} {l : Array α} {b : β} :
b filterMap f l a, a l f a = some b := by
simp only [mem_def, filterMap_data, List.mem_filterMap]
simp only [mem_def, filterMap_toList, List.mem_filterMap]
/-! ### empty -/
theorem size_empty : (#[] : Array α).size = 0 := rfl
theorem empty_data : (#[] : Array α).data = [] := rfl
theorem toList_empty : (#[] : Array α).toList = [] := rfl
@[deprecated toList_empty (since := "2024-09-09")]
abbrev empty_data := @toList_empty
/-! ### append -/
theorem push_eq_append_singleton (as : Array α) (x) : as.push x = as ++ #[x] := rfl
@[simp] theorem mem_append {a : α} {s t : Array α} : a s ++ t a s a t := by
simp only [mem_def, append_data, List.mem_append]
simp only [mem_def, append_toList, List.mem_append]
theorem size_append (as bs : Array α) : (as ++ bs).size = as.size + bs.size := by
simp only [size, append_data, List.length_append]
simp only [size, append_toList, List.length_append]
theorem get_append_left {as bs : Array α} {h : i < (as ++ bs).size} (hlt : i < as.size) :
(as ++ bs)[i] = as[i] := by
simp only [getElem_eq_data_get]
have h' : i < (as.data ++ bs.data).length := by rwa [ data_length, append_data] at h
conv => rhs; rw [ List.get_append_left (bs:=bs.data) (h':=h')]
apply List.get_of_eq; rw [append_data]
simp only [getElem_eq_toList_getElem]
have h' : i < (as.toList ++ bs.toList).length := by rwa [ toList_length, append_toList] at h
conv => rhs; rw [ List.getElem_append_left (bs := bs.toList) (h' := h')]
apply List.get_of_eq; rw [append_toList]
theorem get_append_right {as bs : Array α} {h : i < (as ++ bs).size} (hle : as.size i)
(hlt : i - as.size < bs.size := Nat.sub_lt_left_of_lt_add hle (size_append .. h)) :
(as ++ bs)[i] = bs[i - as.size] := by
simp only [getElem_eq_data_get]
have h' : i < (as.data ++ bs.data).length := by rwa [ data_length, append_data] at h
conv => rhs; rw [ List.get_append_right (h':=h') (h:=Nat.not_lt_of_ge hle)]
apply List.get_of_eq; rw [append_data]
simp only [getElem_eq_toList_getElem]
have h' : i < (as.toList ++ bs.toList).length := by rwa [ toList_length, append_toList] at h
conv => rhs; rw [ List.getElem_append_right (h := hle) (h := h')]
apply List.get_of_eq; rw [append_toList]
@[simp] theorem append_nil (as : Array α) : as ++ #[] = as := by
apply ext'; simp only [append_data, empty_data, List.append_nil]
apply ext'; simp only [append_toList, toList_empty, List.append_nil]
@[simp] theorem nil_append (as : Array α) : #[] ++ as = as := by
apply ext'; simp only [append_data, empty_data, List.nil_append]
apply ext'; simp only [append_toList, toList_empty, List.nil_append]
theorem append_assoc (as bs cs : Array α) : as ++ bs ++ cs = as ++ (bs ++ cs) := by
apply ext'; simp only [append_data, List.append_assoc]
apply ext'; simp only [append_toList, List.append_assoc]
/-! ### extract -/
@@ -922,7 +975,7 @@ theorem extract_empty_of_size_le_start (as : Array α) {start stop : Nat} (h : a
/-! ### any -/
-- Auxiliary for `any_iff_exists`.
theorem anyM_loop_iff_exists (p : α Bool) (as : Array α) (start stop) (h : stop as.size) :
theorem anyM_loop_iff_exists {p : α Bool} {as : Array α} {start stop} (h : stop as.size) :
anyM.loop (m := Id) p as stop h start = true
i : Fin as.size, start i i < stop p as[i] = true := by
unfold anyM.loop
@@ -944,7 +997,7 @@ theorem anyM_loop_iff_exists (p : α → Bool) (as : Array α) (start stop) (h :
termination_by stop - start
-- This could also be proved from `SatisfiesM_anyM_iff_exists` in `Batteries.Data.Array.Init.Monadic`
theorem any_iff_exists (p : α Bool) (as : Array α) (start stop) :
theorem any_iff_exists {p : α Bool} {as : Array α} {start stop} :
any as p start stop i : Fin as.size, start i.1 i.1 < stop p as[i] := by
dsimp [any, anyM, Id.run]
split
@@ -956,10 +1009,10 @@ theorem any_iff_exists (p : α → Bool) (as : Array α) (start stop) :
· rintro i, ge, _, h
exact i, by omega, by omega, h
theorem any_eq_true (p : α Bool) (as : Array α) :
theorem any_eq_true {p : α Bool} {as : Array α} :
any as p i : Fin as.size, p as[i] := by simp [any_iff_exists, Fin.isLt]
theorem any_def {p : α Bool} (as : Array α) : as.any p = as.data.any p := by
theorem any_def {p : α Bool} (as : Array α) : as.any p = as.toList.any p := by
rw [Bool.eq_iff_iff, any_eq_true, List.any_eq_true]; simp only [List.mem_iff_get]
exact fun i, h => _, i, rfl, h, fun _, i, rfl, h => i, h
@@ -970,7 +1023,7 @@ theorem all_eq_not_any_not (p : α → Bool) (as : Array α) (start stop) :
dsimp [all, allM]
rfl
theorem all_iff_forall (p : α Bool) (as : Array α) (start stop) :
theorem all_iff_forall {p : α Bool} {as : Array α} {start stop} :
all as p start stop i : Fin as.size, start i.1 i.1 < stop p as[i] := by
rw [all_eq_not_any_not]
suffices ¬(any as (!p ·) start stop = true)
@@ -979,17 +1032,17 @@ theorem all_iff_forall (p : α → Bool) (as : Array α) (start stop) :
rw [any_iff_exists]
simp
theorem all_eq_true (p : α Bool) (as : Array α) : all as p i : Fin as.size, p as[i] := by
theorem all_eq_true {p : α Bool} {as : Array α} : all as p i : Fin as.size, p as[i] := by
simp [all_iff_forall, Fin.isLt]
theorem all_def {p : α Bool} (as : Array α) : as.all p = as.data.all p := by
rw [Bool.eq_iff_iff, all_eq_true, List.all_eq_true]; simp only [List.mem_iff_get]
theorem all_def {p : α Bool} (as : Array α) : as.all p = as.toList.all p := by
rw [Bool.eq_iff_iff, all_eq_true, List.all_eq_true]; simp only [List.mem_iff_getElem]
constructor
· rintro w x r, rfl
rw [ getElem_eq_data_get]
apply w
· rintro w x r, h, rfl
rw [ getElem_eq_toList_getElem]
exact w r, h
· intro w i
exact w as[i] i, (getElem_eq_data_get as i.2).symm
exact w as[i] i, i.2, (getElem_eq_toList_getElem as i.2).symm
theorem all_eq_true_iff_forall_mem {l : Array α} : l.all p x, x l p x := by
simp only [all_def, List.all_eq_true, mem_def]

View File

@@ -13,17 +13,17 @@ namespace Array
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
-- NB: This is defined as a structure rather than a plain def so that a lemma
-- like `sizeOf_lt_of_mem` will not apply with no actual arrays around.
structure Mem (a : α) (as : Array α) : Prop where
val : a as.data
structure Mem (as : Array α) (a : α) : Prop where
val : a as.toList
instance : Membership α (Array α) where
mem a as := Mem a as
mem := Mem
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
cases as with | _ as =>
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)
@@ -38,8 +38,8 @@ macro "array_get_dec" : tactic =>
-- subsumed by simp
-- | with_reducible apply sizeOf_get
-- | with_reducible apply sizeOf_getElem
| (with_reducible apply Nat.lt_trans (sizeOf_get ..)); simp_arith
| (with_reducible apply Nat.lt_trans (sizeOf_getElem ..)); simp_arith
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_get ..)); simp_arith
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_getElem ..)); simp_arith
)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec)
@@ -52,7 +52,7 @@ macro "array_mem_dec" : tactic =>
`(tactic| first
| with_reducible apply Array.sizeOf_lt_of_mem; assumption; done
| with_reducible
apply Nat.lt_trans (Array.sizeOf_lt_of_mem ?h)
apply Nat.lt_of_lt_of_le (Array.sizeOf_lt_of_mem ?h)
case' h => assumption
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.toList = l₁ ++ self[i] :: l₂ List.length l₁ = i.toNat
(self.uset i d h).toList = l₁ ++ d :: l₂ := by
simpa [Array.getElem_eq_toList_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

@@ -20,6 +20,8 @@ We define many of the bitvector operations from the
of SMT-LIBv2.
-/
set_option linter.missingDocs true
/--
A bitvector of the specified width.
@@ -34,14 +36,14 @@ structure BitVec (w : Nat) where
O(1), because we use `Fin` as the internal representation of a bitvector. -/
toFin : Fin (2^w)
@[deprecated (since := "2024-04-12")]
protected abbrev Std.BitVec := _root_.BitVec
/--
Bitvectors have decidable equality. This should be used via the instance `DecidableEq (BitVec n)`.
-/
-- We manually derive the `DecidableEq` instances for `BitVec` because
-- we want to have builtin support for bit-vector literals, and we
-- need a name for this function to implement `canUnfoldAtMatcher` at `WHNF.lean`.
def BitVec.decEq (a b : BitVec n) : Decidable (a = b) :=
match a, b with
def BitVec.decEq (x y : BitVec n) : Decidable (x = y) :=
match x, y with
| n, m =>
if h : n = m then
isTrue (h rfl)
@@ -62,14 +64,14 @@ protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2^n) : BitVec n where
/-- The `BitVec` with value `i mod 2^n`. -/
@[match_pattern]
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat' i (Nat.two_pow_pos n)
toFin := Fin.ofNat' (2^n) i
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
instance natCastInst : NatCast (BitVec w) := BitVec.ofNat w
/-- Given a bitvector `a`, return the underlying `Nat`. This is O(1) because `BitVec` is a
/-- Given a bitvector `x`, return the underlying `Nat`. This is O(1) because `BitVec` is a
(zero-cost) wrapper around a `Nat`. -/
protected def toNat (a : BitVec n) : Nat := a.toFin.val
protected def toNat (x : BitVec n) : Nat := x.toFin.val
/-- Return the bound in terms of toNat. -/
theorem isLt (x : BitVec w) : x.toNat < 2^w := x.toFin.isLt
@@ -114,25 +116,73 @@ end zero_allOnes
section getXsb
/--
Return the `i`-th least significant bit.
This will be renamed `getLsb` after the existing deprecated alias is removed.
-/
@[inline] def getLsb' (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
/-- Return the `i`-th least significant bit or `none` if `i ≥ w`. -/
@[inline] def getLsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getLsb' x i, h) else none
/--
Return the `i`-th most significant bit.
This will be renamed `getMsb` after the existing deprecated alias is removed.
-/
@[inline] def getMsb' (x : BitVec w) (i : Fin w) : Bool := x.getLsb' w-1-i, by omega
/-- Return the `i`-th most significant bit or `none` if `i ≥ w`. -/
@[inline] def getMsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getMsb' x i, h) else none
/-- Return the `i`-th least significant bit or `false` if `i ≥ w`. -/
@[inline] def getLsb (x : BitVec w) (i : Nat) : Bool := x.toNat.testBit i
@[inline] def getLsbD (x : BitVec w) (i : Nat) : Bool :=
x.toNat.testBit i
@[deprecated getLsbD (since := "2024-08-29"), inherit_doc getLsbD]
def getLsb (x : BitVec w) (i : Nat) : Bool := x.getLsbD i
/-- Return the `i`-th most significant bit or `false` if `i ≥ w`. -/
@[inline] def getMsb (x : BitVec w) (i : Nat) : Bool := i < w && getLsb x (w-1-i)
@[inline] def getMsbD (x : BitVec w) (i : Nat) : Bool :=
i < w && x.getLsbD (w-1-i)
@[deprecated getMsbD (since := "2024-08-29"), inherit_doc getMsbD]
def getMsb (x : BitVec w) (i : Nat) : Bool := x.getMsbD i
/-- Return most-significant bit in bitvector. -/
@[inline] protected def msb (a : BitVec n) : Bool := getMsb a 0
@[inline] protected def msb (x : BitVec n) : Bool := getMsbD x 0
end getXsb
section getElem
instance : GetElem (BitVec w) Nat Bool fun _ i => i < w where
getElem xs i h := xs.getLsb' i, h
/-- We prefer `x[i]` as the simp normal form for `getLsb'` -/
@[simp] theorem getLsb'_eq_getElem (x : BitVec w) (i : Fin w) :
x.getLsb' i = x[i] := rfl
/-- We prefer `x[i]?` as the simp normal form for `getLsb?` -/
@[simp] theorem getLsb?_eq_getElem? (x : BitVec w) (i : Nat) :
x.getLsb? i = x[i]? := rfl
theorem getElem_eq_testBit_toNat (x : BitVec w) (i : Nat) (h : i < w) :
x[i] = x.toNat.testBit i := rfl
end getElem
section Int
/-- Interpret the bitvector as an integer stored in two's complement form. -/
protected def toInt (a : BitVec n) : Int :=
if 2 * a.toNat < 2^n then
a.toNat
protected def toInt (x : BitVec n) : Int :=
if 2 * x.toNat < 2^n then
x.toNat
else
(a.toNat : Int) - (2^n : Nat)
(x.toNat : Int) - (2^n : Nat)
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
@@ -198,7 +248,7 @@ instance : Add (BitVec n) := ⟨BitVec.add⟩
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
modulo `2^n`.
-/
protected def sub (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + (2^n - y.toNat))
protected def sub (x y : BitVec n) : BitVec n := .ofNat n ((2^n - y.toNat) + x.toNat)
instance : Sub (BitVec n) := BitVec.sub
/--
@@ -213,7 +263,7 @@ instance : Neg (BitVec n) := ⟨.neg⟩
/--
Return the absolute value of a signed bitvector.
-/
protected def abs (s : BitVec n) : BitVec n := if s.msb then .neg s else s
protected def abs (x : BitVec n) : BitVec n := if x.msb then .neg x else x
/--
Multiplication for bit vectors. This can be interpreted as either signed or unsigned negation
@@ -260,12 +310,12 @@ sdiv 5#4 -2 = -2#4
sdiv (-7#4) (-2) = 3#4
```
-/
def sdiv (s t : BitVec n) : BitVec n :=
match s.msb, t.msb with
| false, false => udiv s t
| false, true => .neg (udiv s (.neg t))
| true, false => .neg (udiv (.neg s) t)
| true, true => udiv (.neg s) (.neg t)
def sdiv (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with
| false, false => udiv x y
| false, true => .neg (udiv x (.neg y))
| true, false => .neg (udiv (.neg x) y)
| true, true => udiv (.neg x) (.neg y)
/--
Signed division for bit vectors using SMTLIB rules for division by zero.
@@ -274,40 +324,40 @@ Specifically, `smtSDiv x 0 = if x >= 0 then -1 else 1`
SMT-Lib name: `bvsdiv`.
-/
def smtSDiv (s t : BitVec n) : BitVec n :=
match s.msb, t.msb with
| false, false => smtUDiv s t
| false, true => .neg (smtUDiv s (.neg t))
| true, false => .neg (smtUDiv (.neg s) t)
| true, true => smtUDiv (.neg s) (.neg t)
def smtSDiv (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with
| false, false => smtUDiv x y
| false, true => .neg (smtUDiv x (.neg y))
| true, false => .neg (smtUDiv (.neg x) y)
| true, true => smtUDiv (.neg x) (.neg y)
/--
Remainder for signed division rounding to zero.
SMT_Lib name: `bvsrem`.
-/
def srem (s t : BitVec n) : BitVec n :=
match s.msb, t.msb with
| false, false => umod s t
| false, true => umod s (.neg t)
| true, false => .neg (umod (.neg s) t)
| true, true => .neg (umod (.neg s) (.neg t))
def srem (x y : BitVec n) : BitVec n :=
match x.msb, y.msb with
| false, false => umod x y
| false, true => umod x (.neg y)
| true, false => .neg (umod (.neg x) y)
| true, true => .neg (umod (.neg x) (.neg y))
/--
Remainder for signed division rounded to negative infinity.
SMT_Lib name: `bvsmod`.
-/
def smod (s t : BitVec m) : BitVec m :=
match s.msb, t.msb with
| false, false => umod s t
def smod (x y : BitVec m) : BitVec m :=
match x.msb, y.msb with
| false, false => umod x y
| false, true =>
let u := umod s (.neg t)
(if u = .zero m then u else .add u t)
let u := umod x (.neg y)
(if u = .zero m then u else .add u y)
| true, false =>
let u := umod (.neg s) t
(if u = .zero m then u else .sub t u)
| true, true => .neg (umod (.neg s) (.neg t))
let u := umod (.neg x) y
(if u = .zero m then u else .sub y u)
| true, true => .neg (umod (.neg x) (.neg y))
end arithmetic
@@ -371,8 +421,8 @@ end relations
section cast
/-- `cast eq i` embeds `i` into an equal `BitVec` type. -/
@[inline] def cast (eq : n = m) (i : BitVec n) : BitVec m := .ofNatLt i.toNat (eq i.isLt)
/-- `cast eq x` embeds `x` into an equal `BitVec` type. -/
@[inline] def cast (eq : n = m) (x : BitVec n) : BitVec m := .ofNatLt x.toNat (eq x.isLt)
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
cast h (BitVec.ofNat n x) = BitVec.ofNat m x := by
@@ -389,7 +439,7 @@ Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to
new bitvector of size `len`. If `start + len > n`, then the vector will be zero-padded in the
high bits.
-/
def extractLsb' (start len : Nat) (a : BitVec n) : BitVec len := .ofNat _ (a.toNat >>> start)
def extractLsb' (start len : Nat) (x : BitVec n) : BitVec len := .ofNat _ (x.toNat >>> start)
/--
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
@@ -397,12 +447,12 @@ yield a new bitvector of size `hi - lo + 1`.
SMT-Lib name: `extract`.
-/
def extractLsb (hi lo : Nat) (a : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ a
def extractLsb (hi lo : Nat) (x : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ x
/--
A version of `zeroExtend` that requires a proof, but is a noop.
-/
def zeroExtend' {n w : Nat} (le : n w) (x : BitVec n) : BitVec w :=
def zeroExtend' {n w : Nat} (le : n w) (x : BitVec n) : BitVec w :=
x.toNat#'(by
apply Nat.lt_of_lt_of_le x.isLt
exact Nat.pow_le_pow_of_le_right (by trivial) le)
@@ -411,8 +461,8 @@ def zeroExtend' {n w : Nat} (le : n ≤ w) (x : BitVec n) : BitVec w :=
`shiftLeftZeroExtend x n` returns `zeroExtend (w+n) x <<< n` without
needing to compute `x % 2^(2+n)`.
-/
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w+m) :=
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w+m) := by
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w + m) :=
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w + m) := by
simp [Nat.shiftLeft_eq, Nat.pow_add]
apply Nat.mul_lt_mul_of_pos_right p
exact (Nat.two_pow_pos m)
@@ -500,24 +550,24 @@ instance : Complement (BitVec w) := ⟨.not⟩
/--
Left shift for bit vectors. The low bits are filled with zeros. As a numeric operation, this is
equivalent to `a * 2^s`, modulo `2^n`.
equivalent to `x * 2^s`, modulo `2^n`.
SMT-Lib name: `bvshl` except this operator uses a `Nat` shift value.
-/
protected def shiftLeft (a : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (a.toNat <<< s)
protected def shiftLeft (x : BitVec n) (s : Nat) : BitVec n := BitVec.ofNat n (x.toNat <<< s)
instance : HShiftLeft (BitVec w) Nat (BitVec w) := .shiftLeft
/--
(Logical) right shift for bit vectors. The high bits are filled with zeros.
As a numeric operation, this is equivalent to `a / 2^s`, rounding down.
As a numeric operation, this is equivalent to `x / 2^s`, rounding down.
SMT-Lib name: `bvlshr` except this operator uses a `Nat` shift value.
-/
def ushiftRight (a : BitVec n) (s : Nat) : BitVec n :=
(a.toNat >>> s)#'(by
let a, lt := a
def ushiftRight (x : BitVec n) (s : Nat) : BitVec n :=
(x.toNat >>> s)#'(by
let x, lt := x
simp only [BitVec.toNat, Nat.shiftRight_eq_div_pow, Nat.div_lt_iff_lt_mul (Nat.two_pow_pos s)]
rw [Nat.mul_one a]
rw [Nat.mul_one x]
exact Nat.mul_lt_mul_of_lt_of_le' lt (Nat.two_pow_pos s) (Nat.le_refl 1))
instance : HShiftRight (BitVec w) Nat (BitVec w) := .ushiftRight
@@ -525,15 +575,24 @@ instance : HShiftRight (BitVec w) Nat (BitVec w) := ⟨.ushiftRight⟩
/--
Arithmetic right shift for bit vectors. The high bits are filled with the
most-significant bit.
As a numeric operation, this is equivalent to `a.toInt >>> s`.
As a numeric operation, this is equivalent to `x.toInt >>> s`.
SMT-Lib name: `bvashr` except this operator uses a `Nat` shift value.
-/
def sshiftRight (a : BitVec n) (s : Nat) : BitVec n := .ofInt n (a.toInt >>> s)
def sshiftRight (x : BitVec n) (s : Nat) : BitVec n := .ofInt n (x.toInt >>> s)
instance {n} : HShiftLeft (BitVec m) (BitVec n) (BitVec m) := fun x y => x <<< y.toNat
instance {n} : HShiftRight (BitVec m) (BitVec n) (BitVec m) := fun x y => x >>> y.toNat
/--
Arithmetic right shift for bit vectors. The high bits are filled with the
most-significant bit.
As a numeric operation, this is equivalent to `a.toInt >>> s.toNat`.
SMT-Lib name: `bvashr`.
-/
def sshiftRight' (a : BitVec n) (s : BitVec m) : BitVec n := a.sshiftRight s.toNat
/-- Auxiliary function for `rotateLeft`, which does not take into account the case where
the rotation amount is greater than the bitvector width. -/
def rotateLeftAux (x : BitVec w) (n : Nat) : BitVec w :=
@@ -583,11 +642,9 @@ instance : HAppend (BitVec w) (BitVec v) (BitVec (w + v)) := ⟨.append⟩
-- TODO: write this using multiplication
/-- `replicate i x` concatenates `i` copies of `x` into a new vector of length `w*i`. -/
def replicate : (i : Nat) BitVec w BitVec (w*i)
| 0, _ => 0
| 0, _ => 0#0
| n+1, x =>
have hEq : w + w*n = w*(n + 1) := by
rw [Nat.mul_add, Nat.add_comm, Nat.mul_one]
hEq (x ++ replicate n x)
(x ++ replicate n x).cast (by rw [Nat.mul_succ]; omega)
/-!
### Cons and Concat
@@ -614,6 +671,13 @@ theorem ofBool_append (msb : Bool) (lsbs : BitVec w) :
ofBool msb ++ lsbs = (cons msb lsbs).cast (Nat.add_comm ..) :=
rfl
/--
`twoPow w i` is the bitvector `2^i` if `i < w`, and `0` otherwise.
That is, 2 to the power `i`.
For the bitwise point of view, it has the `i`th bit as `1` and all other bits as `0`.
-/
def twoPow (w : Nat) (i : Nat) : BitVec w := 1#w <<< i
end bitwise
section normalization_eqs

View File

@@ -28,6 +28,8 @@ https://github.com/mhk119/lean-smt/blob/bitvec/Smt/Data/Bitwise.lean.
-/
set_option linter.missingDocs true
open Nat Bool
namespace Bool
@@ -90,27 +92,58 @@ def carry (i : Nat) (x y : BitVec w) (c : Bool) : Bool :=
cases c <;> simp [carry, mod_one]
theorem carry_succ (i : Nat) (x y : BitVec w) (c : Bool) :
carry (i+1) x y c = atLeastTwo (x.getLsb i) (y.getLsb i) (carry i x y c) := by
simp only [carry, mod_two_pow_succ, atLeastTwo, getLsb]
carry (i+1) x y c = atLeastTwo (x.getLsbD i) (y.getLsbD i) (carry i x y c) := by
simp only [carry, mod_two_pow_succ, atLeastTwo, getLsbD]
simp only [Nat.pow_succ']
have sum_bnd : x.toNat%2^i + (y.toNat%2^i + c.toNat) < 2*2^i := by
simp only [ Nat.pow_succ']
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 (·.getLsbD 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))
/-- Bitwise addition implemented via a ripple carry adder. -/
def adc (x y : BitVec w) : Bool Bool × BitVec w :=
iunfoldr fun (i : Fin w) c => adcb (x.getLsb i) (y.getLsb i) c
iunfoldr fun (i : Fin w) c => adcb (x.getLsbD i) (y.getLsbD i) c
theorem getLsb_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool) :
getLsb (x + y + zeroExtend w (ofBool c)) i =
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y c)) := by
theorem getLsbD_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool) :
getLsbD (x + y + zeroExtend w (ofBool c)) i =
Bool.xor (getLsbD x i) (Bool.xor (getLsbD y i) (carry i x y c)) := by
let x, x_lt := x
let y, y_lt := y
simp only [getLsb, toNat_add, toNat_zeroExtend, i_lt, toNat_ofFin, toNat_ofBool,
simp only [getLsbD, toNat_add, toNat_zeroExtend, i_lt, toNat_ofFin, toNat_ofBool,
Nat.mod_add_mod, Nat.add_mod_mod]
apply Eq.trans
rw [ Nat.div_add_mod x (2^i), Nat.div_add_mod y (2^i)]
@@ -126,10 +159,10 @@ theorem getLsb_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool)
]
simp [testBit_to_div_mod, carry, Nat.add_assoc]
theorem getLsb_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
getLsb (x + y) i =
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y false)) := by
simpa using getLsb_add_add_bool i_lt x y false
theorem getLsbD_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
getLsbD (x + y) i =
Bool.xor (getLsbD x i) (Bool.xor (getLsbD y i) (carry i x y false)) := by
simpa using getLsbD_add_add_bool i_lt x y false
theorem adc_spec (x y : BitVec w) (c : Bool) :
adc x y c = (carry w x y c, x + y + zeroExtend w (ofBool c)) := by
@@ -142,7 +175,7 @@ theorem adc_spec (x y : BitVec w) (c : Bool) :
simp [carry, Nat.mod_one]
cases c <;> rfl
case step =>
simp [adcb, Prod.mk.injEq, carry_succ, getLsb_add_add_bool]
simp [adcb, Prod.mk.injEq, carry_succ, getLsbD_add_add_bool]
theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := by
simp [adc_spec]
@@ -159,27 +192,42 @@ 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, getLsbD_or,
Prod.mk.injEq, and_eq_false_imp]
intros i
replace h : (x &&& y).getLsbD i = (0#w).getLsbD i := by rw [h]
simp only [getLsbD_and, getLsbD_zero, and_eq_false_imp] at h
constructor
· intros hx
simp_all [hx]
· by_cases hx : x.getLsbD i <;> simp_all [hx]
/-! ### Negation -/
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
getLsb (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd) i.val = !(getLsb x i.val) := by
apply iunfoldr_getLsb (fun _ => ()) i (by simp)
getLsbD (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) i.val = !(getLsbD x i.val) := by
apply iunfoldr_getLsbD (fun _ => ()) i (by simp)
theorem bit_not_add_self (x : BitVec w) :
((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd + x = -1 := by
((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd + x = -1 := by
simp only [add_eq_adc]
apply iunfoldr_replace_snd (fun _ => false) (-1) false rfl
intro i; simp only [ BitVec.not, adcb, testBit_toNat]
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x.getLsb i)))) ()).snd)]
<;> simp [bit_not_testBit, negOne_eq_allOnes, getLsb_allOnes]
rw [iunfoldr_replace_snd (fun _ => ()) (((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd)]
<;> simp [bit_not_testBit, negOne_eq_allOnes, getLsbD_allOnes]
theorem bit_not_eq_not (x : BitVec w) :
((iunfoldr (fun i c => (c, !(x.getLsb i)))) ()).snd = ~~~ x := by
((iunfoldr (fun i c => (c, !(x.getLsbD i)))) ()).snd = ~~~ x := by
simp [allOnes_sub_eq_not, BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), negOne_eq_allOnes]
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
theorem bit_neg_eq_neg (x : BitVec w) : -x = (adc (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) (BitVec.ofNat w 1) false).snd:= by
simp only [ add_eq_adc]
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsb i)))) ()).snd) _ rfl]
rw [iunfoldr_replace_snd ((fun _ => ())) (((iunfoldr (fun (i : Fin w) c => (c, !(x.getLsbD i)))) ()).snd) _ rfl]
· rw [BitVec.eq_sub_iff_add_eq.mpr (bit_not_add_self x), sub_toAdd, BitVec.add_comm _ (-x)]
simp [ sub_toAdd, BitVec.sub_add_cancel]
· simp [bit_not_testBit x _]
@@ -235,4 +283,275 @@ 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 (x y : BitVec w) (s : Nat) : BitVec w :=
let cur := if y.getLsbD s then (x <<< s) else 0
match s with
| 0 => cur
| s + 1 => mulRec x y s + cur
theorem mulRec_zero_eq (x y : BitVec w) :
mulRec x y 0 = if y.getLsbD 0 then x else 0 := by
simp [mulRec]
theorem mulRec_succ_eq (x y : BitVec w) (s : Nat) :
mulRec x y (s + 1) = mulRec x y s + if y.getLsbD (s + 1) then (x <<< (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 [getLsbD_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsbD_or, getLsbD_and]
by_cases hik : i = k
· subst hik
simp
· simp only [getLsbD_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 only [and_twoPow, getLsbD_and, getLsbD_zeroExtend, Fin.is_lt, decide_True, Bool.true_and,
getLsbD_zero, and_eq_false_imp, and_eq_true, decide_eq_true_eq, and_imp]
by_cases hi : x.getLsbD i <;> simp [hi] <;> omega
/--
Recurrence lemma: multiplying `x` with the first `s` bits of `y` is the
same as truncating `y` to `s` bits, then zero extending to the original length,
and performing the multplication. -/
theorem mulRec_eq_mul_signExtend_truncate (x y : BitVec w) (s : Nat) :
mulRec x y s = x * ((y.truncate (s + 1)).zeroExtend w) := by
induction s
case zero =>
simp only [mulRec_zero_eq, ofNat_eq_ofNat, Nat.reduceAdd]
by_cases y.getLsbD 0
case pos hy =>
simp only [hy, reduceIte, truncate, zeroExtend_one_eq_ofBool_getLsb_zero,
ofBool_true, ofNat_eq_ofNat]
rw [zeroExtend_ofNat_one_eq_ofNat_one_of_lt (by omega)]
simp
case neg hy =>
simp [hy, zeroExtend_one_eq_ofBool_getLsb_zero]
case succ s' hs =>
rw [mulRec_succ_eq, hs]
have heq :
(if y.getLsbD (s' + 1) = true then x <<< (s' + 1) else 0) =
(x * (y &&& (BitVec.twoPow w (s' + 1)))) := by
simp only [ofNat_eq_ofNat, and_twoPow]
by_cases hy : y.getLsbD (s' + 1) <;> simp [hy]
rw [heq, BitVec.mul_add, zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow]
theorem getLsbD_mul (x y : BitVec w) (i : Nat) :
(x * y).getLsbD i = (mulRec x y w).getLsbD 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,
and_one_eq_zeroExtend_ofBool_getLsbD]
case succ n ih =>
simp only [shiftLeftRec_succ, and_twoPow]
rw [ih]
by_cases h : y.getLsbD (n + 1)
· simp only [h, reduceIte]
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsbD_true h,
shiftLeft_or_of_and_eq_zero]
simp [and_twoPow]
· simp only [h, false_eq_true, reduceIte, shiftLeft_zero']
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsbD_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]
/- ### Arithmetic shift right (sshiftRight) recurrence -/
/--
`sshiftRightRec x y n` shifts `x` arithmetically/signed to the right by the first `n` bits of `y`.
The theorem `sshiftRight_eq_sshiftRightRec` proves the equivalence of `(x.sshiftRight y)` and `sshiftRightRec`.
Together with equations `sshiftRightRec_zero`, `sshiftRightRec_succ`,
this allows us to unfold `sshiftRight` into a circuit for bitblasting.
-/
def sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
match n with
| 0 => x.sshiftRight' shiftAmt
| n + 1 => (sshiftRightRec x y n).sshiftRight' shiftAmt
@[simp]
theorem sshiftRightRec_zero_eq (x : BitVec w₁) (y : BitVec w₂) :
sshiftRightRec x y 0 = x.sshiftRight' (y &&& 1#w₂) := by
simp only [sshiftRightRec, twoPow_zero]
@[simp]
theorem sshiftRightRec_succ_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
sshiftRightRec x y (n + 1) = (sshiftRightRec x y n).sshiftRight' (y &&& twoPow w₂ (n + 1)) := by
simp [sshiftRightRec]
/--
If `y &&& z = 0`, `x.sshiftRight (y ||| z) = (x.sshiftRight y).sshiftRight z`.
This follows as `y &&& z = 0` implies `y ||| z = y + z`,
and thus `x.sshiftRight (y ||| z) = x.sshiftRight (y + z) = (x.sshiftRight y).sshiftRight z`.
-/
theorem sshiftRight'_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
(h : y &&& z = 0#w₂) :
x.sshiftRight' (y ||| z) = (x.sshiftRight' y).sshiftRight' z := by
simp [sshiftRight', add_eq_or_of_and_eq_zero _ _ h,
toNat_add_of_and_eq_zero h, sshiftRight_add]
theorem sshiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
sshiftRightRec x y n = x.sshiftRight' ((y.truncate (n + 1)).zeroExtend w₂) := by
induction n generalizing x y
case zero =>
ext i
simp [twoPow_zero, Nat.reduceAdd, and_one_eq_zeroExtend_ofBool_getLsbD, truncate_one]
case succ n ih =>
simp only [sshiftRightRec_succ_eq, and_twoPow, ih]
by_cases h : y.getLsbD (n + 1)
· rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsbD_true h,
sshiftRight'_or_of_and_eq_zero (by simp [and_twoPow]), h]
simp
· rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsbD_false (i := n + 1)
(by simp [h])]
simp [h]
/--
Show that `x.sshiftRight y` can be written in terms of `sshiftRightRec`.
This can be unfolded in terms of `sshiftRightRec_zero_eq`, `sshiftRightRec_succ_eq` for bitblasting.
-/
theorem sshiftRight_eq_sshiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
(x.sshiftRight' y).getLsbD i = (sshiftRightRec x y (w₂ - 1)).getLsbD i := by
rcases w₂ with rfl | w₂
· simp [of_length_zero]
· simp [sshiftRightRec_eq]
/- ### Logical shift right (ushiftRight) recurrence for bitblasting -/
/--
`ushiftRightRec x y n` shifts `x` logically to the right by the first `n` bits of `y`.
The theorem `shiftRight_eq_ushiftRightRec` proves the equivalence
of `(x >>> y)` and `ushiftRightRec`.
Together with equations `ushiftRightRec_zero`, `ushiftRightRec_succ`,
this allows us to unfold `ushiftRight` into a circuit for bitblasting.
-/
def ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
let shiftAmt := (y &&& (twoPow w₂ n))
match n with
| 0 => x >>> shiftAmt
| n + 1 => (ushiftRightRec x y n) >>> shiftAmt
@[simp]
theorem ushiftRightRec_zero (x : BitVec w₁) (y : BitVec w₂) :
ushiftRightRec x y 0 = x >>> (y &&& twoPow w₂ 0) := by
simp [ushiftRightRec]
@[simp]
theorem ushiftRightRec_succ (x : BitVec w₁) (y : BitVec w₂) :
ushiftRightRec x y (n + 1) = (ushiftRightRec x y n) >>> (y &&& twoPow w₂ (n + 1)) := by
simp [ushiftRightRec]
/--
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 ushiftRight'_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
(h : y &&& z = 0#w₂) :
x >>> (y ||| z) = x >>> y >>> z := by
simp [ add_eq_or_of_and_eq_zero _ _ h, toNat_add_of_and_eq_zero h, shiftRight_add]
theorem ushiftRightRec_eq (x : BitVec w₁) (y : BitVec w₂) (n : Nat) :
ushiftRightRec x y n = x >>> (y.truncate (n + 1)).zeroExtend w₂ := by
induction n generalizing x y
case zero =>
ext i
simp only [ushiftRightRec_zero, twoPow_zero, Nat.reduceAdd,
and_one_eq_zeroExtend_ofBool_getLsbD, truncate_one]
case succ n ih =>
simp only [ushiftRightRec_succ, and_twoPow]
rw [ih]
by_cases h : y.getLsbD (n + 1) <;> simp only [h, reduceIte]
· rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsbD_true h,
ushiftRight'_or_of_and_eq_zero]
simp [and_twoPow]
· simp [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsbD_false, h]
/--
Show that `x >>> y` can be written in terms of `ushiftRightRec`.
This can be unfolded in terms of `ushiftRightRec_zero`, `ushiftRightRec_succ` for bitblasting.
-/
theorem shiftRight_eq_ushiftRightRec (x : BitVec w₁) (y : BitVec w₂) :
x >>> y = ushiftRightRec x y (w₂ - 1) := by
rcases w₂ with rfl | w₂
· simp [of_length_zero]
· simp [ushiftRightRec_eq]
end BitVec

View File

@@ -8,6 +8,8 @@ import Init.Data.BitVec.Lemmas
import Init.Data.Nat.Lemmas
import Init.Data.Fin.Iterate
set_option linter.missingDocs true
namespace BitVec
/--
@@ -39,7 +41,7 @@ theorem iunfoldr.fst_eq
private theorem iunfoldr.eq_test
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)
(init : state 0 = a)
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
iunfoldr f a = (state w, BitVec.truncate w value) := by
apply Fin.hIterate_eq (fun i => ((state i, BitVec.truncate i value) : α × BitVec i))
case init =>
@@ -48,15 +50,15 @@ private theorem iunfoldr.eq_test
intro i
simp_all [truncate_succ]
theorem iunfoldr_getLsb' {f : Fin w α α × Bool} (state : Nat α)
theorem iunfoldr_getLsbD' {f : Fin w α α × Bool} (state : Nat α)
(ind : (i : Fin w), (f i (state i.val)).fst = state (i.val+1)) :
( i : Fin w, getLsb (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd)
( i : Fin w, getLsbD (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd)
(iunfoldr f (state 0)).fst = state w := by
unfold iunfoldr
simp
apply Fin.hIterate_elim
(fun j (p : α × BitVec j) => (hj : j w)
( i : Fin j, getLsb p.snd i.val = (f i.val, Nat.lt_of_lt_of_le i.isLt hj (state i.val)).snd)
( i : Fin j, getLsbD p.snd i.val = (f i.val, Nat.lt_of_lt_of_le i.isLt hj (state i.val)).snd)
p.fst = state j)
case hj => simp
case init =>
@@ -71,7 +73,7 @@ theorem iunfoldr_getLsb' {f : Fin w → αα × Bool} (state : Nat → α)
apply And.intro
case left =>
intro i
simp only [getLsb_cons]
simp only [getLsbD_cons]
have hj2 : j.val w := by simp
cases (Nat.lt_or_eq_of_le (Nat.lt_succ.mp i.isLt)) with
| inl h3 => simp [if_neg, (Nat.ne_of_lt h3)]
@@ -88,10 +90,10 @@ theorem iunfoldr_getLsb' {f : Fin w → αα × Bool} (state : Nat → α)
rw [ ind j, (ih hj2).2]
theorem iunfoldr_getLsb {f : Fin w α α × Bool} (state : Nat α) (i : Fin w)
theorem iunfoldr_getLsbD {f : Fin w α α × Bool} (state : Nat α) (i : Fin w)
(ind : (i : Fin w), (f i (state i.val)).fst = state (i.val+1)) :
getLsb (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd := by
exact (iunfoldr_getLsb' state ind).1 i
getLsbD (iunfoldr f (state 0)).snd i.val = (f i (state i.val)).snd := by
exact (iunfoldr_getLsbD' state ind).1 i
/--
Correctness theorem for `iunfoldr`.
@@ -99,14 +101,14 @@ Correctness theorem for `iunfoldr`.
theorem iunfoldr_replace
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)
(init : state 0 = a)
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
iunfoldr f a = (state w, value) := by
simp [iunfoldr.eq_test state value a init step]
theorem iunfoldr_replace_snd
{f : Fin w α α × Bool} (state : Nat α) (value : BitVec w) (a : α)
(init : state 0 = a)
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsbD i.val)) :
(iunfoldr f a).snd = value := by
simp [iunfoldr.eq_test state value a init step]

File diff suppressed because it is too large Load Diff

View File

@@ -4,18 +4,13 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: F. G. Dorais
-/
prelude
import Init.BinderPredicates
import Init.NotationExtra
/-- Boolean exclusive or -/
abbrev xor : Bool Bool Bool := bne
namespace Bool
/- Namespaced versions that can be used instead of prefixing `_root_` -/
@[inherit_doc not] protected abbrev not := not
@[inherit_doc or] protected abbrev or := or
@[inherit_doc and] protected abbrev and := and
@[inherit_doc xor] protected abbrev xor := xor
/-- Boolean exclusive or -/
abbrev xor : Bool Bool Bool := bne
instance (p : Bool Prop) [inst : DecidablePred p] : Decidable ( x, p x) :=
match inst true, inst false with
@@ -52,13 +47,19 @@ 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
-- These lemmas assist with confluence.
@[simp] theorem eq_false_imp_eq_true_iff :
(a b : Bool), ((a = false b = true) (b = false a = true)) = True := by decide
@[simp] theorem eq_true_imp_eq_false_iff :
(a b : Bool), ((a = true b = false) (b = true a = false)) = True := by decide
/-! ### and -/
@[simp] theorem and_self_left : (a b : Bool), (a && (a && b)) = (a && b) := by decide
@[simp] theorem and_self_right : (a b : Bool), ((a && b) && b) = (a && b) := by decide
@[simp] theorem and_self_left : (a b : Bool), (a && (a && b)) = (a && b) := by decide
@[simp] theorem and_self_right : (a b : Bool), ((a && b) && b) = (a && b) := by decide
@[simp] theorem not_and_self : (x : Bool), (!x && x) = false := by decide
@[simp] theorem and_not_self : (x : Bool), (x && !x) = false := by decide
@@ -70,8 +71,8 @@ Added for confluence with `not_and_self` `and_not_self` on term
1. `(b = true !b = true)` via `Bool.and_eq_true`
2. `false = true` via `Bool.and_not_self`
-/
@[simp] theorem eq_true_and_eq_false_self : (b : Bool), (b = true b = false) False := by decide
@[simp] theorem eq_false_and_eq_true_self : (b : Bool), (b = false b = true) False := by decide
@[simp] theorem eq_true_and_eq_false_self : (b : Bool), (b = true b = false) False := by decide
@[simp] theorem eq_false_and_eq_true_self : (b : Bool), (b = false b = true) False := by decide
theorem and_comm : (x y : Bool), (x && y) = (y && x) := by decide
instance : Std.Commutative (· && ·) := and_comm
@@ -86,15 +87,20 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
`Bool.coe_iff_coe` and `a → b` via `Bool.and_eq_true` and
`and_iff_left_iff_imp`.
-/
@[simp] theorem and_iff_left_iff_imp : (a b : Bool), ((a && b) = a) (a b) := by decide
@[simp] theorem and_iff_right_iff_imp : (a b : Bool), ((a && b) = b) (b a) := by decide
@[simp] theorem iff_self_and : (a b : Bool), (a = (a && b)) (a b) := by decide
@[simp] theorem iff_and_self : (a b : Bool), (b = (a && b)) (b a) := by decide
@[simp] theorem and_iff_left_iff_imp : {a b : Bool}, ((a && b) = a) (a b) := by decide
@[simp] theorem and_iff_right_iff_imp : {a b : Bool}, ((a && b) = b) (b a) := by decide
@[simp] theorem iff_self_and : {a b : Bool}, (a = (a && b)) (a b) := by decide
@[simp] theorem iff_and_self : {a b : Bool}, (b = (a && b)) (b a) := by decide
@[simp] theorem not_and_iff_left_iff_imp : {a b : Bool}, ((!a && b) = a) !a !b := by decide
@[simp] theorem and_not_iff_right_iff_imp : {a b : Bool}, ((a && !b) = b) !a !b := by decide
@[simp] theorem iff_not_self_and : {a b : Bool}, (a = (!a && b)) !a !b := by decide
@[simp] theorem iff_and_not_self : {a b : Bool}, (b = (a && !b)) !a !b := by decide
/-! ### or -/
@[simp] theorem or_self_left : (a b : Bool), (a || (a || b)) = (a || b) := by decide
@[simp] theorem or_self_right : (a b : Bool), ((a || b) || b) = (a || b) := by decide
@[simp] theorem or_self_left : (a b : Bool), (a || (a || b)) = (a || b) := by decide
@[simp] theorem or_self_right : (a b : Bool), ((a || b) || b) = (a || b) := by decide
@[simp] theorem not_or_self : (x : Bool), (!x || x) = true := by decide
@[simp] theorem or_not_self : (x : Bool), (x || !x) = true := by decide
@@ -115,10 +121,15 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
`Bool.coe_iff_coe` and `a → b` via `Bool.or_eq_true` and
`and_iff_left_iff_imp`.
-/
@[simp] theorem or_iff_left_iff_imp : (a b : Bool), ((a || b) = a) (b a) := by decide
@[simp] theorem or_iff_right_iff_imp : (a b : Bool), ((a || b) = b) (a b) := by decide
@[simp] theorem iff_self_or : (a b : Bool), (a = (a || b)) (b a) := by decide
@[simp] theorem iff_or_self : (a b : Bool), (b = (a || b)) (a b) := by decide
@[simp] theorem or_iff_left_iff_imp : {a b : Bool}, ((a || b) = a) (b a) := by decide
@[simp] theorem or_iff_right_iff_imp : {a b : Bool}, ((a || b) = b) (a b) := by decide
@[simp] theorem iff_self_or : {a b : Bool}, (a = (a || b)) (b a) := by decide
@[simp] theorem iff_or_self : {a b : Bool}, (b = (a || b)) (a b) := by decide
@[simp] theorem not_or_iff_left_iff_imp : {a b : Bool}, ((!a || b) = a) a b := by decide
@[simp] theorem or_not_iff_right_iff_imp : {a b : Bool}, ((a || !b) = b) a b := by decide
@[simp] theorem iff_not_self_or : {a b : Bool}, (a = (!a || b)) a b := by decide
@[simp] theorem iff_or_not_self : {a b : Bool}, (b = (a || !b)) a b := by decide
theorem or_comm : (x y : Bool), (x || y) = (y || x) := by decide
instance : Std.Commutative (· || ·) := or_comm
@@ -134,7 +145,7 @@ theorem and_or_distrib_right : ∀ (x y z : Bool), ((x || y) && z) = (x && z ||
theorem or_and_distrib_left : (x y z : Bool), (x || y && z) = ((x || y) && (x || z)) := by decide
theorem or_and_distrib_right : (x y z : Bool), (x && y || z) = ((x || z) && (y || z)) := by decide
theorem and_xor_distrib_left : (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
theorem and_xor_distrib_left : (x y z : Bool), (x && xor y z) = xor (x && y) (x && z) := by decide
theorem and_xor_distrib_right : (x y z : Bool), (xor x y && z) = xor (x && z) (y && z) := by decide
/-- De Morgan's law for boolean and -/
@@ -143,10 +154,10 @@ theorem and_xor_distrib_right : ∀ (x y z : Bool), (xor x y && z) = xor (x && z
/-- De Morgan's law for boolean or -/
@[simp] theorem not_or : (x y : Bool), (!(x || y)) = (!x && !y) := by decide
theorem and_eq_true_iff (x y : Bool) : (x && y) = true x = true y = true :=
theorem and_eq_true_iff {x y : Bool} : (x && y) = true x = true y = true :=
Iff.of_eq (and_eq_true x y)
theorem and_eq_false_iff : (x y : Bool), (x && y) = false x = false y = false := by decide
theorem and_eq_false_iff : {x y : Bool}, (x && y) = false x = false y = false := by decide
/-
New simp rule that replaces `Bool.and_eq_false_eq_eq_false_or_eq_false` in
@@ -161,11 +172,11 @@ Consider the term: `¬((b && c) = true)`:
1. Further reduces to `b = false c = false` via `Bool.and_eq_false_eq_eq_false_or_eq_false`.
2. Further reduces to `b = true → c = false` via `not_and` and `Bool.not_eq_true`.
-/
@[simp] theorem and_eq_false_imp : (x y : Bool), (x && y) = false (x = true y = false) := by decide
@[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
@[simp] theorem or_eq_false_iff : {x y : Bool}, (x || y) = false x = false y = false := by decide
/-! ### eq/beq/bne -/
@@ -187,11 +198,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
@@ -204,8 +213,11 @@ instance : Std.LawfulIdentity (· != ·) false where
@[simp] theorem not_beq_self : (x : Bool), ((!x) == x) = false := by decide
@[simp] theorem beq_not_self : (x : Bool), (x == !x) = false := by decide
@[simp] theorem not_bne_self : (x : Bool), ((!x) != x) = true := by decide
@[simp] theorem bne_not_self : (x : Bool), (x != !x) = true := by decide
@[simp] theorem not_bne : (a b : Bool), ((!a) != b) = !(a != b) := by decide
@[simp] theorem bne_not : (a b : Bool), (a != !b) = !(a != b) := by decide
theorem not_bne_self : (x : Bool), ((!x) != x) = true := by decide
theorem bne_not_self : (x : Bool), (x != !x) = true := by decide
/-
Added for equivalence with `Bool.not_beq_self` and needed for confluence
@@ -219,13 +231,13 @@ due to `beq_iff_eq`.
@[simp] theorem bne_self_left : (a b : Bool), (a != (a != b)) = b := by decide
@[simp] theorem bne_self_right : (a b : Bool), ((a != b) != b) = a := by decide
@[simp] theorem not_bne_not : (x y : Bool), ((!x) != (!y)) = (x != y) := by decide
theorem not_bne_not : (x y : Bool), ((!x) != (!y)) = (x != y) := by simp
@[simp] theorem bne_assoc : (x y z : Bool), ((x != y) != z) = (x != (y != z)) := by decide
instance : Std.Associative (· != ·) := bne_assoc
@[simp] theorem bne_left_inj : (x y z : Bool), (x != y) = (x != z) y = z := by decide
@[simp] theorem bne_right_inj : (x y z : Bool), (x != z) = (y != z) x = y := by decide
@[simp] theorem bne_left_inj : {x y z : Bool}, (x != y) = (x != z) y = z := by decide
@[simp] theorem bne_right_inj : {x y z : Bool}, (x != z) = (y != z) x = y := by decide
theorem eq_not_of_ne : {x y : Bool}, x y x = !y := by decide
@@ -237,20 +249,28 @@ theorem beq_eq_decide_eq [BEq α] [LawfulBEq α] [DecidableEq α] (a b : α) :
· simp [ne_of_beq_false h]
· simp [eq_of_beq h]
@[simp] theorem not_eq_not : {a b : Bool}, ¬a = !b a = b := by decide
theorem eq_not : {a b : Bool}, (a = (!b)) (a b) := by decide
theorem not_eq : {a b : Bool}, ((!a) = b) (a b) := by decide
@[simp] theorem not_eq_not : {a b : Bool}, ¬a = !b a = b := by decide
@[simp] theorem not_not_eq : {a b : Bool}, ¬(!a) = b a = b := by decide
@[simp] theorem coe_iff_coe : (a b : Bool), (a b) a = b := by decide
/--
We move `!` from the left hand side of an equality to the right hand side.
This helps confluence, and also helps combining pairs of `!`s.
-/
@[simp] theorem not_eq_eq_eq_not : {a b : Bool}, ((!a) = b) (a = !b) := by decide
@[simp] theorem coe_true_iff_false : (a b : Bool), (a b = false) a = (!b) := by decide
@[simp] theorem coe_false_iff_true : (a b : Bool), (a = false b) (!a) = b := by decide
@[simp] theorem coe_false_iff_false : (a b : Bool), (a = false b = false) (!a) = (!b) := by decide
@[simp] theorem coe_iff_coe : {a b : Bool}, (a b) a = b := by decide
@[simp] theorem coe_true_iff_false : {a b : Bool}, (a b = false) a = (!b) := by decide
@[simp] theorem coe_false_iff_true : {a b : Bool}, (a = false b) (!a) = b := by decide
@[simp] theorem coe_false_iff_false : {a b : Bool}, (a = false b = false) (!a) = (!b) := by decide
/-! ### beq properties -/
theorem beq_comm {α} [BEq α] [LawfulBEq α] {a b : α} : (a == b) = (b == a) :=
(Bool.coe_iff_coe (a == b) (b == a)).mp (by simp [@eq_comm α])
Bool.coe_iff_coe.mp (by simp [@eq_comm α])
/-! ### xor -/
@@ -282,9 +302,9 @@ theorem xor_right_comm : ∀ (x y z : Bool), xor (xor x y) z = xor (xor x z) y :
theorem xor_assoc : (x y z : Bool), xor (xor x y) z = xor x (xor y z) := bne_assoc
theorem xor_left_inj : (x y z : Bool), xor x y = xor x z y = z := bne_left_inj
theorem xor_left_inj : {x y z : Bool}, xor x y = xor x z y = z := bne_left_inj
theorem xor_right_inj : (x y z : Bool), xor x z = xor y z x = y := bne_right_inj
theorem xor_right_inj : {x y z : Bool}, xor x z = xor y z x = y := bne_right_inj
/-! ### le/lt -/
@@ -353,7 +373,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
@@ -362,15 +382,12 @@ def toNat (b:Bool) : Nat := cond b 1 0
theorem toNat_le (c : Bool) : c.toNat 1 := by
cases c <;> trivial
@[deprecated toNat_le (since := "2024-02-23")]
abbrev toNat_le_one := toNat_le
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
Nat.lt_succ_of_le (toNat_le _)
@[simp] theorem toNat_eq_zero (b : Bool) : b.toNat = 0 b = false := by
@[simp] theorem toNat_eq_zero {b : Bool} : b.toNat = 0 b = false := by
cases b <;> simp
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 b = true := by
@[simp] theorem toNat_eq_one {b : Bool} : b.toNat = 1 b = true := by
cases b <;> simp
/-! ### ite -/
@@ -395,6 +412,13 @@ theorem toNat_lt (b : Bool) : b.toNat < 2 :=
(ite p t f = false) = ite p (t = false) (f = false) := by
cases h with | _ p => simp [p]
@[simp] theorem ite_eq_false : (if b = false then p else q) if b then q else p := by
cases b <;> simp
@[simp] theorem ite_eq_true_else_eq_false {q : Prop} :
(if b = true then q else b = false) (b = true q) := by
cases b <;> simp
/-
`not_ite_eq_true_eq_true` and related theorems below are added for
non-confluence. A motivating example is
@@ -409,37 +433,57 @@ lemmas.
-/
@[simp]
theorem not_ite_eq_true_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
theorem not_ite_eq_true_eq_true {p : Prop} [h : Decidable p] {b c : Bool} :
¬(ite p (b = true) (c = true)) (ite p (b = false) (c = false)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_false_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
theorem not_ite_eq_false_eq_false {p : Prop} [h : Decidable p] {b c : Bool} :
¬(ite p (b = false) (c = false)) (ite p (b = true) (c = true)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_true_eq_false (p : Prop) [h : Decidable p] (b c : Bool) :
theorem not_ite_eq_true_eq_false {p : Prop} [h : Decidable p] {b c : Bool} :
¬(ite p (b = true) (c = false)) (ite p (b = false) (c = true)) := by
cases h with | _ p => simp [p]
@[simp]
theorem not_ite_eq_false_eq_true (p : Prop) [h : Decidable p] (b c : Bool) :
theorem not_ite_eq_false_eq_true {p : Prop} [h : Decidable p] {b c : Bool} :
¬(ite p (b = false) (c = true)) (ite p (b = true) (c = false)) := by
cases h with | _ p => simp [p]
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = true then True else b = true`
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
`if b = true then True else b = true`.
However the discrimination tree key is just `→`, so this is tried too often.
-/
@[simp] theorem eq_false_imp_eq_true : (b:Bool), (b = false b = true) (b = true) := by decide
theorem eq_false_imp_eq_true : {b : Bool}, (b = false b = true) (b = true) := by decide
/-
Added for confluence between `if_true_left` and `ite_false_same` on
`if b = false then True else b = false`
It would be nice to have this for confluence between `if_true_left` and `ite_false_same` on
`if b = false then True else b = false`.
However the discrimination tree key is just `→`, so this is tried too often.
-/
@[simp] theorem eq_true_imp_eq_false : (b:Bool), (b = true b = false) (b = false) := by decide
theorem eq_true_imp_eq_false : {b : Bool}, (b = true b = false) (b = false) := by decide
/-! ### forall -/
theorem forall_bool' {p : Bool Prop} (b : Bool) : ( x, p x) p b p !b :=
fun h h _, h _, fun h₁, h₂ x by cases b <;> cases x <;> assumption
@[simp]
theorem forall_bool {p : Bool Prop} : ( b, p b) p false p true :=
forall_bool' false
/-! ### exists -/
theorem exists_bool' {p : Bool Prop} (b : Bool) : ( x, p x) p b p !b :=
fun x, hx by cases x <;> cases b <;> first | exact .inl _ | exact .inr _,
fun h by cases h <;> exact _, _
@[simp]
theorem exists_bool {p : Bool Prop} : ( b, p b) p false p true :=
exists_bool' false
/-! ### cond -/
@@ -453,6 +497,11 @@ theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := cond_eq_ite
@[simp] theorem cond_self (c : Bool) (t : α) : cond c t t = t := by cases c <;> rfl
/-- If the return values are propositions, there is no harm in simplifying a `bif` to an `if`. -/
@[simp] theorem cond_prop {b : Bool} {p q : Prop} :
(bif b then p else q) if b then p else q := by
cases b <;> simp
/-
This is a simp rule in Mathlib, but results in non-confluence that is difficult
to fix as decide distributes over propositions. As an example, observe that
@@ -470,11 +519,11 @@ theorem cond_decide {α} (p : Prop) [Decidable p] (t e : α) :
cond (decide p) t e = if p then t else e := by
simp [cond_eq_ite]
@[simp] theorem cond_eq_ite_iff (a : Bool) (p : Prop) [h : Decidable p] (x y u v : α) :
@[simp] theorem cond_eq_ite_iff {a : Bool} {p : Prop} [h : Decidable p] {x y u v : α} :
(cond a x y = ite p u v) ite a x y = ite p u v := by
simp [Bool.cond_eq_ite]
@[simp] theorem ite_eq_cond_iff (p : Prop) [h : Decidable p] (a : Bool) (x y u v : α) :
@[simp] theorem ite_eq_cond_iff {p : Prop} {a : Bool} [h : Decidable p] {x y u v : α} :
(ite p x y = cond a u v) ite p x y = ite a u v := by
simp [Bool.cond_eq_ite]
@@ -493,10 +542,24 @@ protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := co
@[simp] theorem cond_true_right : (c t : Bool), cond c t true = (!c || t) := by decide
@[simp] theorem cond_false_right : (c t : Bool), cond c t false = ( c && t) := by decide
-- These restore confluence between the above lemmas and `cond_not`.
@[simp] theorem cond_true_not_same : (c b : Bool), cond c (!c) b = (!c && b) := by decide
@[simp] theorem cond_false_not_same : (c b : Bool), cond c b (!c) = (!c || b) := by decide
@[simp] theorem cond_true_same : (c b : Bool), cond c c b = (c || b) := by decide
@[simp] theorem cond_false_same : (c b : Bool), cond c b c = (c && b) := by decide
/-# decidability -/
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
@@ -512,9 +575,24 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
decide (p q) = (decide p == decide q) := by
cases dp with | _ p => simp [p]
@[boolToPropSimps]
theorem and_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p && q) = decide (p q) := by
cases dp with | _ p => simp [p]
@[boolToPropSimps]
theorem or_eq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(p || q) = decide (p q) := by
cases dp with | _ p => simp [p]
@[boolToPropSimps]
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p q)] [dp : Decidable p] [dq : Decidable q] :
(decide p == decide q) = decide (p q) := by
cases dp with | _ p => simp [p]
end Bool
export Bool (cond_eq_if)
export Bool (cond_eq_if xor and or not)
/-! ### decide -/
@@ -523,3 +601,19 @@ export Bool (cond_eq_if)
@[simp] theorem true_eq_decide_iff {p : Prop} [h : Decidable p] : true = decide p p := by
cases h with | _ q => simp [q]
/-! ### coercions -/
/--
This should not be turned on globally as an instance because it degrades performance in Mathlib,
but may be used locally.
-/
def boolPredToPred : Coe (α Bool) (α Prop) where
coe r := fun a => Eq (r a) true
/--
This should not be turned on globally as an instance because it degrades performance in Mathlib,
but may be used locally.
-/
def boolRelToRel : Coe (α α Bool) (α α Prop) where
coe r := fun a b => Eq (r a b) true

View File

@@ -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
@@ -187,6 +191,137 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 →
def foldl {β : Type v} (f : β UInt8 β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : β :=
Id.run <| as.foldlM f init start stop
/-- Iterator over the bytes (`UInt8`) of a `ByteArray`.
Typically created by `arr.iter`, where `arr` is a `ByteArray`.
An iterator is *valid* if the position `i` is *valid* for the array `arr`, meaning `0 ≤ i ≤ arr.size`
Most operations on iterators return arbitrary values if the iterator is not valid. The functions in
the `ByteArray.Iterator` API should rule out the creation of invalid iterators, with two exceptions:
- `Iterator.next iter` is invalid if `iter` is already at the end of the array (`iter.atEnd` is
`true`)
- `Iterator.forward iter n`/`Iterator.nextn iter n` is invalid if `n` is strictly greater than the
number of remaining bytes.
-/
structure Iterator where
/-- The array the iterator is for. -/
array : ByteArray
/-- The current position.
This position is not necessarily valid for the array, for instance if one keeps calling
`Iterator.next` when `Iterator.atEnd` is true. If the position is not valid, then the
current byte is `(default : UInt8)`. -/
idx : Nat
deriving Inhabited
/-- Creates an iterator at the beginning of an array. -/
def mkIterator (arr : ByteArray) : Iterator :=
arr, 0
@[inherit_doc mkIterator]
abbrev iter := mkIterator
/-- The size of an array iterator is the number of bytes remaining. -/
instance : SizeOf Iterator where
sizeOf i := i.array.size - i.idx
theorem Iterator.sizeOf_eq (i : Iterator) : sizeOf i = i.array.size - i.idx :=
rfl
namespace Iterator
/-- Number of bytes remaining in the iterator. -/
def remainingBytes : Iterator Nat
| arr, i => arr.size - i
@[inherit_doc Iterator.idx]
def pos := Iterator.idx
/-- The byte at the current position.
On an invalid position, returns `(default : UInt8)`. -/
@[inline]
def curr : Iterator UInt8
| arr, i =>
if h:i < arr.size then
arr[i]'h
else
default
/-- Moves the iterator's position forward by one byte, unconditionally.
It is only valid to call this function if the iterator is not at the end of the array, *i.e.*
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
@[inline]
def next : Iterator Iterator
| arr, i => arr, i + 1
/-- Decreases the iterator's position.
If the position is zero, this function is the identity. -/
@[inline]
def prev : Iterator Iterator
| arr, i => arr, i - 1
/-- True if the iterator is past the array's last byte. -/
@[inline]
def atEnd : Iterator Bool
| arr, i => i arr.size
/-- True if the iterator is not past the array's last byte. -/
@[inline]
def hasNext : Iterator Bool
| arr, i => i < arr.size
/-- The byte at the current position. --/
@[inline]
def curr' (it : Iterator) (h : it.hasNext) : UInt8 :=
match it with
| arr, i =>
have : i < arr.size := by
simp only [hasNext, decide_eq_true_eq] at h
assumption
arr[i]
/-- Moves the iterator's position forward by one byte. --/
@[inline]
def next' (it : Iterator) (_h : it.hasNext) : Iterator :=
match it with
| arr, i => arr, i + 1
/-- True if the position is not zero. -/
@[inline]
def hasPrev : Iterator Bool
| _, i => i > 0
/-- Moves the iterator's position to the end of the array.
Note that `i.toEnd.atEnd` is always `true`. -/
@[inline]
def toEnd : Iterator Iterator
| arr, _ => arr, arr.size
/-- Moves the iterator's position several bytes forward.
The resulting iterator is only valid if the number of bytes to skip is less than or equal to
the number of bytes left in the iterator. -/
@[inline]
def forward : Iterator Nat Iterator
| arr, i, f => arr, i + f
@[inherit_doc forward, inline]
def nextn : Iterator Nat Iterator := forward
/-- Moves the iterator's position several bytes back.
If asked to go back more bytes than available, stops at the beginning of the array. -/
@[inline]
def prevn : Iterator Nat Iterator
| arr, i, f => arr, i - f
end Iterator
end ByteArray
def List.toByteArray (bs : List UInt8) : ByteArray :=

View File

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

View File

@@ -22,13 +22,18 @@ protected theorem le_total (a b : Char) : a ≤ b b ≤ a := UInt32.le_total
protected theorem lt_asymm {a b : Char} (h : a < b) : ¬ b < a := UInt32.lt_asymm h
protected theorem ne_of_lt {a b : Char} (h : a < b) : a b := Char.ne_of_val_ne (UInt32.ne_of_lt h)
theorem utf8Size_pos (c : Char) : 0 < c.utf8Size := by
simp only [utf8Size]
repeat (split; decide)
decide
theorem utf8Size_eq (c : Char) : c.utf8Size = 1 c.utf8Size = 2 c.utf8Size = 3 c.utf8Size = 4 := by
have := c.utf8Size_pos
have := c.utf8Size_le_four
omega
@[simp] theorem ofNat_toNat (c : Char) : Char.ofNat c.toNat = c := by
rw [Char.ofNat, dif_pos]
rfl
@[ext] protected theorem ext : {a b : Char} a.val = b.val a = b
| _,_, _,_, rfl => rfl
end Char
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size

View File

@@ -31,7 +31,7 @@ This differs from addition, which wraps around:
(2 : Fin 3) + 1 = (0 : Fin 3)
```
-/
def succ : Fin n Fin n.succ
def succ : Fin n Fin (n + 1)
| i, h => i+1, Nat.succ_lt_succ h
variable {n : Nat}
@@ -39,16 +39,20 @@ variable {n : Nat}
/--
Returns `a` modulo `n + 1` as a `Fin n.succ`.
-/
protected def ofNat {n : Nat} (a : Nat) : Fin n.succ :=
protected def ofNat {n : Nat} (a : Nat) : Fin (n + 1) :=
a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)
/--
Returns `a` modulo `n` as a `Fin n`.
The assumption `n > 0` ensures that `Fin n` is nonempty.
The assumption `NeZero n` ensures that `Fin n` is nonempty.
-/
protected def ofNat' {n : Nat} (a : Nat) (h : n > 0) : Fin n :=
a % n, Nat.mod_lt _ h
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
a % n, Nat.mod_lt _ (pos_of_neZero n)
-- We intend to deprecate `Fin.ofNat` in favor of `Fin.ofNat'` (and later rename).
-- This is waiting on https://github.com/leanprover/lean4/pull/5323
-- attribute [deprecated Fin.ofNat' (since := "2024-09-16")] Fin.ofNat
private theorem mlt {b : Nat} : {a : Nat} a < n b % n < n
| 0, h => Nat.mod_lt _ h
@@ -66,7 +70,24 @@ protected def mul : Fin n → Fin n → Fin n
/-- Subtraction modulo `n` -/
protected def sub : Fin n Fin n Fin n
| a, h, b, _ => (a + (n - b)) % n, mlt h
/-
The definition of `Fin.sub` has been updated to improve performance.
The right-hand-side of the following `match` was originally
```
⟨(a + (n - b)) % n, mlt h⟩
```
This caused significant performance issues when testing definitional equality,
such as `x =?= x - 1` where `x : Fin n` and `n` is a big number,
as Lean spent a long time reducing
```
((n - 1) + x.val) % n
```
For example, this was an issue for `Fin 2^64` (i.e., `UInt64`).
This change improves performance by leveraging the fact that `Nat.add` is defined
using recursion on the second argument.
See issue #4413.
-/
| a, h, b, _ => ((n - b) + a) % n, mlt h
/-!
Remark: land/lor can be defined without using (% n), but
@@ -124,14 +145,17 @@ instance : ShiftLeft (Fin n) where
instance : ShiftRight (Fin n) where
shiftRight := Fin.shiftRight
instance instOfNat : OfNat (Fin (no_index (n+1))) i where
ofNat := Fin.ofNat i
instance instOfNat {n : Nat} [NeZero n] {i : Nat} : OfNat (Fin n) i where
ofNat := Fin.ofNat' n i
instance : Inhabited (Fin (no_index (n+1))) where
instance instInhabited {n : Nat} [NeZero n] : Inhabited (Fin n) where
default := 0
@[simp] theorem zero_eta : (0, Nat.zero_lt_succ _ : Fin (n + 1)) = 0 := rfl
theorem ne_of_val_ne {i j : Fin n} (h : val i val j) : i j :=
fun h' => absurd (val_eq_of_eq h') h
theorem val_ne_of_ne {i j : Fin n} (h : i j) : val i val j :=
fun h' => absurd (eq_of_val_eq h') h
@@ -193,4 +217,7 @@ theorem val_add_one_le_of_lt {n : Nat} {a b : Fin n} (h : a < b) : (a : Nat) + 1
theorem val_add_one_le_of_gt {n : Nat} {a b : Fin n} (h : a > b) : (b : Nat) + 1 (a : Nat) := h
theorem exists_iff {p : Fin n Prop} : (Exists fun i => p i) Exists fun i => Exists fun h => p i, h :=
fun i, hi, hpi => i, hi, hpi, fun i, hi, hpi => i, hi, hpi
end Fin

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

@@ -11,9 +11,6 @@ import Init.ByCases
import Init.Conv
import Init.Omega
-- Remove after the next stage0 update
set_option allowUnsafeReducibility true
namespace Fin
/-- If you actually have an element of `Fin n`, then the `n` is always positive -/
@@ -24,7 +21,7 @@ theorem mod_def (a m : Fin n) : a % m = Fin.mk (a % m) (Nat.lt_of_le_of_lt (Nat.
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a * b) % n) (Nat.mod_lt _ a.size_pos) := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk ((a + (n - b)) % n) (Nat.mod_lt _ a.size_pos) := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b) + a) % n) (Nat.mod_lt _ a.size_pos) := rfl
theorem size_pos' : [Nonempty (Fin n)], 0 < n | i => i.size_pos
@@ -37,33 +34,30 @@ 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
theorem exists_iff {p : Fin n Prop} : ( i, p i) i h, p i, h :=
fun i, hi, hpi => i, hi, hpi, fun i, hi, hpi => i, hi, hpi
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 ..
@[simp] theorem val_ofNat' (a : Nat) (is_pos : n > 0) :
(Fin.ofNat' a is_pos).val = a % n := rfl
@[simp] theorem val_ofNat' (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat' n a).val = a % n := rfl
@[deprecated ofNat'_zero_val (since := "2024-02-22")]
theorem ofNat'_zero_val : (Fin.ofNat' 0 h).val = 0 := Nat.zero_mod _
@[simp] theorem ofNat'_val_eq_self [NeZero n](x : Fin n) : (Fin.ofNat' n x) = x := by
ext
rw [val_ofNat', Nat.mod_eq_of_lt]
exact x.2
@[simp] theorem mod_val (a b : Fin n) : (a % b).val = a.val % b.val :=
rfl
@@ -146,9 +140,15 @@ theorem eq_zero_or_eq_succ {n : Nat} : ∀ i : Fin (n + 1), i = 0 ∃ j : Fi
theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i 0) : j : Fin n, i = j.succ :=
(eq_zero_or_eq_succ i).resolve_left hi
protected theorem le_antisymm_iff {x y : Fin n} : x = y x y y x :=
Fin.ext_iff.trans Nat.le_antisymm_iff
protected theorem le_antisymm {x y : Fin n} (h1 : x y) (h2 : y x) : x = y :=
Fin.le_antisymm_iff.2 h1, h2
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := 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
@@ -174,12 +174,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]
@@ -247,11 +247,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
@@ -270,7 +270,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]
@@ -288,7 +288,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
@@ -309,10 +309,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 :=
@@ -325,7 +325,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
@@ -351,7 +351,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
@@ -381,14 +381,14 @@ theorem castSucc_lt_succ (i : Fin n) : Fin.castSucc i < i.succ :=
lt_def.2 <| by simp only [coe_castSucc, val_succ, Nat.lt_succ_self]
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i Fin.castSucc j i < j.succ := by
simpa [lt_def, le_def] using Nat.succ_le_succ_iff.symm
simpa only [lt_def, le_def] using Nat.add_one_le_add_one_iff.symm
theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
Fin.castSucc i < j i.succ j := .rfl
@[simp] theorem succ_last (n : Nat) : (last n).succ = last n.succ := rfl
@[simp] theorem succ_eq_last_succ {n : Nat} (i : Fin n.succ) :
@[simp] theorem succ_eq_last_succ {n : Nat} {i : Fin n.succ} :
i.succ = last (n + 1) i = last n := by rw [ succ_last, succ_inj]
@[simp] theorem castSucc_castLT (i : Fin (n + 1)) (h : (i : Nat) < n) :
@@ -400,7 +400,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
@@ -412,10 +412,10 @@ 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
theorem castSucc_ne_zero_iff {a : Fin (n + 1)} : castSucc a 0 a 0 :=
not_congr <| castSucc_eq_zero_iff
theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
castSucc (Fin.succ j) = Fin.succ (castSucc j) := by simp [Fin.ext_iff]
@@ -424,7 +424,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
@@ -457,7 +457,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
@@ -477,7 +477,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
@@ -487,27 +487,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
@@ -530,14 +530,14 @@ theorem pred_succ (i : Fin n) {h : i.succ ≠ 0} : i.succ.pred h = i := by
cases i
rfl
theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i 0) (j : Fin n) :
theorem pred_eq_iff_eq_succ {n : Nat} {i : Fin (n + 1)} (hi : i 0) {j : Fin n} :
i.pred hi = j i = j.succ :=
fun h => by simp only [ h, Fin.succ_pred], fun h => by simp only [h, Fin.pred_succ]
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 _
@@ -557,14 +557,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
@@ -576,10 +576,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
@@ -750,28 +750,28 @@ theorem addCases_right {m n : Nat} {motive : Fin (m + n) → Sort _} {left right
/-! ### add -/
@[simp] theorem ofNat'_add (x : Nat) (lt : 0 < n) (y : Fin n) :
Fin.ofNat' x lt + y = Fin.ofNat' (x + y.val) lt := by
@[simp] theorem ofNat'_add [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x + y = Fin.ofNat' n (x + y.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.add_def]
@[simp] theorem add_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
x + Fin.ofNat' y lt = Fin.ofNat' (x.val + y) lt := by
@[simp] theorem add_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x + Fin.ofNat' n y = Fin.ofNat' n (x.val + y) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.add_def]
/-! ### sub -/
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = (a + (n - b)) % n := by
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = ((n - b) + a) % n := by
cases a; cases b; rfl
@[simp] theorem ofNat'_sub (x : Nat) (lt : 0 < n) (y : Fin n) :
Fin.ofNat' x lt - y = Fin.ofNat' (x + (n - y.val)) lt := by
@[simp] theorem ofNat'_sub [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x - y = Fin.ofNat' n ((n - y.val) + x) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.sub_def]
@[simp] theorem sub_ofNat' (x : Fin n) (y : Nat) (lt : 0 < n) :
x - Fin.ofNat' y lt = Fin.ofNat' (x.val + (n - y % n)) lt := by
@[simp] theorem sub_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x - Fin.ofNat' n y = Fin.ofNat' n ((n - y % n) + x.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.sub_def]
@@ -782,17 +782,20 @@ private theorem _root_.Nat.mod_eq_sub_of_lt_two_mul {x n} (h₁ : n ≤ x) (h₂
theorem coe_sub_iff_le {a b : Fin n} : ((a - b) : Nat) = a - b b a := by
rw [sub_def, le_def]
dsimp only
if h : n a + (n - b) then
if h : n (n - b) + a then
rw [Nat.mod_eq_sub_of_lt_two_mul h]
all_goals omega
else
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
if h : n a + (n - b) then
if h : n (n - b) + a then
rw [Nat.mod_eq_sub_of_lt_two_mul h]
all_goals omega
else
@@ -810,10 +813,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
@@ -829,9 +832,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

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

View File

@@ -8,7 +8,7 @@ The integers, with addition, multiplication, and subtraction.
prelude
import Init.Data.Cast
import Init.Data.Nat.Div
import Init.Data.List.Basic
set_option linter.missingDocs true -- keep it documented
open Nat
@@ -322,8 +322,8 @@ protected def pow (m : Int) : Nat → Int
| 0 => 1
| succ n => Int.pow m n * m
instance : HPow Int Nat Int where
hPow := Int.pow
instance : NatPow Int where
pow := Int.pow
instance : LawfulBEq Int where
eq_of_beq h := by simp [BEq.beq] at h; assumption

View File

@@ -16,83 +16,99 @@ There are three main conventions for integer division,
referred here as the E, F, T rounding conventions.
All three pairs satisfy the identity `x % y + (x / y) * y = x` unconditionally,
and satisfy `x / 0 = 0` and `x % 0 = x`.
### Historical notes
In early versions of Lean, the typeclasses provided by `/` and `%`
were defined in terms of `tdiv` and `tmod`, and these were named simply as `div` and `mod`.
However we decided it was better to use `ediv` and `emod`,
as they are consistent with the conventions used in SMTLib, and Mathlib,
and often mathematical reasoning is easier with these conventions.
At that time, we did not rename `div` and `mod` to `tdiv` and `tmod` (along with all their lemma).
In September 2024, we decided to do this rename (with deprecations in place),
and later we intend to rename `ediv` and `emod` to `div` and `mod`, as nearly all users will only
ever need to use these functions and their associated lemmas.
-/
/-! ### T-rounding division -/
/--
`div` uses the [*"T-rounding"*][t-rounding]
`tdiv` uses the [*"T-rounding"*][t-rounding]
(**T**runcation-rounding) convention, meaning that it rounds toward
zero. Also note that division by zero is defined to equal zero.
The relation between integer division and modulo is found in
`Int.mod_add_div` which states that
`a % b + b * (a / b) = a`, unconditionally.
`Int.tmod_add_tdiv` which states that
`tmod a b + b * (tdiv a b) = a`, unconditionally.
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862 [theo
mod_add_div]:
https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
Examples:
```
#eval (7 : Int) / (0 : Int) -- 0
#eval (0 : Int) / (7 : Int) -- 0
#eval (7 : Int).tdiv (0 : Int) -- 0
#eval (0 : Int).tdiv (7 : Int) -- 0
#eval (12 : Int) / (6 : Int) -- 2
#eval (12 : Int) / (-6 : Int) -- -2
#eval (-12 : Int) / (6 : Int) -- -2
#eval (-12 : Int) / (-6 : Int) -- 2
#eval (12 : Int).tdiv (6 : Int) -- 2
#eval (12 : Int).tdiv (-6 : Int) -- -2
#eval (-12 : Int).tdiv (6 : Int) -- -2
#eval (-12 : Int).tdiv (-6 : Int) -- 2
#eval (12 : Int) / (7 : Int) -- 1
#eval (12 : Int) / (-7 : Int) -- -1
#eval (-12 : Int) / (7 : Int) -- -1
#eval (-12 : Int) / (-7 : Int) -- 1
#eval (12 : Int).tdiv (7 : Int) -- 1
#eval (12 : Int).tdiv (-7 : Int) -- -1
#eval (-12 : Int).tdiv (7 : Int) -- -1
#eval (-12 : Int).tdiv (-7 : Int) -- 1
```
Implemented by efficient native code.
-/
@[extern "lean_int_div"]
def div : (@& Int) (@& Int) Int
def tdiv : (@& Int) (@& Int) Int
| ofNat m, ofNat n => ofNat (m / n)
| ofNat m, -[n +1] => -ofNat (m / succ n)
| -[m +1], ofNat n => -ofNat (succ m / n)
| -[m +1], -[n +1] => ofNat (succ m / succ n)
@[deprecated tdiv (since := "2024-09-11")] abbrev div := tdiv
/-- Integer modulo. This function uses the
[*"T-rounding"*][t-rounding] (**T**runcation-rounding) convention
to pair with `Int.div`, meaning that `a % b + b * (a / b) = a`
unconditionally (see [`Int.mod_add_div`][theo mod_add_div]). In
to pair with `Int.tdiv`, meaning that `tmod a b + b * (tdiv a b) = a`
unconditionally (see [`Int.tmod_add_tdiv`][theo tmod_add_tdiv]). In
particular, `a % 0 = a`.
[t-rounding]: https://dl.acm.org/doi/pdf/10.1145/128861.128862
[theo mod_add_div]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.mod_add_div#doc
[theo tmod_add_tdiv]: https://leanprover-community.github.io/mathlib4_docs/find/?pattern=Int.tmod_add_tdiv#doc
Examples:
```
#eval (7 : Int) % (0 : Int) -- 7
#eval (0 : Int) % (7 : Int) -- 0
#eval (7 : Int).tmod (0 : Int) -- 7
#eval (0 : Int).tmod (7 : Int) -- 0
#eval (12 : Int) % (6 : Int) -- 0
#eval (12 : Int) % (-6 : Int) -- 0
#eval (-12 : Int) % (6 : Int) -- 0
#eval (-12 : Int) % (-6 : Int) -- 0
#eval (12 : Int).tmod (6 : Int) -- 0
#eval (12 : Int).tmod (-6 : Int) -- 0
#eval (-12 : Int).tmod (6 : Int) -- 0
#eval (-12 : Int).tmod (-6 : Int) -- 0
#eval (12 : Int) % (7 : Int) -- 5
#eval (12 : Int) % (-7 : Int) -- 5
#eval (-12 : Int) % (7 : Int) -- 2
#eval (-12 : Int) % (-7 : Int) -- 2
#eval (12 : Int).tmod (7 : Int) -- 5
#eval (12 : Int).tmod (-7 : Int) -- 5
#eval (-12 : Int).tmod (7 : Int) -- -5
#eval (-12 : Int).tmod (-7 : Int) -- -5
```
Implemented by efficient native code. -/
@[extern "lean_int_mod"]
def mod : (@& Int) (@& Int) Int
def tmod : (@& Int) (@& Int) Int
| ofNat m, ofNat n => ofNat (m % n)
| ofNat m, -[n +1] => ofNat (m % succ n)
| -[m +1], ofNat n => -ofNat (succ m % n)
| -[m +1], -[n +1] => -ofNat (succ m % succ n)
@[deprecated tmod (since := "2024-09-11")] abbrev mod := tmod
/-! ### F-rounding division
This pair satisfies `fdiv x y = floor (x / y)`.
-/
@@ -101,6 +117,22 @@ This pair satisfies `fdiv x y = floor (x / y)`.
Integer division. This version of division uses the F-rounding convention
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
Examples:
```
#eval (7 : Int).fdiv (0 : Int) -- 0
#eval (0 : Int).fdiv (7 : Int) -- 0
#eval (12 : Int).fdiv (6 : Int) -- 2
#eval (12 : Int).fdiv (-6 : Int) -- -2
#eval (-12 : Int).fdiv (6 : Int) -- -2
#eval (-12 : Int).fdiv (-6 : Int) -- 2
#eval (12 : Int).fdiv (7 : Int) -- 1
#eval (12 : Int).fdiv (-7 : Int) -- -2
#eval (-12 : Int).fdiv (7 : Int) -- -2
#eval (-12 : Int).fdiv (-7 : Int) -- 1
```
-/
def fdiv : Int Int Int
| 0, _ => 0
@@ -114,6 +146,23 @@ def fdiv : Int → Int → Int
Integer modulus. This version of `Int.mod` uses the F-rounding convention
(flooring division), in which `Int.fdiv x y` satisfies `fdiv x y = floor (x / y)`
and `Int.fmod` is the unique function satisfying `fmod x y + (fdiv x y) * y = x`.
Examples:
```
#eval (7 : Int).fmod (0 : Int) -- 7
#eval (0 : Int).fmod (7 : Int) -- 0
#eval (12 : Int).fmod (6 : Int) -- 0
#eval (12 : Int).fmod (-6 : Int) -- 0
#eval (-12 : Int).fmod (6 : Int) -- 0
#eval (-12 : Int).fmod (-6 : Int) -- 0
#eval (12 : Int).fmod (7 : Int) -- 5
#eval (12 : Int).fmod (-7 : Int) -- -2
#eval (-12 : Int).fmod (7 : Int) -- 2
#eval (-12 : Int).fmod (-7 : Int) -- -5
```
-/
def fmod : Int Int Int
| 0, _ => 0
@@ -130,6 +179,26 @@ This pair satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`.
Integer division. This version of `Int.div` uses the E-rounding convention
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ mod x y < natAbs y` for `y ≠ 0`
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
This is the function powering the `/` notation on integers.
Examples:
```
#eval (7 : Int) / (0 : Int) -- 0
#eval (0 : Int) / (7 : Int) -- 0
#eval (12 : Int) / (6 : Int) -- 2
#eval (12 : Int) / (-6 : Int) -- -2
#eval (-12 : Int) / (6 : Int) -- -2
#eval (-12 : Int) / (-6 : Int) -- 2
#eval (12 : Int) / (7 : Int) -- 1
#eval (12 : Int) / (-7 : Int) -- -1
#eval (-12 : Int) / (7 : Int) -- -2
#eval (-12 : Int) / (-7 : Int) -- 2
```
Implemented by efficient native code.
-/
@[extern "lean_int_ediv"]
def ediv : (@& Int) (@& Int) Int
@@ -143,6 +212,26 @@ def ediv : (@& Int) → (@& Int) → Int
Integer modulus. This version of `Int.mod` uses the E-rounding convention
(euclidean division), in which `Int.emod x y` satisfies `0 ≤ emod x y < natAbs y` for `y ≠ 0`
and `Int.ediv` is the unique function satisfying `emod x y + (ediv x y) * y = x`.
This is the function powering the `%` notation on integers.
Examples:
```
#eval (7 : Int) % (0 : Int) -- 7
#eval (0 : Int) % (7 : Int) -- 0
#eval (12 : Int) % (6 : Int) -- 0
#eval (12 : Int) % (-6 : Int) -- 0
#eval (-12 : Int) % (6 : Int) -- 0
#eval (-12 : Int) % (-6 : Int) -- 0
#eval (12 : Int) % (7 : Int) -- 5
#eval (12 : Int) % (-7 : Int) -- 5
#eval (-12 : Int) % (7 : Int) -- 2
#eval (-12 : Int) % (-7 : Int) -- 2
```
Implemented by efficient native code.
-/
@[extern "lean_int_emod"]
def emod : (@& Int) (@& Int) Int
@@ -160,7 +249,9 @@ instance : Mod Int where
@[simp, norm_cast] theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
theorem ofNat_div (m n : Nat) : (m / n) = div m n := rfl
theorem ofNat_tdiv (m n : Nat) : (m / n) = tdiv m n := rfl
@[deprecated ofNat_tdiv (since := "2024-09-11")] abbrev ofNat_div := ofNat_tdiv
theorem ofNat_fdiv : m n : Nat, (m / n) = fdiv m n
| 0, _ => by simp [fdiv]

View File

@@ -14,9 +14,6 @@ import Init.RCases
# Lemmas about integer division needed to bootstrap `omega`.
-/
-- Remove after the next stage0 update
set_option allowUnsafeReducibility true
open Nat (succ)
namespace Int
@@ -57,7 +54,7 @@ protected theorem dvd_mul_right (a b : Int) : a a * b := ⟨_, rfl⟩
protected theorem dvd_mul_left (a b : Int) : b a * b := _, Int.mul_comm ..
protected theorem neg_dvd {a b : Int} : -a b a b := by
@[simp] protected theorem neg_dvd {a b : Int} : -a b a b := by
constructor <;> exact fun k, e =>
-k, by simp [e, Int.neg_mul, Int.mul_neg, Int.neg_neg]
@@ -140,12 +137,12 @@ theorem eq_one_of_mul_eq_one_left {a b : Int} (H : 0 ≤ b) (H' : a * b = 1) : b
| ofNat _ => show ofNat _ = _ by simp
| -[_+1] => rfl
@[simp] protected theorem zero_div : b : Int, div 0 b = 0
@[simp] protected theorem zero_tdiv : b : Int, tdiv 0 b = 0
| ofNat _ => show ofNat _ = _ by simp
| -[_+1] => show -ofNat _ = _ by simp
unseal Nat.div in
@[simp] protected theorem div_zero : a : Int, div a 0 = 0
@[simp] protected theorem tdiv_zero : a : Int, tdiv a 0 = 0
| ofNat _ => show ofNat _ = _ by simp
| -[_+1] => rfl
@@ -159,16 +156,17 @@ unseal Nat.div in
/-! ### div equivalences -/
theorem div_eq_ediv : {a b : Int}, 0 a 0 b a.div b = a / b
theorem tdiv_eq_ediv : {a b : Int}, 0 a 0 b a.tdiv b = a / b
| 0, _, _, _ | _, 0, _, _ => by simp
| succ _, succ _, _, _ => rfl
theorem fdiv_eq_ediv : (a : Int) {b : Int}, 0 b fdiv a b = a / b
| 0, _, _ | -[_+1], 0, _ => by simp
| succ _, ofNat _, _ | -[_+1], succ _, _ => rfl
theorem fdiv_eq_div {a b : Int} (Ha : 0 a) (Hb : 0 b) : fdiv a b = div a b :=
div_eq_ediv Ha Hb fdiv_eq_ediv _ Hb
theorem fdiv_eq_tdiv {a b : Int} (Ha : 0 a) (Hb : 0 b) : fdiv a b = tdiv a b :=
tdiv_eq_ediv Ha Hb fdiv_eq_ediv _ Hb
/-! ### mod zero -/
@@ -178,9 +176,9 @@ theorem fdiv_eq_div {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : fdiv a b = div a
| ofNat _ => congrArg ofNat <| Nat.mod_zero _
| -[_+1] => congrArg negSucc <| Nat.mod_zero _
@[simp] theorem zero_mod (b : Int) : mod 0 b = 0 := by cases b <;> simp [mod]
@[simp] theorem zero_tmod (b : Int) : tmod 0 b = 0 := by cases b <;> simp [tmod]
@[simp] theorem mod_zero : a : Int, mod a 0 = a
@[simp] theorem tmod_zero : a : Int, tmod a 0 = a
| ofNat _ => congrArg ofNat <| Nat.mod_zero _
| -[_+1] => congrArg (fun n => -ofNat n) <| Nat.mod_zero _
@@ -224,7 +222,7 @@ theorem ediv_add_emod' (a b : Int) : a / b * b + a % b = a := by
theorem emod_def (a b : Int) : a % b = a - b * (a / b) := by
rw [ Int.add_sub_cancel (a % b), emod_add_ediv]
theorem mod_add_div : a b : Int, mod a b + b * (a.div b) = a
theorem tmod_add_tdiv : a b : Int, tmod a b + b * (a.tdiv b) = a
| ofNat _, ofNat _ => congrArg ofNat (Nat.mod_add_div ..)
| ofNat m, -[n+1] => by
show (m % succ n + -(succ n) * -(m / succ n) : Int) = m
@@ -241,17 +239,17 @@ theorem mod_add_div : ∀ a b : Int, mod a b + b * (a.div b) = a
rw [Int.neg_mul, Int.neg_add]
exact congrArg (-ofNat ·) (Nat.mod_add_div ..)
theorem div_add_mod (a b : Int) : b * a.div b + mod a b = a := by
rw [Int.add_comm]; apply mod_add_div ..
theorem tdiv_add_tmod (a b : Int) : b * a.tdiv b + tmod a b = a := by
rw [Int.add_comm]; apply tmod_add_tdiv ..
theorem mod_add_div' (m k : Int) : mod m k + m.div k * k = m := by
rw [Int.mul_comm]; apply mod_add_div
theorem tmod_add_tdiv' (m k : Int) : tmod m k + m.tdiv k * k = m := by
rw [Int.mul_comm]; apply tmod_add_tdiv
theorem div_add_mod' (m k : Int) : m.div k * k + mod m k = m := by
rw [Int.mul_comm]; apply div_add_mod
theorem tdiv_add_tmod' (m k : Int) : m.tdiv k * k + tmod m k = m := by
rw [Int.mul_comm]; apply tdiv_add_tmod
theorem mod_def (a b : Int) : mod a b = a - b * a.div b := by
rw [ Int.add_sub_cancel (mod a b), mod_add_div]
theorem tmod_def (a b : Int) : tmod a b = a - b * a.tdiv b := by
rw [ Int.add_sub_cancel (tmod a b), tmod_add_tdiv]
theorem fmod_add_fdiv : a b : Int, a.fmod b + b * a.fdiv b = a
| 0, ofNat _ | 0, -[_+1] => congrArg ofNat <| by simp
@@ -281,11 +279,11 @@ theorem fmod_def (a b : Int) : a.fmod b = a - b * a.fdiv b := by
theorem fmod_eq_emod (a : Int) {b : Int} (hb : 0 b) : fmod a b = a % b := by
simp [fmod_def, emod_def, fdiv_eq_ediv _ hb]
theorem mod_eq_emod {a b : Int} (ha : 0 a) (hb : 0 b) : mod a b = a % b := by
simp [emod_def, mod_def, div_eq_ediv ha hb]
theorem tmod_eq_emod {a b : Int} (ha : 0 a) (hb : 0 b) : tmod a b = a % b := by
simp [emod_def, tmod_def, tdiv_eq_ediv ha hb]
theorem fmod_eq_mod {a b : Int} (Ha : 0 a) (Hb : 0 b) : fmod a b = mod a b :=
mod_eq_emod Ha Hb fmod_eq_emod _ Hb
theorem fmod_eq_tmod {a b : Int} (Ha : 0 a) (Hb : 0 b) : fmod a b = tmod a b :=
tmod_eq_emod Ha Hb fmod_eq_emod _ Hb
/-! ### `/` ediv -/
@@ -300,7 +298,7 @@ theorem ediv_neg' {a b : Int} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
protected theorem div_def (a b : Int) : a / b = Int.ediv a b := rfl
theorem negSucc_ediv (m : Nat) {b : Int} (H : 0 < b) : -[m+1] / b = -(div m b + 1) :=
theorem negSucc_ediv (m : Nat) {b : Int} (H : 0 < b) : -[m+1] / b = -(ediv m b + 1) :=
match b, eq_succ_of_zero_lt H with
| _, _, rfl => rfl
@@ -308,6 +306,22 @@ theorem ediv_nonneg {a b : Int} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a / b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_zero_le _
theorem ediv_nonneg_of_nonpos_of_nonpos {a b : Int} (Ha : a 0) (Hb : b 0) : 0 a / b := by
match a, b with
| ofNat a, b =>
match Int.le_antisymm Ha (ofNat_zero_le a) with
| h1 =>
rw [h1, zero_ediv]
exact Int.le_refl 0
| a, ofNat b =>
match Int.le_antisymm Hb (ofNat_zero_le b) with
| h1 =>
rw [h1, Int.ediv_zero]
exact Int.le_refl 0
| negSucc a, negSucc b =>
rw [Int.div_def, ediv]
exact le_add_one (ediv_nonneg (ofNat_zero_le a) (Int.le_trans (ofNat_zero_le b) (le.intro 1 rfl)))
theorem ediv_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a / b 0 :=
Int.nonpos_of_neg_nonneg <| Int.ediv_neg .. Int.ediv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
@@ -357,6 +371,7 @@ theorem add_ediv_of_dvd_left {a b c : Int} (H : c a) : (a + b) / c = a / c +
@[simp] theorem mul_ediv_cancel_left (b : Int) (H : a 0) : (a * b) / a = b :=
Int.mul_comm .. Int.mul_ediv_cancel _ H
theorem div_nonneg_iff_of_pos {a b : Int} (h : 0 < b) : a / b 0 a 0 := by
rw [Int.div_def]
match b, h with
@@ -454,6 +469,12 @@ theorem lt_mul_ediv_self_add {x k : Int} (h : 0 < k) : x < k * (x / k) + k :=
@[simp] theorem add_mul_emod_self_left (a b c : Int) : (a + b * c) % b = a % b := by
rw [Int.mul_comm, Int.add_mul_emod_self]
@[simp] theorem add_neg_mul_emod_self {a b c : Int} : (a + -(b * c)) % c = a % c := by
rw [Int.neg_mul_eq_neg_mul, add_mul_emod_self]
@[simp] theorem add_neg_mul_emod_self_left {a b c : Int} : (a + -(b * c)) % b = a % b := by
rw [Int.neg_mul_eq_mul_neg, add_mul_emod_self_left]
@[simp] theorem add_emod_self {a b : Int} : (a + b) % b = a % b := by
have := add_mul_emod_self_left a b 1; rwa [Int.mul_one] at this
@@ -498,9 +519,12 @@ theorem mul_emod (a b n : Int) : (a * b) % n = (a % n) * (b % n) % n := by
Int.mul_assoc, Int.mul_assoc, Int.mul_add n _ _, add_mul_emod_self_left,
Int.mul_assoc, add_mul_emod_self]
@[local simp] theorem emod_self {a : Int} : a % a = 0 := by
@[simp] theorem emod_self {a : Int} : a % a = 0 := by
have := mul_emod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem neg_emod_self (a : Int) : -a % a = 0 := by
rw [neg_emod, Int.sub_self, zero_emod]
@[simp] theorem emod_emod_of_dvd (n : Int) {m k : Int}
(h : m k) : (n % k) % m = n % m := by
conv => rhs; rw [ emod_add_ediv n k]
@@ -593,9 +617,17 @@ theorem dvd_emod_sub_self {x : Int} {m : Nat} : (m : Int) x % m - x := by
theorem emod_eq_zero_of_dvd : {a b : Int}, a b b % a = 0
| _, _, _, rfl => mul_emod_right ..
theorem dvd_iff_emod_eq_zero (a b : Int) : a b b % a = 0 :=
theorem dvd_iff_emod_eq_zero {a b : Int} : a b b % a = 0 :=
emod_eq_zero_of_dvd, dvd_of_emod_eq_zero
@[simp] theorem neg_mul_emod_left (a b : Int) : -(a * b) % b = 0 := by
rw [ dvd_iff_emod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_left a b
@[simp] theorem neg_mul_emod_right (a b : Int) : -(a * b) % a = 0 := by
rw [ dvd_iff_emod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_right a b
instance decidableDvd : DecidableRel (α := Int) (· ·) := fun _ _ =>
decidable_of_decidable_of_iff (dvd_iff_emod_eq_zero ..).symm
@@ -620,6 +652,12 @@ theorem neg_ediv_of_dvd : ∀ {a b : Int}, b a → (-a) / b = -(a / b)
· simp [bz]
· rw [Int.neg_mul_eq_mul_neg, Int.mul_ediv_cancel_left _ bz, Int.mul_ediv_cancel_left _ bz]
@[simp] theorem neg_mul_ediv_cancel (a b : Int) (h : b 0) : -(a * b) / b = -a := by
rw [neg_ediv_of_dvd (Int.dvd_mul_left a b), mul_ediv_cancel _ h]
@[simp] theorem neg_mul_ediv_cancel_left (a b : Int) (h : a 0) : -(a * b) / a = -b := by
rw [neg_ediv_of_dvd (Int.dvd_mul_right a b), mul_ediv_cancel_left _ h]
theorem sub_ediv_of_dvd (a : Int) {b c : Int}
(hcb : c b) : (a - b) / c = a / c - b / c := by
rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg, Int.add_ediv_of_dvd_right (Int.dvd_neg.2 hcb)]
@@ -635,13 +673,22 @@ theorem sub_ediv_of_dvd (a : Int) {b c : Int}
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp] protected theorem neg_ediv_self (a : Int) (h : a 0) : (-a) / a = -1 := by
rw [neg_ediv_of_dvd (Int.dvd_refl a), Int.ediv_self h]
@[simp]
theorem Int.emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
theorem emod_sub_cancel (x y : Int): (x - y) % y = x % y := by
by_cases h : y = 0
· simp [h]
· simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
@[simp] theorem add_neg_emod_self (a b : Int) : (a + -b) % b = a % b := by
rw [ Int.sub_eq_add_neg, emod_sub_cancel]
@[simp] theorem neg_add_emod_self (a b : Int) : (-a + b) % a = b % a := by
rw [Int.add_comm, add_neg_emod_self]
/-- If `a % b = c` then `b` divides `a - c`. -/
theorem dvd_sub_of_emod_eq {a b c : Int} (h : a % b = c) : b a - c := by
have hx : (a % b) % b = c % b := by
@@ -754,7 +801,7 @@ protected theorem lt_ediv_of_mul_lt {a b c : Int} (H1 : 0 ≤ b) (H2 : b c)
a < c / b :=
Int.lt_of_not_ge <| mt (Int.le_mul_of_ediv_le H1 H2) (Int.not_le_of_gt H3)
protected theorem lt_ediv_iff_mul_lt {a b : Int} (c : Int) (H : 0 < c) (H' : c b) :
protected theorem lt_ediv_iff_mul_lt {a b : Int} {c : Int} (H : 0 < c) (H' : c b) :
a < b / c a * c < b :=
Int.mul_lt_of_lt_ediv H, Int.lt_ediv_of_mul_lt (Int.le_of_lt H) H'
@@ -766,179 +813,191 @@ theorem ediv_eq_ediv_of_mul_eq_mul {a b c d : Int}
Int.ediv_eq_of_eq_mul_right H3 <| by
rw [ Int.mul_ediv_assoc _ H2]; exact (Int.ediv_eq_of_eq_mul_left H4 H5.symm).symm
/-! ### div -/
/-! ### tdiv -/
@[simp] protected theorem div_one : a : Int, a.div 1 = a
@[simp] protected theorem tdiv_one : a : Int, a.tdiv 1 = a
| (n:Nat) => congrArg ofNat (Nat.div_one _)
| -[n+1] => by simp [Int.div, neg_ofNat_succ]; rfl
| -[n+1] => by simp [Int.tdiv, neg_ofNat_succ]; rfl
unseal Nat.div in
@[simp] protected theorem div_neg : a b : Int, a.div (-b) = -(a.div b)
@[simp] protected theorem tdiv_neg : a b : Int, a.tdiv (-b) = -(a.tdiv b)
| ofNat m, 0 => show ofNat (m / 0) = -(m / 0) by rw [Nat.div_zero]; rfl
| ofNat m, -[n+1] | -[m+1], succ n => (Int.neg_neg _).symm
| ofNat m, succ n | -[m+1], 0 | -[m+1], -[n+1] => rfl
unseal Nat.div in
@[simp] protected theorem neg_div : a b : Int, (-a).div b = -(a.div b)
@[simp] protected theorem neg_tdiv : a b : Int, (-a).tdiv b = -(a.tdiv b)
| 0, n => by simp [Int.neg_zero]
| succ m, (n:Nat) | -[m+1], 0 | -[m+1], -[n+1] => rfl
| succ m, -[n+1] | -[m+1], succ n => (Int.neg_neg _).symm
protected theorem neg_div_neg (a b : Int) : (-a).div (-b) = a.div b := by
simp [Int.div_neg, Int.neg_div, Int.neg_neg]
protected theorem neg_tdiv_neg (a b : Int) : (-a).tdiv (-b) = a.tdiv b := by
simp [Int.tdiv_neg, Int.neg_tdiv, Int.neg_neg]
protected theorem div_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.div b :=
protected theorem tdiv_nonneg {a b : Int} (Ha : 0 a) (Hb : 0 b) : 0 a.tdiv b :=
match a, b, eq_ofNat_of_zero_le Ha, eq_ofNat_of_zero_le Hb with
| _, _, _, rfl, _, rfl => ofNat_zero_le _
protected theorem div_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a.div b 0 :=
Int.nonpos_of_neg_nonneg <| Int.div_neg .. Int.div_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
protected theorem tdiv_nonpos {a b : Int} (Ha : 0 a) (Hb : b 0) : a.tdiv b 0 :=
Int.nonpos_of_neg_nonneg <| Int.tdiv_neg .. Int.tdiv_nonneg Ha (Int.neg_nonneg_of_nonpos Hb)
theorem div_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.div b = 0 :=
theorem tdiv_eq_zero_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : a.tdiv b = 0 :=
match a, b, eq_ofNat_of_zero_le H1, eq_succ_of_zero_lt (Int.lt_of_le_of_lt H1 H2) with
| _, _, _, rfl, _, rfl => congrArg Nat.cast <| Nat.div_eq_of_lt <| ofNat_lt.1 H2
@[simp] protected theorem mul_div_cancel (a : Int) {b : Int} (H : b 0) : (a * b).div b = a :=
have : {a b : Nat}, (b : Int) 0 (div (a * b) b : Int) = a := fun H => by
rw [ ofNat_mul, ofNat_div,
@[simp] protected theorem mul_tdiv_cancel (a : Int) {b : Int} (H : b 0) : (a * b).tdiv b = a :=
have : {a b : Nat}, (b : Int) 0 (tdiv (a * b) b : Int) = a := fun H => by
rw [ ofNat_mul, ofNat_tdiv,
Nat.mul_div_cancel _ <| Nat.pos_of_ne_zero <| Int.ofNat_ne_zero.1 H]
match a, b, a.eq_nat_or_neg, b.eq_nat_or_neg with
| _, _, a, .inl rfl, b, .inl rfl => this H
| _, _, a, .inl rfl, b, .inr rfl => by
rw [Int.mul_neg, Int.neg_div, Int.div_neg, Int.neg_neg,
rw [Int.mul_neg, Int.neg_tdiv, Int.tdiv_neg, Int.neg_neg,
this (Int.neg_ne_zero.1 H)]
| _, _, a, .inr rfl, b, .inl rfl => by rw [Int.neg_mul, Int.neg_div, this H]
| _, _, a, .inr rfl, b, .inl rfl => by rw [Int.neg_mul, Int.neg_tdiv, this H]
| _, _, a, .inr rfl, b, .inr rfl => by
rw [Int.neg_mul_neg, Int.div_neg, this (Int.neg_ne_zero.1 H)]
rw [Int.neg_mul_neg, Int.tdiv_neg, this (Int.neg_ne_zero.1 H)]
@[simp] protected theorem mul_div_cancel_left (b : Int) (H : a 0) : (a * b).div a = b :=
Int.mul_comm .. Int.mul_div_cancel _ H
@[simp] protected theorem mul_tdiv_cancel_left (b : Int) (H : a 0) : (a * b).tdiv a = b :=
Int.mul_comm .. Int.mul_tdiv_cancel _ H
@[simp] protected theorem div_self {a : Int} (H : a 0) : a.div a = 1 := by
have := Int.mul_div_cancel 1 H; rwa [Int.one_mul] at this
@[simp] protected theorem tdiv_self {a : Int} (H : a 0) : a.tdiv a = 1 := by
have := Int.mul_tdiv_cancel 1 H; rwa [Int.one_mul] at this
theorem mul_div_cancel_of_mod_eq_zero {a b : Int} (H : a.mod b = 0) : b * (a.div b) = a := by
have := mod_add_div a b; rwa [H, Int.zero_add] at this
theorem mul_tdiv_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : b * (a.tdiv b) = a := by
have := tmod_add_tdiv a b; rwa [H, Int.zero_add] at this
theorem div_mul_cancel_of_mod_eq_zero {a b : Int} (H : a.mod b = 0) : a.div b * b = a := by
rw [Int.mul_comm, mul_div_cancel_of_mod_eq_zero H]
theorem tdiv_mul_cancel_of_tmod_eq_zero {a b : Int} (H : a.tmod b = 0) : a.tdiv b * b = a := by
rw [Int.mul_comm, mul_tdiv_cancel_of_tmod_eq_zero H]
theorem dvd_of_mod_eq_zero {a b : Int} (H : mod b a = 0) : a b :=
b.div a, (mul_div_cancel_of_mod_eq_zero H).symm
theorem dvd_of_tmod_eq_zero {a b : Int} (H : tmod b a = 0) : a b :=
b.tdiv a, (mul_tdiv_cancel_of_tmod_eq_zero H).symm
protected theorem mul_div_assoc (a : Int) : {b c : Int}, c b (a * b).div c = a * (b.div c)
protected theorem mul_tdiv_assoc (a : Int) : {b c : Int}, c b (a * b).tdiv c = a * (b.tdiv c)
| _, c, d, rfl =>
if cz : c = 0 then by simp [cz, Int.mul_zero] else by
rw [Int.mul_left_comm, Int.mul_div_cancel_left _ cz, Int.mul_div_cancel_left _ cz]
rw [Int.mul_left_comm, Int.mul_tdiv_cancel_left _ cz, Int.mul_tdiv_cancel_left _ cz]
protected theorem mul_div_assoc' (b : Int) {a c : Int} (h : c a) :
(a * b).div c = a.div c * b := by
rw [Int.mul_comm, Int.mul_div_assoc _ h, Int.mul_comm]
protected theorem mul_tdiv_assoc' (b : Int) {a c : Int} (h : c a) :
(a * b).tdiv c = a.tdiv c * b := by
rw [Int.mul_comm, Int.mul_tdiv_assoc _ h, Int.mul_comm]
theorem div_dvd_div : {a b c : Int}, a b b c b.div a c.div a
theorem tdiv_dvd_tdiv : {a b c : Int}, a b b c b.tdiv a c.tdiv a
| a, _, _, b, rfl, c, rfl => by
by_cases az : a = 0
· simp [az]
· rw [Int.mul_div_cancel_left _ az, Int.mul_assoc, Int.mul_div_cancel_left _ az]
· rw [Int.mul_tdiv_cancel_left _ az, Int.mul_assoc, Int.mul_tdiv_cancel_left _ az]
apply Int.dvd_mul_right
@[simp] theorem natAbs_div (a b : Int) : natAbs (a.div b) = (natAbs a).div (natAbs b) :=
@[simp] theorem natAbs_tdiv (a b : Int) : natAbs (a.tdiv b) = (natAbs a).div (natAbs b) :=
match a, b, eq_nat_or_neg a, eq_nat_or_neg b with
| _, _, _, .inl rfl, _, .inl rfl => rfl
| _, _, _, .inl rfl, _, .inr rfl => by rw [Int.div_neg, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inl rfl => by rw [Int.neg_div, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inr rfl => by rw [Int.neg_div_neg, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inl rfl, _, .inr rfl => by rw [Int.tdiv_neg, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inl rfl => by rw [Int.neg_tdiv, natAbs_neg, natAbs_neg]; rfl
| _, _, _, .inr rfl, _, .inr rfl => by rw [Int.neg_tdiv_neg, natAbs_neg, natAbs_neg]; rfl
protected theorem div_eq_of_eq_mul_right {a b c : Int}
(H1 : b 0) (H2 : a = b * c) : a.div b = c := by rw [H2, Int.mul_div_cancel_left _ H1]
protected theorem tdiv_eq_of_eq_mul_right {a b c : Int}
(H1 : b 0) (H2 : a = b * c) : a.tdiv b = c := by rw [H2, Int.mul_tdiv_cancel_left _ H1]
protected theorem eq_div_of_mul_eq_right {a b c : Int}
(H1 : a 0) (H2 : a * b = c) : b = c.div a :=
(Int.div_eq_of_eq_mul_right H1 H2.symm).symm
protected theorem eq_tdiv_of_mul_eq_right {a b c : Int}
(H1 : a 0) (H2 : a * b = c) : b = c.tdiv a :=
(Int.tdiv_eq_of_eq_mul_right H1 H2.symm).symm
/-! ### (t-)mod -/
theorem ofNat_mod (m n : Nat) : ((m % n) : Int) = mod m n := rfl
theorem ofNat_tmod (m n : Nat) : ((m % n) : Int) = tmod m n := rfl
@[simp] theorem mod_one (a : Int) : mod a 1 = 0 := by
simp [mod_def, Int.div_one, Int.one_mul, Int.sub_self]
@[simp] theorem tmod_one (a : Int) : tmod a 1 = 0 := by
simp [tmod_def, Int.tdiv_one, Int.one_mul, Int.sub_self]
theorem mod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : mod a b = a := by
rw [mod_eq_emod H1 (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
theorem tmod_eq_of_lt {a b : Int} (H1 : 0 a) (H2 : a < b) : tmod a b = a := by
rw [tmod_eq_emod H1 (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
theorem mod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : mod a b < b :=
theorem tmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : tmod a b < b :=
match a, b, eq_succ_of_zero_lt H with
| ofNat _, _, n, rfl => ofNat_lt.2 <| Nat.mod_lt _ n.succ_pos
| -[_+1], _, n, rfl => Int.lt_of_le_of_lt
(Int.neg_nonpos_of_nonneg <| Int.ofNat_nonneg _) (ofNat_pos.2 n.succ_pos)
theorem mod_nonneg : {a : Int} (b : Int), 0 a 0 mod a b
theorem tmod_nonneg : {a : Int} (b : Int), 0 a 0 tmod a b
| ofNat _, -[_+1], _ | ofNat _, ofNat _, _ => ofNat_nonneg _
@[simp] theorem mod_neg (a b : Int) : mod a (-b) = mod a b := by
rw [mod_def, mod_def, Int.div_neg, Int.neg_mul_neg]
@[simp] theorem tmod_neg (a b : Int) : tmod a (-b) = tmod a b := by
rw [tmod_def, tmod_def, Int.tdiv_neg, Int.neg_mul_neg]
@[simp] theorem mul_mod_left (a b : Int) : (a * b).mod b = 0 :=
@[simp] theorem mul_tmod_left (a b : Int) : (a * b).tmod b = 0 :=
if h : b = 0 then by simp [h, Int.mul_zero] else by
rw [Int.mod_def, Int.mul_div_cancel _ h, Int.mul_comm, Int.sub_self]
rw [Int.tmod_def, Int.mul_tdiv_cancel _ h, Int.mul_comm, Int.sub_self]
@[simp] theorem mul_mod_right (a b : Int) : (a * b).mod a = 0 := by
rw [Int.mul_comm, mul_mod_left]
@[simp] theorem mul_tmod_right (a b : Int) : (a * b).tmod a = 0 := by
rw [Int.mul_comm, mul_tmod_left]
theorem mod_eq_zero_of_dvd : {a b : Int}, a b mod b a = 0
| _, _, _, rfl => mul_mod_right ..
theorem tmod_eq_zero_of_dvd : {a b : Int}, a b tmod b a = 0
| _, _, _, rfl => mul_tmod_right ..
theorem dvd_iff_mod_eq_zero (a b : Int) : a b mod b a = 0 :=
mod_eq_zero_of_dvd, dvd_of_mod_eq_zero
theorem dvd_iff_tmod_eq_zero {a b : Int} : a b tmod b a = 0 :=
tmod_eq_zero_of_dvd, dvd_of_tmod_eq_zero
protected theorem div_mul_cancel {a b : Int} (H : b a) : a.div b * b = a :=
div_mul_cancel_of_mod_eq_zero (mod_eq_zero_of_dvd H)
@[simp] theorem neg_mul_tmod_right (a b : Int) : (-(a * b)).tmod a = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_right a b
protected theorem mul_div_cancel' {a b : Int} (H : a b) : a * b.div a = b := by
rw [Int.mul_comm, Int.div_mul_cancel H]
@[simp] theorem neg_mul_tmod_left (a b : Int) : (-(a * b)).tmod b = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_mul_left a b
protected theorem eq_mul_of_div_eq_right {a b c : Int}
(H1 : b a) (H2 : a.div b = c) : a = b * c := by rw [ H2, Int.mul_div_cancel' H1]
protected theorem tdiv_mul_cancel {a b : Int} (H : b a) : a.tdiv b * b = a :=
tdiv_mul_cancel_of_tmod_eq_zero (tmod_eq_zero_of_dvd H)
@[simp] theorem mod_self {a : Int} : a.mod a = 0 := by
have := mul_mod_left 1 a; rwa [Int.one_mul] at this
protected theorem mul_tdiv_cancel' {a b : Int} (H : a b) : a * b.tdiv a = b := by
rw [Int.mul_comm, Int.tdiv_mul_cancel H]
theorem lt_div_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.div b + 1) * b := by
protected theorem eq_mul_of_tdiv_eq_right {a b c : Int}
(H1 : b a) (H2 : a.tdiv b = c) : a = b * c := by rw [ H2, Int.mul_tdiv_cancel' H1]
@[simp] theorem tmod_self {a : Int} : a.tmod a = 0 := by
have := mul_tmod_left 1 a; rwa [Int.one_mul] at this
@[simp] theorem neg_tmod_self (a : Int) : (-a).tmod a = 0 := by
rw [ dvd_iff_tmod_eq_zero, Int.dvd_neg]
exact Int.dvd_refl a
theorem lt_tdiv_add_one_mul_self (a : Int) {b : Int} (H : 0 < b) : a < (a.tdiv b + 1) * b := by
rw [Int.add_mul, Int.one_mul, Int.mul_comm]
exact Int.lt_add_of_sub_left_lt <| Int.mod_def .. mod_lt_of_pos _ H
exact Int.lt_add_of_sub_left_lt <| Int.tmod_def .. tmod_lt_of_pos _ H
protected theorem div_eq_iff_eq_mul_right {a b c : Int}
(H : b 0) (H' : b a) : a.div b = c a = b * c :=
Int.eq_mul_of_div_eq_right H', Int.div_eq_of_eq_mul_right H
protected theorem tdiv_eq_iff_eq_mul_right {a b c : Int}
(H : b 0) (H' : b a) : a.tdiv b = c a = b * c :=
Int.eq_mul_of_tdiv_eq_right H', Int.tdiv_eq_of_eq_mul_right H
protected theorem div_eq_iff_eq_mul_left {a b c : Int}
(H : b 0) (H' : b a) : a.div b = c a = c * b := by
rw [Int.mul_comm]; exact Int.div_eq_iff_eq_mul_right H H'
protected theorem tdiv_eq_iff_eq_mul_left {a b c : Int}
(H : b 0) (H' : b a) : a.tdiv b = c a = c * b := by
rw [Int.mul_comm]; exact Int.tdiv_eq_iff_eq_mul_right H H'
protected theorem eq_mul_of_div_eq_left {a b c : Int}
(H1 : b a) (H2 : a.div b = c) : a = c * b := by
rw [Int.mul_comm, Int.eq_mul_of_div_eq_right H1 H2]
protected theorem eq_mul_of_tdiv_eq_left {a b c : Int}
(H1 : b a) (H2 : a.tdiv b = c) : a = c * b := by
rw [Int.mul_comm, Int.eq_mul_of_tdiv_eq_right H1 H2]
protected theorem div_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a.div b = c :=
Int.div_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
protected theorem tdiv_eq_of_eq_mul_left {a b c : Int}
(H1 : b 0) (H2 : a = c * b) : a.tdiv b = c :=
Int.tdiv_eq_of_eq_mul_right H1 (by rw [Int.mul_comm, H2])
protected theorem eq_zero_of_div_eq_zero {d n : Int} (h : d n) (H : n.div d = 0) : n = 0 := by
rw [ Int.mul_div_cancel' h, H, Int.mul_zero]
protected theorem eq_zero_of_tdiv_eq_zero {d n : Int} (h : d n) (H : n.tdiv d = 0) : n = 0 := by
rw [ Int.mul_tdiv_cancel' h, H, Int.mul_zero]
@[simp] protected theorem div_left_inj {a b d : Int}
(hda : d a) (hdb : d b) : a.div d = b.div d a = b := by
refine fun h => ?_, congrArg (div · d)
rw [ Int.mul_div_cancel' hda, Int.mul_div_cancel' hdb, h]
@[simp] protected theorem tdiv_left_inj {a b d : Int}
(hda : d a) (hdb : d b) : a.tdiv d = b.tdiv d a = b := by
refine fun h => ?_, congrArg (tdiv · d)
rw [ Int.mul_tdiv_cancel' hda, Int.mul_tdiv_cancel' hdb, h]
theorem div_sign : a b, a.div (sign b) = a * sign b
theorem tdiv_sign : a b, a.tdiv (sign b) = a * sign b
| _, succ _ => by simp [sign, Int.mul_one]
| _, 0 => by simp [sign, Int.mul_zero]
| _, -[_+1] => by simp [sign, Int.mul_neg, Int.mul_one]
protected theorem sign_eq_div_abs (a : Int) : sign a = a.div (natAbs a) :=
protected theorem sign_eq_tdiv_abs (a : Int) : sign a = a.tdiv (natAbs a) :=
if az : a = 0 then by simp [az] else
(Int.div_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(Int.tdiv_eq_of_eq_mul_left (ofNat_ne_zero.2 <| natAbs_ne_zero.2 az)
(sign_mul_natAbs _).symm).symm
/-! ### fdiv -/
@@ -991,7 +1050,7 @@ theorem fmod_eq_of_lt {a b : Int} (H1 : 0 ≤ a) (H2 : a < b) : a.fmod b = a :=
rw [fmod_eq_emod _ (Int.le_trans H1 (Int.le_of_lt H2)), emod_eq_of_lt H1 H2]
theorem fmod_nonneg {a b : Int} (ha : 0 a) (hb : 0 b) : 0 a.fmod b :=
fmod_eq_mod ha hb mod_nonneg _ ha
fmod_eq_tmod ha hb tmod_nonneg _ ha
theorem fmod_nonneg' (a : Int) {b : Int} (hb : 0 < b) : 0 a.fmod b :=
fmod_eq_emod _ (Int.le_of_lt hb) emod_nonneg _ (Int.ne_of_lt hb).symm
@@ -1011,10 +1070,10 @@ theorem fmod_lt_of_pos (a : Int) {b : Int} (H : 0 < b) : a.fmod b < b :=
/-! ### Theorems crossing div/mod versions -/
theorem div_eq_ediv_of_dvd {a b : Int} (h : b a) : a.div b = a / b := by
theorem tdiv_eq_ediv_of_dvd {a b : Int} (h : b a) : a.tdiv b = a / b := by
by_cases b0 : b = 0
· simp [b0]
· rw [Int.div_eq_iff_eq_mul_left b0 h, Int.ediv_eq_iff_eq_mul_left b0 h]
· rw [Int.tdiv_eq_iff_eq_mul_left b0 h, Int.ediv_eq_iff_eq_mul_left b0 h]
theorem fdiv_eq_ediv_of_dvd : {a b : Int}, b a a.fdiv b = a / b
| _, b, c, rfl => by
@@ -1091,8 +1150,7 @@ theorem bmod_mul_bmod : Int.bmod (Int.bmod x n * y) n = Int.bmod (x * y) n := by
next p =>
simp
next p =>
rw [Int.sub_mul, Int.sub_eq_add_neg, Int.mul_neg]
simp
rw [Int.sub_mul, Int.sub_eq_add_neg, Int.mul_neg, bmod_add_mul_cancel, emod_mul_bmod_congr]
@[simp] theorem mul_bmod_bmod : Int.bmod (x * Int.bmod y n) n = Int.bmod (x * y) n := by
rw [Int.mul_comm x, bmod_mul_bmod, Int.mul_comm x]
@@ -1109,7 +1167,7 @@ theorem emod_bmod {x : Int} {m : Nat} : bmod (x % m) m = bmod x m := by
@[simp] theorem bmod_zero : Int.bmod 0 m = 0 := by
dsimp [bmod]
simp only [zero_emod, Int.zero_sub, ite_eq_left_iff, Int.neg_eq_zero]
simp only [Int.zero_sub, ite_eq_left_iff, Int.neg_eq_zero]
intro h
rw [@Int.not_lt] at h
match m with
@@ -1227,3 +1285,65 @@ theorem bmod_natAbs_plus_one (x : Int) (w : 1 < x.natAbs) : bmod x (x.natAbs + 1
all_goals decide
· exact ofNat_nonneg x
· exact succ_ofNat_pos (x + 1)
/-! ### Deprecations -/
@[deprecated Int.zero_tdiv (since := "2024-09-11")] protected abbrev zero_div := @Int.zero_tdiv
@[deprecated Int.tdiv_zero (since := "2024-09-11")] protected abbrev div_zero := @Int.tdiv_zero
@[deprecated tdiv_eq_ediv (since := "2024-09-11")] abbrev div_eq_ediv := @tdiv_eq_ediv
@[deprecated fdiv_eq_tdiv (since := "2024-09-11")] abbrev fdiv_eq_div := @fdiv_eq_tdiv
@[deprecated zero_tmod (since := "2024-09-11")] abbrev zero_mod := @zero_tmod
@[deprecated tmod_zero (since := "2024-09-11")] abbrev mod_zero := @tmod_zero
@[deprecated tmod_add_tdiv (since := "2024-09-11")] abbrev mod_add_div := @tmod_add_tdiv
@[deprecated tdiv_add_tmod (since := "2024-09-11")] abbrev div_add_mod := @tdiv_add_tmod
@[deprecated tmod_add_tdiv' (since := "2024-09-11")] abbrev mod_add_div' := @tmod_add_tdiv'
@[deprecated tdiv_add_tmod' (since := "2024-09-11")] abbrev div_add_mod' := @tdiv_add_tmod'
@[deprecated tmod_def (since := "2024-09-11")] abbrev mod_def := @tmod_def
@[deprecated tmod_eq_emod (since := "2024-09-11")] abbrev mod_eq_emod := @tmod_eq_emod
@[deprecated fmod_eq_tmod (since := "2024-09-11")] abbrev fmod_eq_mod := @fmod_eq_tmod
@[deprecated Int.tdiv_one (since := "2024-09-11")] protected abbrev div_one := @Int.tdiv_one
@[deprecated Int.tdiv_neg (since := "2024-09-11")] protected abbrev div_neg := @Int.tdiv_neg
@[deprecated Int.neg_tdiv (since := "2024-09-11")] protected abbrev neg_div := @Int.neg_tdiv
@[deprecated Int.neg_tdiv_neg (since := "2024-09-11")] protected abbrev neg_div_neg := @Int.neg_tdiv_neg
@[deprecated Int.tdiv_nonneg (since := "2024-09-11")] protected abbrev div_nonneg := @Int.tdiv_nonneg
@[deprecated Int.tdiv_nonpos (since := "2024-09-11")] protected abbrev div_nonpos := @Int.tdiv_nonpos
@[deprecated Int.tdiv_eq_zero_of_lt (since := "2024-09-11")] abbrev div_eq_zero_of_lt := @Int.tdiv_eq_zero_of_lt
@[deprecated Int.mul_tdiv_cancel (since := "2024-09-11")] protected abbrev mul_div_cancel := @Int.mul_tdiv_cancel
@[deprecated Int.mul_tdiv_cancel_left (since := "2024-09-11")] protected abbrev mul_div_cancel_left := @Int.mul_tdiv_cancel_left
@[deprecated Int.tdiv_self (since := "2024-09-11")] protected abbrev div_self := @Int.tdiv_self
@[deprecated Int.mul_tdiv_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev mul_div_cancel_of_mod_eq_zero := @Int.mul_tdiv_cancel_of_tmod_eq_zero
@[deprecated Int.tdiv_mul_cancel_of_tmod_eq_zero (since := "2024-09-11")] abbrev div_mul_cancel_of_mod_eq_zero := @Int.tdiv_mul_cancel_of_tmod_eq_zero
@[deprecated Int.dvd_of_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_of_mod_eq_zero := @Int.dvd_of_tmod_eq_zero
@[deprecated Int.mul_tdiv_assoc (since := "2024-09-11")] protected abbrev mul_div_assoc := @Int.mul_tdiv_assoc
@[deprecated Int.mul_tdiv_assoc' (since := "2024-09-11")] protected abbrev mul_div_assoc' := @Int.mul_tdiv_assoc'
@[deprecated Int.tdiv_dvd_tdiv (since := "2024-09-11")] abbrev div_dvd_div := @Int.tdiv_dvd_tdiv
@[deprecated Int.natAbs_tdiv (since := "2024-09-11")] abbrev natAbs_div := @Int.natAbs_tdiv
@[deprecated Int.tdiv_eq_of_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_right := @Int.tdiv_eq_of_eq_mul_right
@[deprecated Int.eq_tdiv_of_mul_eq_right (since := "2024-09-11")] protected abbrev eq_div_of_mul_eq_right := @Int.eq_tdiv_of_mul_eq_right
@[deprecated Int.ofNat_tmod (since := "2024-09-11")] abbrev ofNat_mod := @Int.ofNat_tmod
@[deprecated Int.tmod_one (since := "2024-09-11")] abbrev mod_one := @Int.tmod_one
@[deprecated Int.tmod_eq_of_lt (since := "2024-09-11")] abbrev mod_eq_of_lt := @Int.tmod_eq_of_lt
@[deprecated Int.tmod_lt_of_pos (since := "2024-09-11")] abbrev mod_lt_of_pos := @Int.tmod_lt_of_pos
@[deprecated Int.tmod_nonneg (since := "2024-09-11")] abbrev mod_nonneg := @Int.tmod_nonneg
@[deprecated Int.tmod_neg (since := "2024-09-11")] abbrev mod_neg := @Int.tmod_neg
@[deprecated Int.mul_tmod_left (since := "2024-09-11")] abbrev mul_mod_left := @Int.mul_tmod_left
@[deprecated Int.mul_tmod_right (since := "2024-09-11")] abbrev mul_mod_right := @Int.mul_tmod_right
@[deprecated Int.tmod_eq_zero_of_dvd (since := "2024-09-11")] abbrev mod_eq_zero_of_dvd := @Int.tmod_eq_zero_of_dvd
@[deprecated Int.dvd_iff_tmod_eq_zero (since := "2024-09-11")] abbrev dvd_iff_mod_eq_zero := @Int.dvd_iff_tmod_eq_zero
@[deprecated Int.neg_mul_tmod_right (since := "2024-09-11")] abbrev neg_mul_mod_right := @Int.neg_mul_tmod_right
@[deprecated Int.neg_mul_tmod_left (since := "2024-09-11")] abbrev neg_mul_mod_left := @Int.neg_mul_tmod_left
@[deprecated Int.tdiv_mul_cancel (since := "2024-09-11")] protected abbrev div_mul_cancel := @Int.tdiv_mul_cancel
@[deprecated Int.mul_tdiv_cancel' (since := "2024-09-11")] protected abbrev mul_div_cancel' := @Int.mul_tdiv_cancel'
@[deprecated Int.eq_mul_of_tdiv_eq_right (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_right := @Int.eq_mul_of_tdiv_eq_right
@[deprecated Int.tmod_self (since := "2024-09-11")] abbrev mod_self := @Int.tmod_self
@[deprecated Int.neg_tmod_self (since := "2024-09-11")] abbrev neg_mod_self := @Int.neg_tmod_self
@[deprecated Int.lt_tdiv_add_one_mul_self (since := "2024-09-11")] abbrev lt_div_add_one_mul_self := @Int.lt_tdiv_add_one_mul_self
@[deprecated Int.tdiv_eq_iff_eq_mul_right (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_right := @Int.tdiv_eq_iff_eq_mul_right
@[deprecated Int.tdiv_eq_iff_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_iff_eq_mul_left := @Int.tdiv_eq_iff_eq_mul_left
@[deprecated Int.eq_mul_of_tdiv_eq_left (since := "2024-09-11")] protected abbrev eq_mul_of_div_eq_left := @Int.eq_mul_of_tdiv_eq_left
@[deprecated Int.tdiv_eq_of_eq_mul_left (since := "2024-09-11")] protected abbrev div_eq_of_eq_mul_left := @Int.tdiv_eq_of_eq_mul_left
@[deprecated Int.eq_zero_of_tdiv_eq_zero (since := "2024-09-11")] protected abbrev eq_zero_of_div_eq_zero := @Int.eq_zero_of_tdiv_eq_zero
@[deprecated Int.tdiv_left_inj (since := "2024-09-11")] protected abbrev div_left_inj := @Int.tdiv_left_inj
@[deprecated Int.tdiv_sign (since := "2024-09-11")] abbrev div_sign := @Int.tdiv_sign
@[deprecated Int.sign_eq_tdiv_abs (since := "2024-09-11")] protected abbrev sign_eq_div_abs := @Int.sign_eq_tdiv_abs
@[deprecated Int.tdiv_eq_ediv_of_dvd (since := "2024-09-11")] abbrev div_eq_ediv_of_dvd := @Int.tdiv_eq_ediv_of_dvd

View File

@@ -7,6 +7,7 @@ prelude
import Init.Data.Int.Basic
import Init.Conv
import Init.NotationExtra
import Init.PropLemmas
namespace Int
@@ -288,7 +289,7 @@ protected theorem neg_sub (a b : Int) : -(a - b) = b - a := by
protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
simp [Int.sub_eq_add_neg, Int.add_assoc]
protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
@[simp] protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_add_neg]
@[simp] protected theorem sub_add_cancel (a b : Int) : a - b + b = a :=
Int.neg_add_cancel_right a b
@@ -328,22 +329,22 @@ theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
/- ## add/sub injectivity -/
@[simp]
protected theorem add_right_inj (i j k : Int) : (i + k = j + k) i = j := by
protected theorem add_right_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by
apply Iff.intro
· intro p
rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p]
· exact congrArg (· + k)
@[simp]
protected theorem add_left_inj (i j k : Int) : (k + i = k + j) i = j := by
protected theorem add_left_inj {i j : Int} (k : Int) : (k + i = k + j) i = j := by
simp [Int.add_comm k]
@[simp]
protected theorem sub_left_inj (i j k : Int) : (k - i = k - j) i = j := by
protected theorem sub_left_inj {i j : Int} (k : Int) : (k - i = k - j) i = j := by
simp [Int.sub_eq_add_neg, Int.neg_inj]
@[simp]
protected theorem sub_right_inj (i j k : Int) : (i - k = j - k) i = j := by
protected theorem sub_right_inj {i j : Int} (k : Int) : (i - k = j - k) i = j := by
simp [Int.sub_eq_add_neg]
/- ## Ring properties -/
@@ -444,10 +445,10 @@ protected theorem neg_mul_eq_neg_mul (a b : Int) : -(a * b) = -a * b :=
protected theorem neg_mul_eq_mul_neg (a b : Int) : -(a * b) = a * -b :=
Int.neg_eq_of_add_eq_zero <| by rw [ Int.mul_add, Int.add_right_neg, Int.mul_zero]
@[local simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
@[simp] protected theorem neg_mul (a b : Int) : -a * b = -(a * b) :=
(Int.neg_mul_eq_neg_mul a b).symm
@[local simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
@[simp] protected theorem mul_neg (a b : Int) : a * -b = -(a * b) :=
(Int.neg_mul_eq_mul_neg a b).symm
protected theorem neg_mul_neg (a b : Int) : -a * -b = a * b := by simp
@@ -486,6 +487,9 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 b = 0 := by
protected theorem mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 :=
Or.rec a0 b0 Int.mul_eq_zero.mp
@[simp] protected theorem mul_ne_zero_iff {a b : Int} : a * b 0 a 0 b 0 := by
rw [ne_eq, Int.mul_eq_zero, not_or, ne_eq]
protected theorem eq_of_mul_eq_mul_right {a b c : Int} (ha : a 0) (h : b * a = c * a) : b = c :=
have : (b - c) * a = 0 := by rwa [Int.sub_mul, Int.sub_eq_zero]
Int.sub_eq_zero.1 <| (Int.mul_eq_zero.mp this).resolve_right ha

View File

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

View File

@@ -26,9 +26,9 @@ theorem nonneg_or_nonneg_neg : ∀ (a : Int), NonNeg a NonNeg (-a)
| (_:Nat) => .inl _
| -[_+1] => .inr _
theorem le_def (a b : Int) : a b NonNeg (b - a) := .rfl
theorem le_def {a b : Int} : a b NonNeg (b - a) := .rfl
theorem lt_iff_add_one_le (a b : Int) : a < b a + 1 b := .rfl
theorem lt_iff_add_one_le {a b : Int} : a < b a + 1 b := .rfl
theorem le.intro_sub {a b : Int} (n : Nat) (h : b - a = n) : a b := by
simp [le_def, h]; constructor
@@ -127,9 +127,14 @@ protected theorem lt_iff_le_not_le {a b : Int} : a < b ↔ a ≤ b ∧ ¬b ≤ a
· exact Int.le_antisymm h h'
· subst h'; apply Int.le_refl
protected theorem lt_of_not_ge {a b : Int} (h : ¬a b) : b < a :=
Int.lt_iff_le_not_le.mpr (Int.le_total ..).resolve_right h, h
protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a b :=
(Int.lt_iff_le_not_le.mp h).right
protected theorem not_le {a b : Int} : ¬a b b < a :=
fun h => Int.lt_iff_le_not_le.2 (Int.le_total ..).resolve_right h, h,
fun h => (Int.lt_iff_le_not_le.1 h).2
Iff.intro Int.lt_of_not_ge Int.not_le_of_gt
protected theorem not_lt {a b : Int} : ¬a < b b a :=
by rw [ Int.not_le, Decidable.not_not]
@@ -235,9 +240,24 @@ theorem le_natAbs {a : Int} : a ≤ natAbs a :=
theorem negSucc_lt_zero (n : Nat) : -[n+1] < 0 :=
Int.not_le.1 fun h => let _, h := eq_ofNat_of_zero_le h; nomatch h
theorem negSucc_le_zero (n : Nat) : -[n+1] 0 :=
Int.le_of_lt (negSucc_lt_zero n)
@[simp] theorem negSucc_not_nonneg (n : Nat) : 0 -[n+1] False := by
simp only [Int.not_le, iff_false]; exact Int.negSucc_lt_zero n
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (ofNat_zero_le n)]
@[simp] theorem zero_max_ofNat (n : Nat) : (max 0 (n : Int)) = n := by
rw [Int.max_eq_right (ofNat_zero_le n)]
@[simp] theorem negSucc_max_zero (n : Nat) : (max (Int.negSucc n) 0) = 0 := by
rw [Int.max_eq_right (negSucc_le_zero _)]
@[simp] theorem zero_max_negSucc (n : Nat) : (max 0 (Int.negSucc n)) = 0 := by
rw [Int.max_eq_left (negSucc_le_zero _)]
protected theorem add_le_add_left {a b : Int} (h : a b) (c : Int) : c + a c + b :=
let n, hn := le.dest h; le.intro n <| by rw [Int.add_assoc, hn]
@@ -460,13 +480,21 @@ theorem toNat_eq_max : ∀ a : Int, (toNat a : Int) = max a 0
@[simp] theorem toNat_one : (1 : Int).toNat = 1 := rfl
@[simp] theorem toNat_of_nonneg {a : Int} (h : 0 a) : (toNat a : Int) = a := by
theorem toNat_of_nonneg {a : Int} (h : 0 a) : (toNat a : Int) = a := by
rw [toNat_eq_max, Int.max_eq_left h]
@[simp] theorem toNat_ofNat (n : Nat) : toNat n = n := rfl
@[simp] theorem toNat_negSucc (n : Nat) : (Int.negSucc n).toNat = 0 := by
simp [toNat]
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
@[simp] theorem ofNat_toNat (a : Int) : (a.toNat : Int) = max a 0 := by
match a with
| Int.ofNat n => simp
| Int.negSucc n => simp
theorem self_le_toNat (a : Int) : a toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
@[simp] theorem le_toNat {n : Nat} {z : Int} (h : 0 z) : n z.toNat (n : Int) z := by
@@ -487,7 +515,7 @@ theorem toNat_add_nat {a : Int} (ha : 0 ≤ a) (n : Nat) : (a + n).toNat = a.toN
| (n+1:Nat) => by simp [ofNat_add]
| -[n+1] => rfl
@[simp] theorem toNat_sub_toNat_neg : n : Int, n.toNat - (-n).toNat = n
theorem toNat_sub_toNat_neg : n : Int, n.toNat - (-n).toNat = n
| 0 => rfl
| (_+1:Nat) => Int.sub_zero _
| -[_+1] => Int.zero_sub _
@@ -503,15 +531,12 @@ theorem toNat_add_nat {a : Int} (ha : 0 ≤ a) (n : Nat) : (a + n).toNat = a.toN
/-! ### toNat' -/
theorem mem_toNat' : (a : Int) (n : Nat), toNat' a = some n a = n
theorem mem_toNat' : {a : Int} {n : Nat}, toNat' a = some n a = n
| (m : Nat), n => by simp [toNat', Int.ofNat_inj]
| -[m+1], n => by constructor <;> nofun
/-! ## Order properties of the integers -/
protected theorem lt_of_not_ge {a b : Int} : ¬a b b < a := Int.not_le.mp
protected theorem not_le_of_gt {a b : Int} : b < a ¬a b := Int.not_le.mpr
protected theorem le_of_not_le {a b : Int} : ¬ a b b a := (Int.le_total a b).resolve_left
@[simp] theorem negSucc_not_pos (n : Nat) : 0 < -[n+1] False := by
@@ -586,7 +611,10 @@ theorem add_one_le_iff {a b : Int} : a + 1 ≤ b ↔ a < b := .rfl
theorem lt_add_one_iff {a b : Int} : a < b + 1 a b := Int.add_le_add_iff_right _
@[simp] theorem succ_ofNat_pos (n : Nat) : 0 < (n : Int) + 1 :=
lt_add_one_iff.2 (ofNat_zero_le _)
lt_add_one_iff.mpr (ofNat_zero_le _)
theorem not_ofNat_neg (n : Nat) : ¬((n : Int) < 0) :=
Int.not_lt.mpr (ofNat_zero_le ..)
theorem le_add_one {a b : Int} (h : a b) : a b + 1 :=
Int.le_of_lt (Int.lt_add_one_iff.2 h)
@@ -801,6 +829,12 @@ protected theorem lt_add_of_neg_lt_sub_right {a b c : Int} (h : -b < a - c) : c
protected theorem neg_lt_sub_right_of_lt_add {a b c : Int} (h : c < a + b) : -b < a - c :=
Int.lt_sub_left_of_add_lt (Int.sub_right_lt_of_lt_add h)
protected theorem add_lt_iff {a b c : Int} : a + b < c a < -b + c := by
rw [ Int.add_lt_add_iff_left (-b), Int.add_comm (-b), Int.add_neg_cancel_right]
protected theorem sub_lt_iff {a b c : Int} : a - b < c a < c + b :=
Iff.intro Int.lt_add_of_sub_right_lt Int.sub_right_lt_of_lt_add
protected theorem sub_lt_of_sub_lt {a b c : Int} (h : a - b < c) : a - c < b :=
Int.sub_left_lt_of_lt_add (Int.lt_add_of_sub_right_lt h)
@@ -819,12 +853,10 @@ protected theorem lt_of_sub_lt_sub_left {a b c : Int} (h : c - a < c - b) : b <
protected theorem lt_of_sub_lt_sub_right {a b c : Int} (h : a - c < b - c) : a < b :=
Int.lt_of_add_lt_add_right h
@[simp] protected theorem sub_lt_sub_left_iff (a b c : Int) :
c - a < c - b b < a :=
@[simp] protected theorem sub_lt_sub_left_iff {a b c : Int} : c - a < c - b b < a :=
Int.lt_of_sub_lt_sub_left, (Int.sub_lt_sub_left · c)
@[simp] protected theorem sub_lt_sub_right_iff (a b c : Int) :
a - c < b - c a < b :=
@[simp] protected theorem sub_lt_sub_right_iff {a b c : Int} : a - c < b - c a < b :=
Int.lt_of_sub_lt_sub_right, (Int.sub_lt_sub_right · c)
protected theorem sub_lt_sub_of_le_of_lt {a b c d : Int}
@@ -956,13 +988,13 @@ theorem neg_of_sign_eq_neg_one : ∀ {a : Int}, sign a = -1 → a < 0
| 0, h => nomatch h
| -[_+1], _ => negSucc_lt_zero _
theorem sign_eq_one_iff_pos (a : Int) : sign a = 1 0 < a :=
theorem sign_eq_one_iff_pos {a : Int} : sign a = 1 0 < a :=
pos_of_sign_eq_one, sign_eq_one_of_pos
theorem sign_eq_neg_one_iff_neg (a : Int) : sign a = -1 a < 0 :=
theorem sign_eq_neg_one_iff_neg {a : Int} : sign a = -1 a < 0 :=
neg_of_sign_eq_neg_one, sign_eq_neg_one_of_neg
@[simp] theorem sign_eq_zero_iff_zero (a : Int) : sign a = 0 a = 0 :=
@[simp] theorem sign_eq_zero_iff_zero {a : Int} : sign a = 0 a = 0 :=
eq_zero_of_sign_eq_zero, fun h => by rw [h, sign_zero]
@[simp] theorem sign_sign : sign (sign x) = sign x := by
@@ -995,7 +1027,7 @@ theorem natAbs_mul_self : ∀ {a : Int}, ↑(natAbs a * natAbs a) = a * a
theorem eq_nat_or_neg (a : Int) : n : Nat, a = n a = -n := _, natAbs_eq a
theorem natAbs_mul_natAbs_eq {a b : Int} {c : Nat}
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs]
(h : a * b = (c : Int)) : a.natAbs * b.natAbs = c := by rw [ natAbs_mul, h, natAbs.eq_def]
@[simp] theorem natAbs_mul_self' (a : Int) : (natAbs a * natAbs a : Int) = a * a := by
rw [ Int.ofNat_mul, natAbs_mul_self]

View File

@@ -4,9 +4,22 @@ 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.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
import Init.Data.List.Perm
import Init.Data.List.Sort

View File

@@ -0,0 +1,495 @@
/-
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 attachWith_nil : ([] : List α).attachWith P H = [] := 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_left {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)]
@[deprecated pmap_congr_left (since := "2024-09-06")] abbrev pmap_congr := @pmap_congr_left
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 attach_congr {l₁ l₂ : List α} (h : l₁ = l₂) :
l₁.attach = l₂.attach.map (fun x => x.1, h x.2) := by
subst h
simp
theorem attachWith_congr {l₁ l₂ : List α} (w : l₁ = l₂) {P : α Prop} {H : x l₁, P x} :
l₁.attachWith P H = l₂.attachWith P fun x h => H _ (w h) := by
subst w
simp
@[simp] theorem attach_cons {x : α} {xs : List α} :
(x :: xs).attach =
x, mem_cons_self x xs :: xs.attach.map fun y, h => y, mem_cons_of_mem x h := by
simp only [attach, attachWith, pmap, map_pmap, cons.injEq, true_and]
apply pmap_congr_left
intros a _ m' _
rfl
@[simp]
theorem attachWith_cons {x : α} {xs : List α} {p : α Prop} (h : a x :: xs, p a) :
(x :: xs).attachWith p h = x, h x (mem_cons_self x xs) ::
xs.attachWith p (fun a ha h a (mem_cons_of_mem x ha)) :=
rfl
theorem pmap_eq_map_attach {p : α Prop} (f : a, p a β) (l H) :
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
rw [attach, attachWith, map_pmap]; exact pmap_congr_left 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 (List.map_id _)
theorem attachWith_map_coe {p : α Prop} (f : α β) (l : List α) (H : a l, p a) :
((l.attachWith p H).map fun (i : { i // p i}) => f i) = l.map f := by
rw [attachWith, map_pmap]; exact pmap_eq_map _ _ _ _
theorem attachWith_map_val {p : α Prop} (f : α β) (l : List α) (H : a l, p a) :
((l.attachWith p H).map fun i => f i.val) = l.map f :=
attachWith_map_coe _ _ _
@[simp]
theorem attachWith_map_subtype_val {p : α Prop} (l : List α) (H : a l, p a) :
(l.attachWith p H).map Subtype.val = l :=
(attachWith_map_coe _ _ _).trans (List.map_id _)
theorem countP_attach (l : List α) (p : α Bool) :
l.attach.countP (fun a : {x // x l} => p a) = l.countP p := by
simp only [ Function.comp_apply (g := Subtype.val), countP_map, attach_map_subtype_val]
theorem countP_attachWith {p : α Prop} (l : List α) (H : a l, p a) (q : α Bool) :
(l.attachWith p H).countP (fun a : {x // p x} => q a) = l.countP q := by
simp only [ Function.comp_apply (g := Subtype.val), countP_map, attachWith_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 count_attachWith [DecidableEq α] {p : α Prop} (l : List α) (H : a l, p a) (a : {x // p x}) :
(l.attachWith p H).count a = l.count a :=
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attachWith _ _ _
@[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]
theorem mem_pmap_of_mem {p : α Prop} {f : a, p a β} {l H} {a} (h : a l) :
f a (H a h) pmap f l H := by
rw [mem_pmap]
exact a, h, rfl
@[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 length_attachWith {p : α Prop} {l H} : length (l.attachWith p H) = length l :=
length_pmap
@[simp]
theorem pmap_eq_nil_iff {p : α Prop} {f : a, p a β} {l H} : pmap f l H = [] l = [] := by
rw [ length_eq_zero, length_pmap, length_eq_zero]
theorem pmap_ne_nil_iff {P : α Prop} (f : (a : α) P a β) {xs : List α}
(H : (a : α), a xs P a) : xs.pmap f H [] xs [] := by
simp
@[simp]
theorem attach_eq_nil_iff {l : List α} : l.attach = [] l = [] :=
pmap_eq_nil_iff
theorem attach_ne_nil_iff {l : List α} : l.attach [] l [] :=
pmap_ne_nil_iff _ _
@[simp]
theorem attachWith_eq_nil_iff {l : List α} {P : α Prop} {H : a l, P a} :
l.attachWith P H = [] l = [] :=
pmap_eq_nil_iff
theorem attachWith_ne_nil_iff {l : List α} {P : α Prop} {H : a l, P a} :
l.attachWith P H [] l [] :=
pmap_ne_nil_iff _ _
@[deprecated pmap_eq_nil_iff (since := "2024-09-06")] abbrev pmap_eq_nil := @pmap_eq_nil_iff
@[deprecated pmap_ne_nil_iff (since := "2024-09-06")] abbrev pmap_ne_nil := @pmap_ne_nil_iff
@[deprecated attach_eq_nil_iff (since := "2024-09-06")] abbrev attach_eq_nil := @attach_eq_nil_iff
@[deprecated attach_ne_nil_iff (since := "2024-09-06")] abbrev attach_ne_nil := @attach_ne_nil_iff
@[simp]
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]
@[simp]
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 (@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]
@[simp]
theorem getElem?_attachWith {xs : List α} {i : Nat} {P : α Prop} {H : a xs, P a} :
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (getElem?_mem a)) :=
getElem?_pmap ..
@[simp]
theorem getElem?_attach {xs : List α} {i : Nat} :
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => getElem?_mem a) :=
getElem?_attachWith
@[simp]
theorem getElem_attachWith {xs : List α} {P : α Prop} {H : a xs, P a}
{i : Nat} (h : i < (xs.attachWith P H).length) :
(xs.attachWith P H)[i] = xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h)) :=
getElem_pmap ..
@[simp]
theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
xs.attach[i] = xs[i]'(by simpa using h), getElem_mem (by simpa using h) :=
getElem_attachWith h
@[simp] theorem head?_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) :
(xs.pmap f H).head? = xs.attach.head?.map fun a, m => f a (H a m) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp at ih
simp [head?_pmap, ih]
@[simp] theorem head_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) (h : xs.pmap f H []) :
(xs.pmap f H).head h = f (xs.head (by simpa using h)) (H _ (head_mem _)) := by
induction xs with
| nil => simp at h
| cons x xs ih => simp [head_pmap, ih]
@[simp] theorem head?_attachWith {P : α Prop} {xs : List α}
(H : (a : α), a xs P a) :
(xs.attachWith P H).head? = xs.head?.pbind (fun a h => some a, H _ (mem_of_mem_head? h)) := by
cases xs <;> simp_all
@[simp] theorem head_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} (h : xs.attachWith P H []) :
(xs.attachWith P H).head h = xs.head (by simpa using h), H _ (head_mem _) := by
cases xs with
| nil => simp at h
| cons x xs => simp [head_attachWith, h]
@[simp] theorem head?_attach (xs : List α) :
xs.attach.head? = xs.head?.pbind (fun a h => some a, mem_of_mem_head? h) := by
cases xs <;> simp_all
@[simp] theorem head_attach {xs : List α} (h) :
xs.attach.head h = xs.head (by simpa using h), head_mem (by simpa using h) := by
cases xs with
| nil => simp at h
| cons x xs => simp [head_attach, h]
theorem attach_map {l : List α} (f : α β) :
(l.map f).attach = l.attach.map (fun x, h => f x, mem_map_of_mem f h) := by
induction l <;> simp [*]
theorem attachWith_map {l : List α} (f : α β) {P : β Prop} {H : (b : β), b l.map f P b} :
(l.map f).attachWith P H = (l.attachWith (P f) (fun a h => H _ (mem_map_of_mem f h))).map
fun x, h => f x, h := by
induction l <;> simp [*]
theorem map_attachWith {l : List α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f =
l.pmap (fun a (h : a l P a) => f a, H _ h.1) (fun a h => h, H a h) := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [attachWith_cons, map_cons, ih, pmap, cons.injEq, true_and]
apply pmap_congr_left
simp
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
theorem map_attach {l : List α} (f : { x // x l } β) :
l.attach.map f = l.pmap (fun a h => f a, h) (fun _ => id) := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [attach_cons, map_cons, map_map, Function.comp_apply, pmap, cons.injEq, true_and, ih]
apply pmap_congr_left
simp
theorem attach_filterMap {l : List α} {f : α Option β} :
(l.filterMap f).attach = l.attach.filterMap
fun x, h => (f x).pbind (fun b m => some b, mem_filterMap.mpr x, h, m) := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [filterMap_cons, attach_cons, ih, filterMap_map]
split <;> rename_i h
· simp only [Option.pbind_eq_none_iff, reduceCtorEq, Option.mem_def, exists_false,
or_false] at h
rw [attach_congr]
rotate_left
· simp only [h]
rfl
rw [ih]
simp only [map_filterMap, Option.map_pbind, Option.map_some']
rfl
· simp only [Option.pbind_eq_some_iff] at h
obtain a, h, w := h
simp only [Option.some.injEq] at w
subst w
simp only [Option.mem_def] at h
rw [attach_congr]
rotate_left
· simp only [h]
rfl
rw [attach_cons, map_cons, map_map, ih, map_filterMap]
congr
ext
simp
theorem attach_filter {l : List α} (p : α Bool) :
(l.filter p).attach = l.attach.filterMap
fun x => if w : p x.1 then some x.1, mem_filter.mpr x.2, w else none := by
rw [attach_congr (congrFun (filterMap_eq_filter _).symm _), attach_filterMap, map_filterMap]
simp only [Option.guard]
congr
ext1
split <;> simp
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
-- Also missing are `filterMap_attach`, `filter_attach`, `filterMap_attachWith` and `filter_attachWith`.
theorem pmap_pmap {p : α Prop} {q : β Prop} (g : a, p a β) (f : b, q b γ) (l H₁ H₂) :
pmap f (pmap g l H₁) H₂ =
pmap (α := { x // x l }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) l.attach
(fun a _ => H₁ a a.2) := by
simp [pmap_eq_map_attach, attach_map]
@[simp] theorem pmap_append {p : ι Prop} (f : a : ι, p a α) (l₁ l₂ : List ι)
(h : a l₁ ++ l₂, p a) :
(l₁ ++ l₂).pmap f h =
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
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₂ _
@[simp] theorem attach_append (xs ys : List α) :
(xs ++ ys).attach = xs.attach.map (fun x, h => x, mem_append_of_mem_left ys h) ++
ys.attach.map fun x, h => x, mem_append_of_mem_right xs h := by
simp only [attach, attachWith, pmap, map_pmap, pmap_append]
congr 1 <;>
exact pmap_congr_left _ fun _ _ _ _ => rfl
@[simp] theorem attachWith_append {P : α Prop} {xs ys : List α}
{H : (a : α), a xs ++ ys P a} :
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_of_mem_left ys h)) ++
ys.attachWith P (fun a h => H a (mem_append_of_mem_right xs h)) := by
simp only [attachWith, attach_append, map_pmap, pmap_append]
@[simp] theorem pmap_reverse {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs.reverse P a) :
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
induction xs <;> simp_all
theorem reverse_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) :
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
rw [pmap_reverse]
@[simp] theorem attachWith_reverse {P : α Prop} {xs : List α}
{H : (a : α), a xs.reverse P a} :
xs.reverse.attachWith P H =
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse :=
pmap_reverse ..
theorem reverse_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) :=
reverse_pmap ..
@[simp] theorem attach_reverse (xs : List α) :
xs.reverse.attach = xs.attach.reverse.map fun x, h => x, by simpa using h := by
simp only [attach, attachWith, reverse_pmap, map_pmap]
apply pmap_congr_left
intros
rfl
theorem reverse_attach (xs : List α) :
xs.attach.reverse = xs.reverse.attach.map fun x, h => x, by simpa using h := by
simp only [attach, attachWith, reverse_pmap, map_pmap]
apply pmap_congr_left
intros
rfl
@[simp] theorem getLast?_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) :
(xs.pmap f H).getLast? = xs.attach.getLast?.map fun a, m => f a (H a m) := by
simp only [getLast?_eq_head?_reverse]
rw [reverse_pmap, reverse_attach, head?_map, pmap_eq_map_attach, head?_map]
simp only [Option.map_map]
congr
@[simp] theorem getLast_pmap {P : α Prop} (f : (a : α) P a β) (xs : List α)
(H : (a : α), a xs P a) (h : xs.pmap f H []) :
(xs.pmap f H).getLast h = f (xs.getLast (by simpa using h)) (H _ (getLast_mem _)) := by
simp only [getLast_eq_head_reverse]
simp only [reverse_pmap, head_pmap, head_reverse]
@[simp] theorem getLast?_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).getLast? = xs.getLast?.pbind (fun a h => some a, H _ (mem_of_getLast?_eq_some h)) := by
rw [getLast?_eq_head?_reverse, reverse_attachWith, head?_attachWith]
simp
@[simp] theorem getLast_attachWith {P : α Prop} {xs : List α}
{H : (a : α), a xs P a} (h : xs.attachWith P H []) :
(xs.attachWith P H).getLast h = xs.getLast (by simpa using h), H _ (getLast_mem _) := by
simp only [getLast_eq_head_reverse, reverse_attachWith, head_attachWith, head_map]
@[simp]
theorem getLast?_attach {xs : List α} :
xs.attach.getLast? = xs.getLast?.pbind fun a h => some a, mem_of_getLast?_eq_some h := by
rw [getLast?_eq_head?_reverse, reverse_attach, head?_map, head?_attach]
simp
@[simp]
theorem getLast_attach {xs : List α} (h : xs.attach []) :
xs.attach.getLast h = xs.getLast (by simpa using h), getLast_mem (by simpa using h) := by
simp only [getLast_eq_head_reverse, reverse_attach, head_map, head_attach]
end List

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,6 @@ Author: Leonardo de Moura
-/
prelude
import Init.Data.Nat.Linear
import Init.Ext
universe u
@@ -13,6 +12,10 @@ namespace List
/-! The following functions can't be defined at `Init.Data.List.Basic`, because they depend on `Init.Util`,
and `Init.Util` depends on `Init.Data.List.Basic`. -/
/-! ## Alternative getters -/
/-! ### get! -/
/--
Returns the `i`-th element in the list (zero-based).
@@ -24,108 +27,12 @@ def get! [Inhabited α] : (as : List α) → (i : Nat) → α
| _::as, n+1 => get! as n
| _, _ => panic! "invalid index"
/--
Returns the `i`-th element in the list (zero-based).
theorem get!_nil [Inhabited α] (n : Nat) : [].get! n = (default : α) := rfl
theorem get!_cons_succ [Inhabited α] (l : List α) (a : α) (n : Nat) :
(a::l).get! (n+1) = get! l n := rfl
theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a := rfl
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
Also see `get`, `getD` and `get!`.
-/
def get? : (as : List α) (i : Nat) Option α
| a::_, 0 => some a
| _::as, n+1 => get? as n
| _, _ => none
/--
Returns the `i`-th element in the list (zero-based).
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
See also `get?` and `get!`.
-/
def getD (as : List α) (i : Nat) (fallback : α) : α :=
(as.get? i).getD fallback
@[ext] theorem ext : {l₁ l₂ : List α}, ( n, l₁.get? n = l₂.get? n) l₁ = l₂
| [], [], _ => rfl
| a :: l₁, [], h => nomatch h 0
| [], a' :: l₂, h => nomatch h 0
| a :: l₁, a' :: l₂, h => by
have h0 : some a = some a' := h 0
injection h0 with aa; simp only [aa, ext fun n => h (n+1)]
/--
Returns the first element in the list.
If the list is empty, this function panics when executed, and returns `default`.
See `head` and `headD` for safer alternatives.
-/
def head! [Inhabited α] : List α α
| [] => panic! "empty list"
| a::_ => a
/--
Returns the first element in the list.
If the list is empty, this function returns `none`.
Also see `headD` and `head!`.
-/
def head? : List α Option α
| [] => none
| a::_ => some a
/--
Returns the first element in the list.
If the list is empty, this function returns `fallback`.
Also see `head?` and `head!`.
-/
def headD : (as : List α) (fallback : α) α
| [], fallback => fallback
| a::_, _ => a
/--
Returns the first element of a non-empty list.
-/
def head : (as : List α) as [] α
| a::_, _ => a
/--
Drops the first element of the list.
If the list is empty, this function panics when executed, and returns the empty list.
See `tail` and `tailD` for safer alternatives.
-/
def tail! : List α List α
| [] => panic! "empty list"
| _::as => as
/--
Drops the first element of the list.
If the list is empty, this function returns `none`.
Also see `tailD` and `tail!`.
-/
def tail? : List α Option (List α)
| [] => none
| _::as => some as
/--
Drops the first element of the list.
If the list is empty, this function returns `fallback`.
Also see `head?` and `head!`.
-/
def tailD (list fallback : List α) : List α :=
match list with
| [] => fallback
| _ :: tl => tl
/--
Returns the last element of a non-empty list.
-/
def getLast : (as : List α), as [] α
| [], h => absurd rfl h
| [a], _ => a
| _::b::as, _ => getLast (b::as) (fun h => List.noConfusion h)
/-! ### getLast! -/
/--
Returns the last element in the list.
@@ -137,61 +44,118 @@ def getLast! [Inhabited α] : List αα
| [] => panic! "empty list"
| a::as => getLast (a::as) (fun h => List.noConfusion h)
/--
Returns the last element in the list.
/-! ## Head and tail -/
If the list is empty, this function returns `none`.
Also see `getLastD` and `getLast!`.
-/
def getLast? : List α Option α
| [] => none
| a::as => some (getLast (a::as) (fun h => List.noConfusion h))
/-! ### head! -/
/--
Returns the last element in the list.
Returns the first element in the list.
If the list is empty, this function returns `fallback`.
Also see `getLast?` and `getLast!`.
If the list is empty, this function panics when executed, and returns `default`.
See `head` and `headD` for safer alternatives.
-/
def getLastD : (as : List α) (fallback : α) α
| [], a₀ => a₀
| a::as, _ => getLast (a::as) (fun h => List.noConfusion h)
def head! [Inhabited α] : List α α
| [] => panic! "empty list"
| a::_ => a
/-! ### tail! -/
/--
`O(n)`. Rotates the elements of `xs` to the left such that the element at
`xs[i]` rotates to `xs[(i - n) % l.length]`.
* `rotateLeft [1, 2, 3, 4, 5] 3 = [4, 5, 1, 2, 3]`
* `rotateLeft [1, 2, 3, 4, 5] 5 = [1, 2, 3, 4, 5]`
* `rotateLeft [1, 2, 3, 4, 5] = [2, 3, 4, 5, 1]`
Drops the first element of the list.
If the list is empty, this function panics when executed, and returns the empty list.
See `tail` and `tailD` for safer alternatives.
-/
def rotateLeft (xs : List α) (n : Nat := 1) : List α :=
let len := xs.length
if len 1 then
xs
else
let n := n % len
let b := xs.take n
let e := xs.drop n
e ++ b
def tail! : List α List α
| [] => panic! "empty list"
| _::as => as
@[simp] theorem tail!_cons : @tail! α (a::l) = l := rfl
/-! ### partitionM -/
/--
`O(n)`. Rotates the elements of `xs` to the right such that the element at
`xs[i]` rotates to `xs[(i + n) % l.length]`.
* `rotateRight [1, 2, 3, 4, 5] 3 = [3, 4, 5, 1, 2]`
* `rotateRight [1, 2, 3, 4, 5] 5 = [1, 2, 3, 4, 5]`
* `rotateRight [1, 2, 3, 4, 5] = [5, 1, 2, 3, 4]`
-/
def rotateRight (xs : List α) (n : Nat := 1) : List α :=
let len := xs.length
if len 1 then
xs
else
let n := len - n % len
let b := xs.take n
let e := xs.drop n
e ++ b
Monadic generalization of `List.partition`.
theorem get_append_left (as bs : List α) (h : i < as.length) {h'} : (as ++ bs).get i, h' = as.get i, h := by
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic` or `Init.Data.List.Control`.
```
def posOrNeg (x : Int) : Except String Bool :=
if x > 0 then pure true
else if x < 0 then pure false
else throw "Zero is not positive or negative"
partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
```
-/
@[inline] def partitionM [Monad m] (p : α m Bool) (l : List α) : m (List α × List α) :=
go l #[] #[]
where
/-- Auxiliary for `partitionM`:
`partitionM.go p l acc₁ acc₂` returns `(acc₁.toList ++ left, acc₂.toList ++ right)`
if `partitionM p l` returns `(left, right)`. -/
@[specialize] go : List α Array α Array α m (List α × List α)
| [], acc₁, acc₂ => pure (acc₁.toList, acc₂.toList)
| x :: xs, acc₁, acc₂ => do
if p x then
go xs (acc₁.push x) acc₂
else
go xs acc₁ (acc₂.push x)
/-! ### partitionMap -/
/--
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
whilst partitioning the result into a pair of lists, `List β × List γ`,
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
```
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
```
-/
@[inline] def partitionMap (f : α β γ) (l : List α) : List β × List γ := go l #[] #[] where
/-- Auxiliary for `partitionMap`:
`partitionMap.go f l acc₁ acc₂ = (acc₁.toList ++ left, acc₂.toList ++ right)`
if `partitionMap f l = (left, right)`. -/
@[specialize] go : List α Array β Array γ List β × List γ
| [], acc₁, acc₂ => (acc₁.toList, acc₂.toList)
| x :: xs, acc₁, acc₂ =>
match f x with
| .inl a => go xs (acc₁.push a) acc₂
| .inr b => go xs acc₁ (acc₂.push b)
/-! ### mapMono
This is a performance optimization for `List.mapM` that avoids allocating a new list when the result of each `f a` is a pointer equal value `a`.
For verification purposes, `List.mapMono = List.map`.
-/
@[specialize] private unsafe def mapMonoMImp [Monad m] (as : List α) (f : α m α) : m (List α) := do
match as with
| [] => return as
| b :: bs =>
let b' f b
let bs' mapMonoMImp bs f
if ptrEq b' b && ptrEq bs' bs then
return as
else
return b' :: bs'
/--
Monomorphic `List.mapM`. The internal implementation uses pointer equality, and does not allocate a new list
if the result of each `f a` is a pointer equal value `a`.
-/
@[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α m α) : m (List α) :=
match as with
| [] => return []
| a :: as => return ( f a) :: ( mapMonoM as f)
def mapMono (as : List α) (f : α α) : List α :=
Id.run <| as.mapMonoM f
/-! ## Additional lemmas required for bootstrapping `Array`. -/
theorem getElem_append_left {as bs : List α} (h : i < as.length) {h'} : (as ++ bs)[i] = as[i] := by
induction as generalizing i with
| nil => trivial
| cons a as ih =>
@@ -199,12 +163,14 @@ theorem get_append_left (as bs : List α) (h : i < as.length) {h'} : (as ++ bs).
| zero => rfl
| succ i => apply ih
theorem get_append_right (as bs : List α) (h : ¬ i < as.length) {h' h''} : (as ++ bs).get i, h' = bs.get i - as.length, h'' := by
theorem getElem_append_right {as bs : List α} {i : Nat} (h : as.length i) {h₂} :
(as ++ bs)[i]'h₂ =
bs[i - as.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) := by
induction as generalizing i with
| nil => trivial
| cons a as ih =>
cases i with simp [get, Nat.succ_sub_succ] <;> simp_arith [Nat.succ_sub_succ] at h
| succ i => apply ih; simp_arith [h]
cases i with simp [get, Nat.succ_sub_succ] <;> simp [Nat.succ_sub_succ] at h
| succ i => apply ih; simp [h]
theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.length) : (as ++ [a] : List _).get i = a := by
cases i; rename_i i h'
@@ -213,8 +179,8 @@ theorem get_last {as : List α} {i : Fin (length (as ++ [a]))} (h : ¬ i.1 < as.
| zero => simp [List.get]
| succ => simp_arith at h'
| cons a as ih =>
cases i with simp_arith at h
| succ i => apply ih; simp_arith [h]
cases i with simp at h
| succ i => apply ih; simp [h]
theorem sizeOf_lt_of_mem [SizeOf α] {as : List α} (h : a as) : sizeOf a < sizeOf as := by
induction h with
@@ -228,7 +194,7 @@ macro "sizeOf_list_dec" : tactic =>
`(tactic| first
| with_reducible apply sizeOf_lt_of_mem; assumption; done
| with_reducible
apply Nat.lt_trans (sizeOf_lt_of_mem ?h)
apply Nat.lt_of_lt_of_le (sizeOf_lt_of_mem ?h)
case' h => assumption
simp_arith)
@@ -258,7 +224,7 @@ theorem append_cancel_right {as bs cs : List α} (h : as ++ bs = cs ++ bs) : as
next => apply append_cancel_right
next => intro h; simp [h]
@[simp] theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
theorem sizeOf_get [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
match as, i with
| a::as, 0, _ => simp_arith [get]
| a::as, i+1, h =>
@@ -285,74 +251,4 @@ theorem le_antisymm [LT α] [s : Antisymm (¬ · < · : αα → Prop)] {as
instance [LT α] [Antisymm (¬ · < · : α α Prop)] : Antisymm (· · : List α List α Prop) where
antisymm h₁ h₂ := le_antisymm h₁ h₂
@[specialize] private unsafe def mapMonoMImp [Monad m] (as : List α) (f : α m α) : m (List α) := do
match as with
| [] => return as
| b :: bs =>
let b' f b
let bs' mapMonoMImp bs f
if ptrEq b' b && ptrEq bs' bs then
return as
else
return b' :: bs'
/--
Monomorphic `List.mapM`. The internal implementation uses pointer equality, and does not allocate a new list
if the result of each `f a` is a pointer equal value `a`.
-/
@[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α m α) : m (List α) :=
match as with
| [] => return []
| a :: as => return ( f a) :: ( mapMonoM as f)
def mapMono (as : List α) (f : α α) : List α :=
Id.run <| as.mapMonoM f
/--
Monadic generalization of `List.partition`.
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic`.
```
def posOrNeg (x : Int) : Except String Bool :=
if x > 0 then pure true
else if x < 0 then pure false
else throw "Zero is not positive or negative"
partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
```
-/
@[inline] def partitionM [Monad m] (p : α m Bool) (l : List α) : m (List α × List α) :=
go l #[] #[]
where
/-- Auxiliary for `partitionM`:
`partitionM.go p l acc₁ acc₂` returns `(acc₁.toList ++ left, acc₂.toList ++ right)`
if `partitionM p l` returns `(left, right)`. -/
@[specialize] go : List α Array α Array α m (List α × List α)
| [], acc₁, acc₂ => pure (acc₁.toList, acc₂.toList)
| x :: xs, acc₁, acc₂ => do
if p x then
go xs (acc₁.push x) acc₂
else
go xs acc₁ (acc₂.push x)
/--
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
whilst partitioning the result it into a pair of lists, `List β × List γ`,
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
```
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
```
-/
@[inline] def partitionMap (f : α β γ) (l : List α) : List β × List γ := go l #[] #[] where
/-- Auxiliary for `partitionMap`:
`partitionMap.go f l acc₁ acc₂ = (acc₁.toList ++ left, acc₂.toList ++ right)`
if `partitionMap f l = (left, right)`. -/
@[specialize] go : List α Array β Array γ List β × List γ
| [], acc₁, acc₂ => (acc₁.toList, acc₂.toList)
| x :: xs, acc₁, acc₂ =>
match f x with
| .inl a => go xs (acc₁.push a) acc₂
| .inr b => go xs acc₁ (acc₂.push b)
end List

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:
@@ -151,6 +151,11 @@ protected def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w
let s' f s a
List.foldlM f s' as
@[simp] theorem foldlM_nil [Monad m] (f : β α m β) (b) : [].foldlM f b = pure b := rfl
@[simp] theorem foldlM_cons [Monad m] (f : β α m β) (b) (a) (l : List α) :
(a :: l).foldlM f b = f b a >>= l.foldlM f := by
simp [List.foldlM]
/--
Folds a monadic function over a list from right to left:
```
@@ -165,6 +170,8 @@ foldrM f x₀ [a, b, c] = do
def foldrM {m : Type u Type v} [Monad m] {s : Type u} {α : Type w} (f : α s m s) (init : s) (l : List α) : m s :=
l.reverse.foldlM (fun s a => f a s) init
@[simp] theorem foldrM_nil [Monad m] (f : α β m β) (b) : [].foldrM f b = pure b := rfl
/--
Maps `f` over the list and collects the results with `<|>`.
```
@@ -220,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,334 @@
/-
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 countP_singleton (a : α) : countP p [a] = if p a then 1 else 0 := by
simp [countP_cons]
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 [h]
else
rw [countP_cons_of_pos (fun a => ¬p a) _ _, countP_cons_of_neg _ _ h, length, ih]
· rfl
· simp [h]
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_eq_length_filter' : countP p = length filter p := by
funext l
apply countP_eq_length_filter
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]
@[simp] theorem countP_pos_iff {p} : 0 < countP p l a l, p a := by
simp only [countP_eq_length_filter, length_pos_iff_exists_mem, mem_filter, exists_prop]
@[deprecated countP_pos_iff (since := "2024-09-09")] abbrev countP_pos := @countP_pos_iff
@[simp] theorem one_le_countP_iff {p} : 1 countP p l a l, p a :=
countP_pos_iff
@[simp] theorem countP_eq_zero {p} : countP p l = 0 a l, ¬p a := by
simp only [countP_eq_length_filter, length_eq_zero, filter_eq_nil_iff]
@[simp] theorem countP_eq_length {p} : countP p l = l.length a l, p a := by
rw [countP_eq_length_filter, filter_length_eq_length]
theorem countP_replicate (p : α Bool) (a : α) (n : Nat) :
countP p (replicate n a) = if p a then n else 0 := by
simp only [countP_eq_length_filter, filter_replicate]
split <;> simp
theorem boole_getElem_le_countP (p : α Bool) (l : List α) (i : Nat) (h : i < l.length) :
(if p l[i] then 1 else 0) l.countP p := by
induction l generalizing i with
| nil => simp at h
| cons x l ih =>
cases i with
| zero => simp [countP_cons]
| succ i =>
simp only [length_cons, add_one_lt_add_one_iff] at h
simp only [getElem_cons_succ, countP_cons]
specialize ih _ h
exact le_add_right_of_le ih
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 IsPrefix.countP_le (s : l₁ <+: l₂) : countP p l₁ countP p l₂ := s.sublist.countP_le _
theorem IsSuffix.countP_le (s : l₁ <:+ l₂) : countP p l₁ countP p l₂ := s.sublist.countP_le _
theorem IsInfix.countP_le (s : l₁ <:+: l₂) : countP p l₁ countP p l₂ := s.sublist.countP_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 : (countP fun (_ : α) => true) = length := by
funext l
simp
@[simp] theorem countP_false : (countP fun (_ : α) => false) = Function.const _ 0 := by
funext l
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
theorem length_filterMap_eq_countP (f : α Option β) (l : List α) :
(filterMap f l).length = countP (fun a => (f a).isSome) l := by
induction l with
| nil => rfl
| cons x l ih =>
simp only [filterMap_cons, countP_cons]
split <;> simp [ih, *]
theorem countP_filterMap (p : β Bool) (f : α Option β) (l : List α) :
countP p (filterMap f l) = countP (fun a => ((f a).map p).getD false) l := by
simp only [countP_eq_length_filter, filter_filterMap, filterMap_eq_filter]
simp only [length_filterMap_eq_countP]
congr
ext a
simp (config := { contextual := true }) [Option.getD_eq_iff]
@[simp] theorem countP_join (l : List (List α)) :
countP p l.join = Nat.sum (l.map (countP p)) := by
simp only [countP_eq_length_filter, filter_join]
simp [countP_eq_length_filter']
@[simp] theorem countP_reverse (l : List α) : countP p l.reverse = countP p l := by
simp [countP_eq_length_filter, filter_reverse]
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_eq_countP (a : α) (l : List α) : count a l = countP (· == a) l := rfl
theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
funext l
apply count_eq_countP
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 IsPrefix.count_le (h : l₁ <+: l₂) (a : α) : count a l₁ count a l₂ := h.sublist.count_le _
theorem IsSuffix.count_le (h : l₁ <:+ l₂) (a : α) : count a l₁ count a l₂ := h.sublist.count_le _
theorem IsInfix.count_le (h : l₁ <:+: l₂) (a : α) : count a l₁ count a l₂ := h.sublist.count_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 _
theorem count_join (a : α) (l : List (List α)) : count a l.join = Nat.sum (l.map (count a)) := by
simp only [count_eq_countP, countP_join, count_eq_countP']
@[simp] theorem count_reverse (a : α) (l : List α) : count a l.reverse = count a l := by
simp only [count_eq_countP, countP_eq_length_filter, filter_reverse, length_reverse]
theorem boole_getElem_le_count (a : α) (l : List α) (i : Nat) (h : i < l.length) :
(if l[i] == a then 1 else 0) l.count a := by
rw [count_eq_countP]
apply boole_getElem_le_countP (· == a)
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 {a : α} {l : List α} : 0 < count a l a l := by
simp only [count, countP_pos_iff, beq_iff_eq, exists_eq_right]
@[deprecated count_pos_iff (since := "2024-09-09")] abbrev count_pos_iff_mem := @count_pos_iff
@[simp] theorem one_le_count_iff {a : α} {l : List α} : 1 count a l a l :=
count_pos_iff
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.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.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_iff, 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_filterMap {α} [BEq β] (b : β) (f : α Option β) (l : List α) :
count b (filterMap f l) = countP (fun a => f a == some b) l := by
rw [count_eq_countP, countP_filterMap]
congr
ext a
obtain _ | b := f a
· simp
· simp
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, reduceCtorEq]
if ha : b = a then
rw [ha, eq_comm] at hc
rw [if_pos (beq_iff_eq.2 ha), if_neg (by simpa using Ne.symm hc), Nat.add_zero, Nat.add_zero]
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

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