mirror of
https://github.com/leanprover/lean4.git
synced 2026-04-14 16:14:08 +00:00
Compare commits
136 Commits
pairwise_s
...
fold_const
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
832f5a3ef8 | ||
|
|
bdd4d42559 | ||
|
|
90dab5e267 | ||
|
|
6a904f2c85 | ||
|
|
2c396d6424 | ||
|
|
69f86d6478 | ||
|
|
4ea55687a5 | ||
|
|
69c71f6476 | ||
|
|
7f128b39e7 | ||
|
|
a845a007ac | ||
|
|
abf4206e9c | ||
|
|
83ad82162f | ||
|
|
93ac635a89 | ||
|
|
642c28cdbb | ||
|
|
a04f3cab5a | ||
|
|
86af04cc08 | ||
|
|
7253ef8751 | ||
|
|
f830fc9f4d | ||
|
|
671ce7afd3 | ||
|
|
87c92a3f87 | ||
|
|
15bf41cd67 | ||
|
|
906bc583c5 | ||
|
|
ea43ebd54a | ||
|
|
bb9c9bd99f | ||
|
|
fe5894f2f6 | ||
|
|
3ecbf4ae2d | ||
|
|
9b342efb84 | ||
|
|
c02aa98c6a | ||
|
|
18ba5f24e1 | ||
|
|
cbe39dc4bb | ||
|
|
a5b8d5b486 | ||
|
|
895391b73f | ||
|
|
e280de00b6 | ||
|
|
8c87a90cea | ||
|
|
54c22efca1 | ||
|
|
d4f2db9559 | ||
|
|
39e0b41fe1 | ||
|
|
84f8871c3f | ||
|
|
93fa9c8837 | ||
|
|
0768ad4eb9 | ||
|
|
c545e7b0c9 | ||
|
|
7b3c64fc85 | ||
|
|
af0b563099 | ||
|
|
af40e61811 | ||
|
|
1758b37a71 | ||
|
|
3701bee777 | ||
|
|
6d971827e2 | ||
|
|
871c9b4164 | ||
|
|
ee6737ab4d | ||
|
|
7d60d8b563 | ||
|
|
a4673e20a5 | ||
|
|
b2ee8c240d | ||
|
|
5d632a97b8 | ||
|
|
5938dbbd14 | ||
|
|
852add3e55 | ||
|
|
20c857147c | ||
|
|
9f1eb479b0 | ||
|
|
92cca5ed1b | ||
|
|
3a4d2cded3 | ||
|
|
22ae04f3e7 | ||
|
|
99f362979b | ||
|
|
3a309f7691 | ||
|
|
8f0631ab1f | ||
|
|
08acf5a136 | ||
|
|
bdfaa00b1e | ||
|
|
8ceb24a5e6 | ||
|
|
201749ccac | ||
|
|
d0bc4e4245 | ||
|
|
c2117d75a6 | ||
|
|
3477b0e7f6 | ||
|
|
696f70bb4e | ||
|
|
726e162527 | ||
|
|
de5e07c4d2 | ||
|
|
327986e6fb | ||
|
|
6c33b9c57f | ||
|
|
d907771fdd | ||
|
|
5c3360200e | ||
|
|
204d4839fa | ||
|
|
e32f3e8140 | ||
|
|
7d2155943c | ||
|
|
78c4d6daff | ||
|
|
5526ff6320 | ||
|
|
bfca7ec72a | ||
|
|
9208b3585f | ||
|
|
a94805ff71 | ||
|
|
4eb842560c | ||
|
|
490d16c80d | ||
|
|
f60721bfbd | ||
|
|
a5ecdd0a17 | ||
|
|
be717f03ef | ||
|
|
41b4914836 | ||
|
|
933445608c | ||
|
|
8e396068e4 | ||
|
|
c1df7564ce | ||
|
|
ba3565f441 | ||
|
|
af03af5037 | ||
|
|
f6666fe266 | ||
|
|
c580684c22 | ||
|
|
1a12f63f74 | ||
|
|
95b8095fa6 | ||
|
|
94cc8eb863 | ||
|
|
1cf47bce5a | ||
|
|
b73fe04710 | ||
|
|
f986a2e9ef | ||
|
|
1a9cbc96f1 | ||
|
|
7aec6c9ae7 | ||
|
|
31de2494fb | ||
|
|
d679591880 | ||
|
|
f167cfba71 | ||
|
|
180c6aaa5e | ||
|
|
ab0241dac8 | ||
|
|
dc65f03c41 | ||
|
|
de96b6d8a7 | ||
|
|
3ab2c714ec | ||
|
|
f99427bd1a | ||
|
|
1118978cbb | ||
|
|
4ea8c5ad8d | ||
|
|
a6ae49c3ab | ||
|
|
2ad6d397f8 | ||
|
|
891824bc51 | ||
|
|
f35c562ef8 | ||
|
|
bcd8517307 | ||
|
|
ce73bbe277 | ||
|
|
f0eab4b7b1 | ||
|
|
5f70c1ca64 | ||
|
|
fce82eba40 | ||
|
|
9d14e4423c | ||
|
|
0c7859a7dd | ||
|
|
c01e003b49 | ||
|
|
ce8a130724 | ||
|
|
3c18d151a6 | ||
|
|
0f48e926eb | ||
|
|
850964999e | ||
|
|
57b8b32c72 | ||
|
|
bd2aefee01 | ||
|
|
74dcd6c2a9 |
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -298,8 +298,8 @@ jobs:
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: clang64
|
||||
# `:p` means prefix with appropriate msystem prefix
|
||||
pacboy: "make python cmake:p clang:p ccache:p gmp:p git zip unzip diffutils binutils tree zstd:p tar"
|
||||
# `:` means do not prefix with msystem
|
||||
pacboy: "make: python: cmake clang ccache gmp git: zip: unzip: diffutils: binutils: tree: zstd tar:"
|
||||
if: runner.os == 'Windows'
|
||||
- name: Install Brew Packages
|
||||
run: |
|
||||
@@ -426,7 +426,7 @@ jobs:
|
||||
if: matrix.test-speedcenter
|
||||
- name: Check Stage 3
|
||||
run: |
|
||||
make -C build -j$NPROC stage3
|
||||
make -C build -j$NPROC check-stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Test Speedcenter Benchmarks
|
||||
run: |
|
||||
@@ -455,12 +455,24 @@ jobs:
|
||||
# mark as merely cancelled not failed if builds are cancelled
|
||||
if: ${{ !cancelled() }}
|
||||
steps:
|
||||
- if: ${{ contains(needs.*.result, 'failure') && github.repository == 'leanprover/lean4' && github.ref_name == 'master' }}
|
||||
uses: zulip/github-actions-zulip/send-message@v1
|
||||
with:
|
||||
api-key: ${{ secrets.ZULIP_BOT_KEY }}
|
||||
email: "github-actions-bot@lean-fro.zulipchat.com"
|
||||
organization-url: "https://lean-fro.zulipchat.com"
|
||||
to: "infrastructure"
|
||||
topic: "Github actions"
|
||||
type: "stream"
|
||||
content: |
|
||||
A build of `${{ github.ref_name }}`, triggered by event `${{ github.event_name }}`, [failed](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
|
||||
- if: contains(needs.*.result, 'failure')
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Some jobs failed')
|
||||
|
||||
|
||||
# This job creates releases from tags
|
||||
# (whether they are "unofficial" releases for experiments, or official releases when the tag is "v" followed by a semver string.)
|
||||
# We do not attempt to automatically construct a changelog here:
|
||||
@@ -533,3 +545,8 @@ jobs:
|
||||
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
34
.github/workflows/jira.yml
vendored
Normal 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"
|
||||
@@ -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.
|
||||
|
||||
@@ -149,4 +149,4 @@ def fact : Expr ctx (Ty.fn Ty.int Ty.int) :=
|
||||
(op (·*·) (delay fun _ => app fact (op (·-·) (var stop) (val 1))) (var stop)))
|
||||
decreasing_by sorry
|
||||
|
||||
#eval fact.interp Env.nil 10
|
||||
#eval! fact.interp Env.nil 10
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 65 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 33 KiB |
@@ -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
|
||||
|
||||
@@ -7,12 +7,17 @@ See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
|
||||
|
||||
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
|
||||
|
||||

|
||||

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

|
||||

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

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

|
||||
|
||||
65
releases_drafts/mutualStructural.md
Normal file
65
releases_drafts/mutualStructural.md
Normal file
@@ -0,0 +1,65 @@
|
||||
* Structural recursion can now be explicitly requested using
|
||||
```
|
||||
termination_by structural x
|
||||
```
|
||||
in analogy to the existing `termination_by x` syntax that causes well-founded recursion to be used.
|
||||
(#4542)
|
||||
|
||||
* The `termination_by?` syntax no longer forces the use of well-founded recursion, and when structural
|
||||
recursion is inferred, will print the result using the `termination_by` syntax.
|
||||
|
||||
* Mutual structural recursion is supported now. This supports both mutual recursion over a non-mutual
|
||||
data type, as well as recursion over mutual or nested data types:
|
||||
|
||||
```lean
|
||||
mutual
|
||||
def Even : Nat → Prop
|
||||
| 0 => True
|
||||
| n+1 => Odd n
|
||||
|
||||
def Odd : Nat → Prop
|
||||
| 0 => False
|
||||
| n+1 => Even n
|
||||
end
|
||||
|
||||
mutual
|
||||
inductive A
|
||||
| other : B → A
|
||||
| empty
|
||||
inductive B
|
||||
| other : A → B
|
||||
| empty
|
||||
end
|
||||
|
||||
mutual
|
||||
def A.size : A → Nat
|
||||
| .other b => b.size + 1
|
||||
| .empty => 0
|
||||
|
||||
def B.size : B → Nat
|
||||
| .other a => a.size + 1
|
||||
| .empty => 0
|
||||
end
|
||||
|
||||
inductive Tree where | node : List Tree → Tree
|
||||
|
||||
mutual
|
||||
def Tree.size : Tree → Nat
|
||||
| node ts => Tree.list_size ts
|
||||
|
||||
def Tree.list_size : List Tree → Nat
|
||||
| [] => 0
|
||||
| t::ts => Tree.size t + Tree.list_size ts
|
||||
end
|
||||
```
|
||||
|
||||
Functional induction principles are generated for these functions as well (`A.size.induct`, `A.size.mutual_induct`).
|
||||
|
||||
Nested structural recursion is still not supported.
|
||||
|
||||
PRs #4639, #4715, #4642, #4656, #4684, #4715, #4728, #4575, #4731, #4658, #4734, #4738, #4718,
|
||||
#4733, #4787, #4788, #4789, #4807, #4772
|
||||
|
||||
* A bugfix in the structural recursion code may in some cases break existing code, when a parameter
|
||||
of the type of the recursive argument is bound behind indices of that type. This can usually be
|
||||
fixed by reordering the parameters of the function (PR #4672)
|
||||
@@ -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()
|
||||
|
||||
@@ -67,12 +67,8 @@ theorem ite_some_none_eq_none [Decidable P] :
|
||||
-- This is not marked as `simp` as it is already handled by `dite_eq_right_iff`.
|
||||
theorem dite_some_none_eq_none [Decidable P] {x : P → α} :
|
||||
(if h : P then some (x h) else none) = none ↔ ¬P := by
|
||||
simp only [dite_eq_right_iff]
|
||||
rfl
|
||||
simp
|
||||
|
||||
@[simp] theorem dite_some_none_eq_some [Decidable P] {x : P → α} {y : α} :
|
||||
(if h : P then some (x h) else none) = some y ↔ ∃ h : P, x h = y := by
|
||||
by_cases h : P <;> simp only [h, dite_cond_eq_true, dite_cond_eq_false, Option.some.injEq,
|
||||
false_iff, not_exists]
|
||||
case pos => exact ⟨fun h_eq ↦ Exists.intro h h_eq, fun h_exists => h_exists.2⟩
|
||||
case neg => exact fun h_false _ ↦ h_false
|
||||
by_cases h : P <;> simp [h]
|
||||
|
||||
@@ -474,6 +474,8 @@ class LawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert
|
||||
insert_emptyc_eq (x : α) : (insert x ∅ : β) = singleton x
|
||||
export LawfulSingleton (insert_emptyc_eq)
|
||||
|
||||
attribute [simp] insert_emptyc_eq
|
||||
|
||||
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
|
||||
class Sep (α : outParam <| Type u) (γ : Type v) where
|
||||
/-- Computes `{ a ∈ c | p a }`. -/
|
||||
@@ -701,7 +703,7 @@ theorem Ne.elim (h : a ≠ b) : a = b → False := h
|
||||
|
||||
theorem Ne.irrefl (h : a ≠ a) : False := h rfl
|
||||
|
||||
theorem Ne.symm (h : a ≠ b) : b ≠ a := fun h₁ => h (h₁.symm)
|
||||
@[symm] theorem Ne.symm (h : a ≠ b) : b ≠ a := fun h₁ => h (h₁.symm)
|
||||
|
||||
theorem ne_comm {α} {a b : α} : a ≠ b ↔ b ≠ a := ⟨Ne.symm, Ne.symm⟩
|
||||
|
||||
@@ -754,7 +756,7 @@ noncomputable def HEq.elim {α : Sort u} {a : α} {p : α → Sort v} {b : α} (
|
||||
theorem HEq.subst {p : (T : Sort u) → T → Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
|
||||
HEq.ndrecOn h₁ h₂
|
||||
|
||||
theorem HEq.symm (h : HEq a b) : HEq b a :=
|
||||
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
|
||||
h.rec (HEq.refl a)
|
||||
|
||||
theorem heq_of_eq (h : a = a') : HEq a a' :=
|
||||
@@ -810,15 +812,15 @@ instance : Trans Iff Iff Iff where
|
||||
theorem Eq.comm {a b : α} : a = b ↔ b = a := Iff.intro Eq.symm Eq.symm
|
||||
theorem eq_comm {a b : α} : a = b ↔ b = a := Eq.comm
|
||||
|
||||
theorem Iff.symm (h : a ↔ b) : b ↔ a := Iff.intro h.mpr h.mp
|
||||
@[symm] theorem Iff.symm (h : a ↔ b) : b ↔ a := Iff.intro h.mpr h.mp
|
||||
theorem Iff.comm: (a ↔ b) ↔ (b ↔ a) := Iff.intro Iff.symm Iff.symm
|
||||
theorem iff_comm : (a ↔ b) ↔ (b ↔ a) := Iff.comm
|
||||
|
||||
theorem And.symm : a ∧ b → b ∧ a := fun ⟨ha, hb⟩ => ⟨hb, ha⟩
|
||||
@[symm] theorem And.symm : a ∧ b → b ∧ a := fun ⟨ha, hb⟩ => ⟨hb, ha⟩
|
||||
theorem And.comm : a ∧ b ↔ b ∧ a := Iff.intro And.symm And.symm
|
||||
theorem and_comm : a ∧ b ↔ b ∧ a := And.comm
|
||||
|
||||
theorem Or.symm : a ∨ b → b ∨ a := .rec .inr .inl
|
||||
@[symm] theorem Or.symm : a ∨ b → b ∨ a := .rec .inr .inl
|
||||
theorem Or.comm : a ∨ b ↔ b ∨ a := Iff.intro Or.symm Or.symm
|
||||
theorem or_comm : a ∨ b ↔ b ∨ a := Or.comm
|
||||
|
||||
@@ -1089,19 +1091,23 @@ def InvImage {α : Sort u} {β : Sort v} (r : β → β → Prop) (f : α → β
|
||||
fun a₁ a₂ => r (f a₁) (f a₂)
|
||||
|
||||
/--
|
||||
The transitive closure `r⁺` of a relation `r` is the smallest relation which is
|
||||
transitive and contains `r`. `r⁺ a z` if and only if there exists a sequence
|
||||
The transitive closure `TransGen r` of a relation `r` is the smallest relation which is
|
||||
transitive and contains `r`. `TransGen r a z` if and only if there exists a sequence
|
||||
`a r b r ... r z` of length at least 1 connecting `a` to `z`.
|
||||
-/
|
||||
inductive TC {α : Sort u} (r : α → α → Prop) : α → α → Prop where
|
||||
/-- If `r a b` then `r⁺ a b`. This is the base case of the transitive closure. -/
|
||||
| base : ∀ a b, r a b → TC r a b
|
||||
inductive Relation.TransGen {α : Sort u} (r : α → α → Prop) : α → α → Prop
|
||||
/-- If `r a b` then `TransGen r a b`. This is the base case of the transitive closure. -/
|
||||
| single {a b} : r a b → TransGen r a b
|
||||
/-- The transitive closure is transitive. -/
|
||||
| trans : ∀ a b c, TC r a b → TC r b c → TC r a c
|
||||
| tail {a b c} : TransGen r a b → r b c → TransGen r a c
|
||||
|
||||
/-- Deprecated synonym for `Relation.TransGen`. -/
|
||||
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
|
||||
|
||||
/-! # Subtype -/
|
||||
|
||||
namespace Subtype
|
||||
|
||||
theorem existsOfSubtype {α : Type u} {p : α → Prop} : { x // p x } → Exists (fun x => p x)
|
||||
| ⟨a, h⟩ => ⟨a, h⟩
|
||||
|
||||
@@ -1198,9 +1204,13 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
|
||||
|
||||
/-! # Dependent products -/
|
||||
|
||||
theorem ex_of_PSigma {α : Type u} {p : α → Prop} : (PSigma (fun x => p x)) → Exists (fun x => p x)
|
||||
theorem PSigma.exists {α : Sort u} {p : α → Prop} : (PSigma (fun x => p x)) → Exists (fun x => p x)
|
||||
| ⟨x, hx⟩ => ⟨x, hx⟩
|
||||
|
||||
@[deprecated PSigma.exists (since := "2024-07-27")]
|
||||
theorem ex_of_PSigma {α : Type u} {p : α → Prop} : (PSigma (fun x => p x)) → Exists (fun x => p x) :=
|
||||
PSigma.exists
|
||||
|
||||
protected theorem PSigma.eta {α : Sort u} {β : α → Sort v} {a₁ a₂ : α} {b₁ : β a₁} {b₂ : β a₂}
|
||||
(h₁ : a₁ = a₂) (h₂ : Eq.ndrec b₁ h₁ = b₂) : PSigma.mk a₁ b₁ = PSigma.mk a₂ b₂ := by
|
||||
subst h₁
|
||||
@@ -1542,7 +1552,7 @@ protected abbrev rec
|
||||
(q : Quot r) : motive q :=
|
||||
Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2)
|
||||
|
||||
@[inherit_doc Quot.rec] protected abbrev recOn
|
||||
@[inherit_doc Quot.rec, elab_as_elim] protected abbrev recOn
|
||||
(q : Quot r)
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
(h : (a b : α) → (p : r a b) → Eq.ndrec (f a) (sound p) = f b)
|
||||
@@ -1553,7 +1563,7 @@ protected abbrev rec
|
||||
Dependent induction principle for a quotient, when the target type is a `Subsingleton`.
|
||||
In this case the quotient's side condition is trivial so any function can be lifted.
|
||||
-/
|
||||
protected abbrev recOnSubsingleton
|
||||
@[elab_as_elim] protected abbrev recOnSubsingleton
|
||||
[h : (a : α) → Subsingleton (motive (Quot.mk r a))]
|
||||
(q : Quot r)
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
|
||||
@@ -36,3 +36,4 @@ import Init.Data.Channel
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Sum
|
||||
import Init.Data.BEq
|
||||
import Init.Data.Subtype
|
||||
|
||||
@@ -50,6 +50,13 @@ instance : Inhabited (Array α) where
|
||||
def singleton (v : α) : Array α :=
|
||||
mkArray 1 v
|
||||
|
||||
/-- Low-level version of `size` that directly queries the C array object cached size.
|
||||
While this is not provable, `usize` always returns the exact size of the array since
|
||||
the implementation only supports arrays of size less than `USize.size`.
|
||||
-/
|
||||
@[extern "lean_array_size", simp]
|
||||
def usize (a : @& Array α) : USize := a.size.toUSize
|
||||
|
||||
/-- Low-level version of `fget` which is as fast as a C array read.
|
||||
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
|
||||
`fget` may be slightly slower than `uget`. -/
|
||||
@@ -174,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
|
||||
@@ -280,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
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Mario Carneiro
|
||||
prelude
|
||||
import Init.Data.Nat.MinMax
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.Monadic
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.Array.Mem
|
||||
import Init.TacticsExtra
|
||||
@@ -51,7 +51,7 @@ theorem foldlM_eq_foldlM_data.aux [Monad m]
|
||||
simp [foldlM_eq_foldlM_data.aux f arr i (j+1) H]
|
||||
rw (config := {occs := .pos [2]}) [← List.get_drop_eq_drop _ _ ‹_›]
|
||||
rfl
|
||||
· rw [List.drop_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
|
||||
theorem foldlM_eq_foldlM_data [Monad m]
|
||||
(f : β → α → m β) (init : β) (arr : Array α) :
|
||||
@@ -141,7 +141,7 @@ where
|
||||
· rw [← List.get_drop_eq_drop _ i ‹_›]
|
||||
simp only [aux (i + 1), map_eq_pure_bind, data_length, List.foldlM_cons, bind_assoc, pure_bind]
|
||||
rfl
|
||||
· rw [List.drop_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
termination_by arr.size - i
|
||||
decreasing_by decreasing_trivial_pre_omega
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Authors: Markus Himmel
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.TakeDrop
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
|
||||
namespace Array
|
||||
|
||||
|
||||
@@ -98,6 +98,37 @@ theorem carry_succ (i : Nat) (x y : BitVec w) (c : Bool) :
|
||||
exact mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ ..
|
||||
cases x.toNat.testBit i <;> cases y.toNat.testBit i <;> (simp; omega)
|
||||
|
||||
/--
|
||||
If `x &&& y = 0`, then the carry bit `(x + y + 0)` is always `false` for any index `i`.
|
||||
Intuitively, this is because a carry is only produced when at least two of `x`, `y`, and the
|
||||
previous carry are true. However, since `x &&& y = 0`, at most one of `x, y` can be true,
|
||||
and thus we never have a previous carry, which means that the sum cannot produce a carry.
|
||||
-/
|
||||
theorem carry_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) : carry i x y false = false := by
|
||||
induction i with
|
||||
| zero => simp
|
||||
| succ i ih =>
|
||||
replace h := congrArg (·.getLsb i) h
|
||||
simp_all [carry_succ]
|
||||
|
||||
/-- The final carry bit when computing `x + y + c` is `true` iff `x.toNat + y.toNat + c.toNat ≥ 2^w`. -/
|
||||
theorem carry_width {x y : BitVec w} :
|
||||
carry w x y c = decide (x.toNat + y.toNat + c.toNat ≥ 2^w) := by
|
||||
simp [carry]
|
||||
|
||||
/--
|
||||
If `x &&& y = 0`, then addition does not overflow, and thus `(x + y).toNat = x.toNat + y.toNat`.
|
||||
-/
|
||||
theorem toNat_add_of_and_eq_zero {x y : BitVec w} (h : x &&& y = 0#w) :
|
||||
(x + y).toNat = x.toNat + y.toNat := by
|
||||
rw [toNat_add]
|
||||
apply Nat.mod_eq_of_lt
|
||||
suffices ¬ decide (x.toNat + y.toNat + false.toNat ≥ 2^w) by
|
||||
simp only [decide_eq_true_eq] at this
|
||||
omega
|
||||
rw [← carry_width]
|
||||
simp [not_eq_true, carry_of_and_eq_zero h]
|
||||
|
||||
/-- Carry function for bitwise addition. -/
|
||||
def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, Bool.xor x (Bool.xor y c))
|
||||
|
||||
@@ -290,7 +321,7 @@ theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow (x : BitVec w
|
||||
simp [hik', hik'']
|
||||
· ext k
|
||||
simp
|
||||
omega
|
||||
by_cases hi : x.getLsb i <;> simp [hi] <;> omega
|
||||
|
||||
/--
|
||||
Recurrence lemma: multiplying `l` with the first `s` bits of `r` is the
|
||||
@@ -314,7 +345,7 @@ theorem mulRec_eq_mul_signExtend_truncate (l r : BitVec w) (s : Nat) :
|
||||
have heq :
|
||||
(if r.getLsb (s' + 1) = true then l <<< (s' + 1) else 0) =
|
||||
(l * (r &&& (BitVec.twoPow w (s' + 1)))) := by
|
||||
simp only [ofNat_eq_ofNat, and_twoPow_eq]
|
||||
simp only [ofNat_eq_ofNat, and_twoPow]
|
||||
by_cases hr : r.getLsb (s' + 1) <;> simp [hr]
|
||||
rw [heq, ← BitVec.mul_add, ← zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow]
|
||||
|
||||
@@ -326,4 +357,78 @@ theorem getLsb_mul (x y : BitVec w) (i : Nat) :
|
||||
· simp
|
||||
· omega
|
||||
|
||||
/-! ## shiftLeft recurrence for bitblasting -/
|
||||
|
||||
/--
|
||||
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
|
||||
|
||||
The theorem `shiftLeft_eq_shiftLeftRec` proves the equivalence of `(x <<< y)` and `shiftLeftRec`.
|
||||
|
||||
Together with equations `shiftLeftRec_zero`, `shiftLeftRec_succ`,
|
||||
this allows us to unfold `shiftLeft` into a circuit for bitblasting.
|
||||
-/
|
||||
def shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) (n : Nat) : BitVec w₁ :=
|
||||
let shiftAmt := (y &&& (twoPow w₂ n))
|
||||
match n with
|
||||
| 0 => x <<< shiftAmt
|
||||
| n + 1 => (shiftLeftRec x y n) <<< shiftAmt
|
||||
|
||||
@[simp]
|
||||
theorem shiftLeftRec_zero {x : BitVec w₁} {y : BitVec w₂} :
|
||||
shiftLeftRec x y 0 = x <<< (y &&& twoPow w₂ 0) := by
|
||||
simp [shiftLeftRec]
|
||||
|
||||
@[simp]
|
||||
theorem shiftLeftRec_succ {x : BitVec w₁} {y : BitVec w₂} :
|
||||
shiftLeftRec x y (n + 1) = (shiftLeftRec x y n) <<< (y &&& twoPow w₂ (n + 1)) := by
|
||||
simp [shiftLeftRec]
|
||||
|
||||
/--
|
||||
If `y &&& z = 0`, `x <<< (y ||| z) = x <<< y <<< z`.
|
||||
This follows as `y &&& z = 0` implies `y ||| z = y + z`,
|
||||
and thus `x <<< (y ||| z) = x <<< (y + z) = x <<< y <<< z`.
|
||||
-/
|
||||
theorem shiftLeft_or_of_and_eq_zero {x : BitVec w₁} {y z : BitVec w₂}
|
||||
(h : y &&& z = 0#w₂) :
|
||||
x <<< (y ||| z) = x <<< y <<< z := by
|
||||
rw [← add_eq_or_of_and_eq_zero _ _ h,
|
||||
shiftLeft_eq', toNat_add_of_and_eq_zero h]
|
||||
simp [shiftLeft_add]
|
||||
|
||||
/--
|
||||
`shiftLeftRec x y n` shifts `x` to the left by the first `n` bits of `y`.
|
||||
-/
|
||||
theorem shiftLeftRec_eq {x : BitVec w₁} {y : BitVec w₂} {n : Nat} :
|
||||
shiftLeftRec x y n = x <<< (y.truncate (n + 1)).zeroExtend w₂ := by
|
||||
induction n generalizing x y
|
||||
case zero =>
|
||||
ext i
|
||||
simp only [shiftLeftRec_zero, twoPow_zero, Nat.reduceAdd, truncate_one]
|
||||
suffices (y &&& 1#w₂) = zeroExtend w₂ (ofBool (y.getLsb 0)) by simp [this]
|
||||
ext i
|
||||
by_cases h : (↑i : Nat) = 0
|
||||
· simp [h, Bool.and_comm]
|
||||
· simp [h]; omega
|
||||
case succ n ih =>
|
||||
simp only [shiftLeftRec_succ, and_twoPow]
|
||||
rw [ih]
|
||||
by_cases h : y.getLsb (n + 1)
|
||||
· simp only [h, ↓reduceIte]
|
||||
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true h,
|
||||
shiftLeft_or_of_and_eq_zero]
|
||||
simp
|
||||
· simp only [h, false_eq_true, ↓reduceIte, shiftLeft_zero']
|
||||
rw [zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false (i := n + 1)]
|
||||
simp [h]
|
||||
|
||||
/--
|
||||
Show that `x <<< y` can be written in terms of `shiftLeftRec`.
|
||||
This can be unfolded in terms of `shiftLeftRec_zero`, `shiftLeftRec_succ` for bitblasting.
|
||||
-/
|
||||
theorem shiftLeft_eq_shiftLeftRec (x : BitVec w₁) (y : BitVec w₂) :
|
||||
x <<< y = shiftLeftRec x y (w₂ - 1) := by
|
||||
rcases w₂ with rfl | w₂
|
||||
· simp [of_length_zero]
|
||||
· simp [shiftLeftRec_eq]
|
||||
|
||||
end BitVec
|
||||
|
||||
@@ -436,6 +436,12 @@ theorem zeroExtend_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
|
||||
have hv := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi₁
|
||||
omega
|
||||
|
||||
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
|
||||
theorem truncate_one {x : BitVec w} :
|
||||
x.truncate 1 = ofBool (x.getLsb 0) := by
|
||||
ext i
|
||||
simp [show i = 0 by omega]
|
||||
|
||||
/-! ## extractLsb -/
|
||||
|
||||
@[simp]
|
||||
@@ -531,6 +537,11 @@ theorem and_assoc (x y z : BitVec w) :
|
||||
ext i
|
||||
simp [Bool.and_assoc]
|
||||
|
||||
theorem and_comm (x y : BitVec w) :
|
||||
x &&& y = y &&& x := by
|
||||
ext i
|
||||
simp [Bool.and_comm]
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
@[simp] theorem toNat_xor (x y : BitVec v) :
|
||||
@@ -626,6 +637,10 @@ theorem shiftLeft_zero_eq (x : BitVec w) : x <<< 0 = x := by
|
||||
apply eq_of_toNat_eq
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem zero_shiftLeft (n : Nat) : 0#w <<< n = 0#w := by
|
||||
simp [bv_toNat]
|
||||
|
||||
@[simp] theorem getLsb_shiftLeft (x : BitVec m) (n) :
|
||||
getLsb (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsb x (i - n)) := by
|
||||
rw [← testBit_toNat, getLsb]
|
||||
@@ -691,6 +706,22 @@ theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
|
||||
(x <<< n) <<< m = x <<< (n + m) := by
|
||||
rw [shiftLeft_add]
|
||||
|
||||
/-! ### shiftLeft reductions from BitVec to Nat -/
|
||||
|
||||
@[simp]
|
||||
theorem shiftLeft_eq' {x : BitVec w₁} {y : BitVec w₂} : x <<< y = x <<< y.toNat := by rfl
|
||||
|
||||
@[simp]
|
||||
theorem shiftLeft_zero' {x : BitVec w₁} : x <<< 0#w₂ = x := by simp
|
||||
|
||||
theorem shiftLeft_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {z : BitVec w₃} :
|
||||
x <<< y <<< z = x <<< (y.toNat + z.toNat) := by
|
||||
simp [shiftLeft_add]
|
||||
|
||||
theorem getLsb_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} :
|
||||
(x <<< y).getLsb i = (decide (i < w₁) && !decide (i < y.toNat) && x.getLsb (i - y.toNat)) := by
|
||||
simp [shiftLeft_eq', getLsb_shiftLeft]
|
||||
|
||||
/-! ### ushiftRight -/
|
||||
|
||||
@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
|
||||
@@ -1452,12 +1483,18 @@ theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j))
|
||||
simp at hi
|
||||
simp_all
|
||||
|
||||
theorem and_twoPow_eq (x : BitVec w) (i : Nat) :
|
||||
@[simp]
|
||||
theorem and_twoPow (x : BitVec w) (i : Nat) :
|
||||
x &&& (twoPow w i) = if x.getLsb i then twoPow w i else 0#w := by
|
||||
ext j
|
||||
simp only [getLsb_and, getLsb_twoPow]
|
||||
by_cases hj : i = j <;> by_cases hx : x.getLsb i <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem twoPow_and (x : BitVec w) (i : Nat) :
|
||||
(twoPow w i) &&& x = if x.getLsb i then twoPow w i else 0#w := by
|
||||
rw [BitVec.and_comm, and_twoPow]
|
||||
|
||||
@[simp]
|
||||
theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
|
||||
x * (twoPow w i) = x <<< i := by
|
||||
@@ -1471,6 +1508,14 @@ theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
|
||||
apply Nat.pow_dvd_pow 2 (by omega)
|
||||
simp [Nat.mul_mod, hpow]
|
||||
|
||||
theorem twoPow_zero {w : Nat} : twoPow w 0 = 1#w := by
|
||||
apply eq_of_toNat_eq
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem getLsb_one {w i : Nat} : (1#w).getLsb i = (decide (0 < w) && decide (0 = i)) := by
|
||||
rw [← twoPow_zero, getLsb_twoPow]
|
||||
|
||||
/- ### zeroExtend, truncate, and bitwise operations -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -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]
|
||||
@@ -119,7 +123,7 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
@@ -90,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
|
||||
|
||||
@@ -4,11 +4,20 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Attach
|
||||
import Init.Data.List.Basic
|
||||
import Init.Data.List.BasicAux
|
||||
import Init.Data.List.Control
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.Attach
|
||||
import Init.Data.List.Count
|
||||
import Init.Data.List.Erase
|
||||
import Init.Data.List.Find
|
||||
import Init.Data.List.Impl
|
||||
import Init.Data.List.TakeDrop
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.MinMax
|
||||
import Init.Data.List.Monadic
|
||||
import Init.Data.List.Nat
|
||||
import Init.Data.List.Notation
|
||||
import Init.Data.List.Pairwise
|
||||
import Init.Data.List.Sublist
|
||||
import Init.Data.List.TakeDrop
|
||||
import Init.Data.List.Zip
|
||||
|
||||
@@ -4,7 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.Count
|
||||
import Init.Data.Subtype
|
||||
|
||||
namespace List
|
||||
|
||||
@@ -44,3 +45,155 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
|
||||
| nil, hL' => rfl
|
||||
| cons _ L', hL' => congrArg _ <| go L' fun _ hx => hL' (.tail _ hx)
|
||||
exact go L h'
|
||||
|
||||
@[simp] theorem attach_nil : ([] : List α).attach = [] := rfl
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_map (p : α → Prop) (f : α → β) (l : List α) (H) :
|
||||
@pmap _ _ p (fun a _ => f a) l H = map f l := by
|
||||
induction l
|
||||
· rfl
|
||||
· simp only [*, pmap, map]
|
||||
|
||||
theorem pmap_congr {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a, q a → β} (l : List α) {H₁ H₂}
|
||||
(h : ∀ a ∈ l, ∀ (h₁ h₂), f a h₁ = g a h₂) : pmap f l H₁ = pmap g l H₂ := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x l ih => rw [pmap, pmap, h _ (mem_cons_self _ _), ih fun a ha => h a (mem_cons_of_mem _ ha)]
|
||||
|
||||
theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (l H) :
|
||||
map g (pmap f l H) = pmap (fun a h => g (f a h)) l H := by
|
||||
induction l
|
||||
· rfl
|
||||
· simp only [*, pmap, map]
|
||||
|
||||
theorem pmap_map {p : β → Prop} (g : ∀ b, p b → γ) (f : α → β) (l H) :
|
||||
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun a h => H _ (mem_map_of_mem _ h) := by
|
||||
induction l
|
||||
· rfl
|
||||
· simp only [*, pmap, map]
|
||||
|
||||
theorem pmap_eq_map_attach {p : α → Prop} (f : ∀ a, p a → β) (l H) :
|
||||
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_congr l fun _ _ _ _ => rfl
|
||||
|
||||
theorem attach_map_coe (l : List α) (f : α → β) :
|
||||
(l.attach.map fun (i : {i // i ∈ l}) => f i) = l.map f := by
|
||||
rw [attach, attachWith, map_pmap]; exact pmap_eq_map _ _ _ _
|
||||
|
||||
theorem attach_map_val (l : List α) (f : α → β) : (l.attach.map fun i => f i.val) = l.map f :=
|
||||
attach_map_coe _ _
|
||||
|
||||
@[simp]
|
||||
theorem attach_map_subtype_val (l : List α) : l.attach.map Subtype.val = l :=
|
||||
(attach_map_coe _ _).trans l.map_id
|
||||
|
||||
theorem countP_attach (l : List α) (p : α → Bool) : l.attach.countP (fun a : {x // x ∈ l} => p a) = l.countP p := by
|
||||
simp only [← Function.comp_apply (g := Subtype.val), ← countP_map, attach_map_subtype_val]
|
||||
|
||||
@[simp]
|
||||
theorem count_attach [DecidableEq α] (l : List α) (a : {x // x ∈ l}) : l.attach.count a = l.count ↑a :=
|
||||
Eq.trans (countP_congr fun _ _ => by simp [Subtype.ext_iff]) <| countP_attach _ _
|
||||
|
||||
@[simp]
|
||||
theorem mem_attach (l : List α) : ∀ x, x ∈ l.attach
|
||||
| ⟨a, h⟩ => by
|
||||
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
|
||||
rcases this with ⟨⟨_, _⟩, m, rfl⟩
|
||||
exact m
|
||||
|
||||
@[simp]
|
||||
theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H b} :
|
||||
b ∈ pmap f l H ↔ ∃ (a : _) (h : a ∈ l), f a (H a h) = b := by
|
||||
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
|
||||
|
||||
@[simp]
|
||||
theorem length_pmap {p : α → Prop} {f : ∀ a, p a → β} {l H} : length (pmap f l H) = length l := by
|
||||
induction l
|
||||
· rfl
|
||||
· simp only [*, pmap, length]
|
||||
|
||||
@[simp]
|
||||
theorem length_attach (L : List α) : L.attach.length = L.length :=
|
||||
length_pmap
|
||||
|
||||
@[simp]
|
||||
theorem pmap_eq_nil {p : α → Prop} {f : ∀ a, p a → β} {l H} : pmap f l H = [] ↔ l = [] := by
|
||||
rw [← length_eq_zero, length_pmap, length_eq_zero]
|
||||
|
||||
@[simp]
|
||||
theorem attach_eq_nil (l : List α) : l.attach = [] ↔ l = [] :=
|
||||
pmap_eq_nil
|
||||
|
||||
theorem getLast_pmap (p : α → Prop) (f : ∀ a, p a → β) (l : List α)
|
||||
(hl₁ : ∀ a ∈ l, p a) (hl₂ : l ≠ []) :
|
||||
(l.pmap f hl₁).getLast (mt List.pmap_eq_nil.1 hl₂) =
|
||||
f (l.getLast hl₂) (hl₁ _ (List.getLast_mem hl₂)) := by
|
||||
induction l with
|
||||
| nil => apply (hl₂ rfl).elim
|
||||
| cons l_hd l_tl l_ih =>
|
||||
by_cases hl_tl : l_tl = []
|
||||
· simp [hl_tl]
|
||||
· simp only [pmap]
|
||||
rw [getLast_cons, l_ih _ hl_tl]
|
||||
simp only [getLast_cons hl_tl]
|
||||
|
||||
theorem getElem?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) (n : Nat) :
|
||||
(pmap f l h)[n]? = Option.pmap f l[n]? fun x H => h x (getElem?_mem H) := by
|
||||
induction l generalizing n with
|
||||
| nil => simp
|
||||
| cons hd tl hl =>
|
||||
rcases n with ⟨n⟩
|
||||
· simp only [Option.pmap]
|
||||
split <;> simp_all
|
||||
· simp only [hl, pmap, Option.pmap, getElem?_cons_succ]
|
||||
split <;> rename_i h₁ _ <;> split <;> rename_i h₂ _
|
||||
· simp_all
|
||||
· simp at h₂
|
||||
simp_all
|
||||
· simp_all
|
||||
· simp_all
|
||||
|
||||
theorem get?_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) (n : Nat) :
|
||||
get? (pmap f l h) n = Option.pmap f (get? l n) fun x H => h x (get?_mem H) := by
|
||||
simp only [get?_eq_getElem?]
|
||||
simp [getElem?_pmap, h]
|
||||
|
||||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) {n : Nat}
|
||||
(hn : n < (pmap f l h).length) :
|
||||
(pmap f l h)[n] =
|
||||
f (l[n]'(@length_pmap _ _ p f l h ▸ hn))
|
||||
(h _ (getElem_mem l n (@length_pmap _ _ p f l h ▸ hn))) := by
|
||||
induction l generalizing n with
|
||||
| nil =>
|
||||
simp only [length, pmap] at hn
|
||||
exact absurd hn (Nat.not_lt_of_le n.zero_le)
|
||||
| cons hd tl hl =>
|
||||
cases n
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
theorem get_pmap {p : α → Prop} (f : ∀ a, p a → β) {l : List α} (h : ∀ a ∈ l, p a) {n : Nat}
|
||||
(hn : n < (pmap f l h).length) :
|
||||
get (pmap f l h) ⟨n, hn⟩ =
|
||||
f (get l ⟨n, @length_pmap _ _ p f l h ▸ hn⟩)
|
||||
(h _ (get_mem l n (@length_pmap _ _ p f l h ▸ hn))) := by
|
||||
simp only [get_eq_getElem]
|
||||
simp [getElem_pmap]
|
||||
|
||||
theorem pmap_append {p : ι → Prop} (f : ∀ a : ι, p a → α) (l₁ l₂ : List ι)
|
||||
(h : ∀ a ∈ l₁ ++ l₂, p a) :
|
||||
(l₁ ++ l₂).pmap f h =
|
||||
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
|
||||
l₂.pmap f fun a ha => h a (mem_append_right l₁ ha) := by
|
||||
induction l₁ with
|
||||
| nil => rfl
|
||||
| cons _ _ ih =>
|
||||
dsimp only [pmap, cons_append]
|
||||
rw [ih]
|
||||
|
||||
theorem pmap_append' {p : α → Prop} (f : ∀ a : α, p a → β) (l₁ l₂ : List α)
|
||||
(h₁ : ∀ a ∈ l₁, p a) (h₂ : ∀ a ∈ l₂, p a) :
|
||||
((l₁ ++ l₂).pmap f fun a ha => (List.mem_append.1 ha).elim (h₁ a) (h₂ a)) =
|
||||
l₁.pmap f h₁ ++ l₂.pmap f h₂ :=
|
||||
pmap_append f l₁ l₂ _
|
||||
|
||||
@@ -22,29 +22,37 @@ along with `@[csimp]` lemmas,
|
||||
|
||||
In `Init.Data.List.Lemmas` we develop the full API for these functions.
|
||||
|
||||
Recall that `length`, `get`, `set`, `fold`, and `concat` have already been defined in `Init.Prelude`.
|
||||
Recall that `length`, `get`, `set`, `foldl`, and `concat` have already been defined in `Init.Prelude`.
|
||||
|
||||
The operations are organized as follow:
|
||||
* Equality: `beq`, `isEqv`.
|
||||
* Lexicographic ordering: `lt`, `le`, and instances.
|
||||
* Head and tail operators: `head`, `head?`, `headD?`, `tail`, `tail?`, `tailD`.
|
||||
* Basic operations:
|
||||
`map`, `filter`, `filterMap`, `foldr`, `append`, `join`, `pure`, `bind`, `replicate`, and `reverse`.
|
||||
`map`, `filter`, `filterMap`, `foldr`, `append`, `join`, `pure`, `bind`, `replicate`, and
|
||||
`reverse`.
|
||||
* Additional functions defined in terms of these: `leftpad`, `rightPad`, and `reduceOption`.
|
||||
* List membership: `isEmpty`, `elem`, `contains`, `mem` (and the `∈` notation),
|
||||
and decidability for predicates quantifying over membership in a `List`.
|
||||
* Sublists: `take`, `drop`, `takeWhile`, `dropWhile`, `partition`, `dropLast`,
|
||||
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`, `rotateLeft` and `rotateRight`.
|
||||
* Manipulating elements: `replace`, `insert`, `erase`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
|
||||
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`,
|
||||
`rotateLeft` and `rotateRight`.
|
||||
* Manipulating elements: `replace`, `insert`, `erase`, `eraseP`, `eraseIdx`.
|
||||
* Finding elements: `find?`, `findSome?`, `findIdx`, `indexOf`, `findIdx?`, `indexOf?`,
|
||||
`countP`, `count`, and `lookup`.
|
||||
* Logic: `any`, `all`, `or`, and `and`.
|
||||
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
|
||||
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
|
||||
* Minima and maxima: `minimum?` and `maximum?`.
|
||||
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`, `removeAll`
|
||||
* Other functions: `intersperse`, `intercalate`, `eraseDups`, `eraseReps`, `span`, `groupBy`,
|
||||
`removeAll`
|
||||
(currently these functions are mostly only used in meta code,
|
||||
and do not have API suitable for verification).
|
||||
|
||||
Further operations are defined in `Init.Data.List.BasicAux` (because they use `Array` in their implementations), namely:
|
||||
Further operations are defined in `Init.Data.List.BasicAux`
|
||||
(because they use `Array` in their implementations), namely:
|
||||
* Variant getters: `get!`, `get?`, `getD`, `getLast`, `getLast!`, `getLast?`, and `getLastD`.
|
||||
* Head and tail: `head`, `head!`, `head?`, `headD`, `tail!`, `tail?`, and `tailD`.
|
||||
* Head and tail: `head!`, `tail!`.
|
||||
* Other operations on sublists: `partitionMap`, `rotateLeft`, and `rotateRight`.
|
||||
-/
|
||||
|
||||
@@ -315,6 +323,16 @@ def headD : (as : List α) → (fallback : α) → α
|
||||
@[simp 1100] theorem headD_nil : @headD α [] d = d := rfl
|
||||
@[simp 1100] theorem headD_cons : @headD α (a::l) d = a := rfl
|
||||
|
||||
/-! ### tail -/
|
||||
|
||||
/-- Get the tail of a nonempty list, or return `[]` for `[]`. -/
|
||||
def tail : List α → List α
|
||||
| [] => []
|
||||
| _::as => as
|
||||
|
||||
@[simp] theorem tail_nil : @tail α [] = [] := rfl
|
||||
@[simp] theorem tail_cons : @tail α (a::as) = as := rfl
|
||||
|
||||
/-! ### tail? -/
|
||||
|
||||
/--
|
||||
@@ -577,6 +595,28 @@ theorem replicate_succ (a : α) (n) : replicate (n+1) a = a :: replicate n a :=
|
||||
| zero => simp
|
||||
| succ n ih => simp only [ih, replicate_succ, length_cons, Nat.succ_eq_add_one]
|
||||
|
||||
/-! ## Additional functions -/
|
||||
|
||||
/-! ### leftpad and rightpad -/
|
||||
|
||||
/--
|
||||
Pads `l : List α` on the left with repeated occurrences of `a : α` until it is of length `n`.
|
||||
If `l` is initially larger than `n`, just return `l`.
|
||||
-/
|
||||
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
|
||||
|
||||
/--
|
||||
Pads `l : List α` on the right with repeated occurrences of `a : α` until it is of length `n`.
|
||||
If `l` is initially larger than `n`, just return `l`.
|
||||
-/
|
||||
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
|
||||
|
||||
/-! ### reduceOption -/
|
||||
|
||||
/-- Drop `none`s from a list, and replace each remaining `some a` with `a`. -/
|
||||
@[inline] def reduceOption {α} : List (Option α) → List α :=
|
||||
List.filterMap id
|
||||
|
||||
/-! ## List membership
|
||||
|
||||
* `L.contains a : Bool` determines, using a `[BEq α]` instance, whether `L` contains an element `· == a`.
|
||||
@@ -719,7 +759,7 @@ def take : Nat → List α → List α
|
||||
|
||||
@[simp] theorem take_nil : ([] : List α).take i = [] := by cases i <;> rfl
|
||||
@[simp] theorem take_zero (l : List α) : l.take 0 = [] := rfl
|
||||
@[simp] theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
@[simp] theorem take_succ_cons : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
|
||||
/-! ### drop -/
|
||||
|
||||
@@ -826,46 +866,6 @@ def dropLast {α} : List α → List α
|
||||
have ih := length_dropLast_cons b bs
|
||||
simp [dropLast, ih]
|
||||
|
||||
/-! ### isPrefixOf -/
|
||||
|
||||
/-- `isPrefixOf l₁ l₂` returns `true` Iff `l₁` is a prefix of `l₂`.
|
||||
That is, there exists a `t` such that `l₂ == l₁ ++ t`. -/
|
||||
def isPrefixOf [BEq α] : List α → List α → Bool
|
||||
| [], _ => true
|
||||
| _, [] => false
|
||||
| a::as, b::bs => a == b && isPrefixOf as bs
|
||||
|
||||
@[simp] theorem isPrefixOf_nil_left [BEq α] : isPrefixOf ([] : List α) l = true := by
|
||||
simp [isPrefixOf]
|
||||
@[simp] theorem isPrefixOf_cons_nil [BEq α] : isPrefixOf (a::as) ([] : List α) = false := rfl
|
||||
theorem isPrefixOf_cons₂ [BEq α] {a : α} :
|
||||
isPrefixOf (a::as) (b::bs) = (a == b && isPrefixOf as bs) := rfl
|
||||
|
||||
/-! ### isPrefixOf? -/
|
||||
|
||||
/-- `isPrefixOf? l₁ l₂` returns `some t` when `l₂ == l₁ ++ t`. -/
|
||||
def isPrefixOf? [BEq α] : List α → List α → Option (List α)
|
||||
| [], l₂ => some l₂
|
||||
| _, [] => none
|
||||
| (x₁ :: l₁), (x₂ :: l₂) =>
|
||||
if x₁ == x₂ then isPrefixOf? l₁ l₂ else none
|
||||
|
||||
/-! ### isSuffixOf -/
|
||||
|
||||
/-- `isSuffixOf l₁ l₂` returns `true` Iff `l₁` is a suffix of `l₂`.
|
||||
That is, there exists a `t` such that `l₂ == t ++ l₁`. -/
|
||||
def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
|
||||
isPrefixOf l₁.reverse l₂.reverse
|
||||
|
||||
@[simp] theorem isSuffixOf_nil_left [BEq α] : isSuffixOf ([] : List α) l = true := by
|
||||
simp [isSuffixOf]
|
||||
|
||||
/-! ### isSuffixOf? -/
|
||||
|
||||
/-- `isSuffixOf? l₁ l₂` returns `some t` when `l₂ == t ++ l₁`.-/
|
||||
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
|
||||
Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
|
||||
|
||||
/-! ### Subset -/
|
||||
|
||||
/--
|
||||
@@ -900,6 +900,68 @@ def isSublist [BEq α] : List α → List α → Bool
|
||||
then tl₁.isSublist tl₂
|
||||
else l₁.isSublist tl₂
|
||||
|
||||
/-! ### IsPrefix / isPrefixOf / isPrefixOf? -/
|
||||
|
||||
/--
|
||||
`IsPrefix l₁ l₂`, or `l₁ <+: l₂`, means that `l₁` is a prefix of `l₂`,
|
||||
that is, `l₂` has the form `l₁ ++ t` for some `t`.
|
||||
-/
|
||||
def IsPrefix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => l₁ ++ t = l₂
|
||||
|
||||
@[inherit_doc] infixl:50 " <+: " => IsPrefix
|
||||
|
||||
/-- `isPrefixOf l₁ l₂` returns `true` Iff `l₁` is a prefix of `l₂`.
|
||||
That is, there exists a `t` such that `l₂ == l₁ ++ t`. -/
|
||||
def isPrefixOf [BEq α] : List α → List α → Bool
|
||||
| [], _ => true
|
||||
| _, [] => false
|
||||
| a::as, b::bs => a == b && isPrefixOf as bs
|
||||
|
||||
@[simp] theorem isPrefixOf_nil_left [BEq α] : isPrefixOf ([] : List α) l = true := by
|
||||
simp [isPrefixOf]
|
||||
@[simp] theorem isPrefixOf_cons_nil [BEq α] : isPrefixOf (a::as) ([] : List α) = false := rfl
|
||||
theorem isPrefixOf_cons₂ [BEq α] {a : α} :
|
||||
isPrefixOf (a::as) (b::bs) = (a == b && isPrefixOf as bs) := rfl
|
||||
|
||||
/-- `isPrefixOf? l₁ l₂` returns `some t` when `l₂ == l₁ ++ t`. -/
|
||||
def isPrefixOf? [BEq α] : List α → List α → Option (List α)
|
||||
| [], l₂ => some l₂
|
||||
| _, [] => none
|
||||
| (x₁ :: l₁), (x₂ :: l₂) =>
|
||||
if x₁ == x₂ then isPrefixOf? l₁ l₂ else none
|
||||
|
||||
/-! ### IsSuffix / isSuffixOf / isSuffixOf? -/
|
||||
|
||||
/-- `isSuffixOf l₁ l₂` returns `true` Iff `l₁` is a suffix of `l₂`.
|
||||
That is, there exists a `t` such that `l₂ == t ++ l₁`. -/
|
||||
def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
|
||||
isPrefixOf l₁.reverse l₂.reverse
|
||||
|
||||
@[simp] theorem isSuffixOf_nil_left [BEq α] : isSuffixOf ([] : List α) l = true := by
|
||||
simp [isSuffixOf]
|
||||
|
||||
/-- `isSuffixOf? l₁ l₂` returns `some t` when `l₂ == t ++ l₁`.-/
|
||||
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
|
||||
Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
|
||||
|
||||
/--
|
||||
`IsSuffix l₁ l₂`, or `l₁ <:+ l₂`, means that `l₁` is a suffix of `l₂`,
|
||||
that is, `l₂` has the form `t ++ l₁` for some `t`.
|
||||
-/
|
||||
def IsSuffix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => t ++ l₁ = l₂
|
||||
|
||||
@[inherit_doc] infixl:50 " <:+ " => IsSuffix
|
||||
|
||||
/-! ### IsInfix -/
|
||||
|
||||
/--
|
||||
`IsInfix l₁ l₂`, or `l₁ <:+: l₂`, means that `l₁` is a contiguous
|
||||
substring of `l₂`, that is, `l₂` has the form `s ++ l₁ ++ t` for some `s, t`.
|
||||
-/
|
||||
def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists fun t => s ++ l₁ ++ t = l₂
|
||||
|
||||
@[inherit_doc] infixl:50 " <:+: " => IsInfix
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
/--
|
||||
@@ -1036,6 +1098,11 @@ theorem erase_cons [BEq α] (a b : α) (l : List α) :
|
||||
(b :: l).erase a = if b == a then l else b :: l.erase a := by
|
||||
simp only [List.erase]; split <;> simp_all
|
||||
|
||||
/-- `eraseP p l` removes the first element of `l` satisfying the predicate `p`. -/
|
||||
def eraseP (p : α → Bool) : List α → List α
|
||||
| [] => []
|
||||
| a :: l => bif p a then l else a :: eraseP p l
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
/--
|
||||
@@ -1053,6 +1120,8 @@ def eraseIdx : List α → Nat → List α
|
||||
@[simp] theorem eraseIdx_cons_zero : (a::as).eraseIdx 0 = as := rfl
|
||||
@[simp] theorem eraseIdx_cons_succ : (a::as).eraseIdx (i+1) = a :: as.eraseIdx i := rfl
|
||||
|
||||
/-! Finding elements -/
|
||||
|
||||
/-! ### find? -/
|
||||
|
||||
/--
|
||||
@@ -1090,6 +1159,50 @@ theorem findSome?_cons {f : α → Option β} :
|
||||
(a::as).findSome? f = match f a with | some b => some b | none => as.findSome? f :=
|
||||
rfl
|
||||
|
||||
/-! ### findIdx -/
|
||||
|
||||
/-- Returns the index of the first element satisfying `p`, or the length of the list otherwise. -/
|
||||
@[inline] def findIdx (p : α → Bool) (l : List α) : Nat := go l 0 where
|
||||
/-- Auxiliary for `findIdx`: `findIdx.go p l n = findIdx p l + n` -/
|
||||
@[specialize] go : List α → Nat → Nat
|
||||
| [], n => n
|
||||
| a :: l, n => bif p a then n else go l (n + 1)
|
||||
|
||||
@[simp] theorem findIdx_nil {α : Type _} (p : α → Bool) : [].findIdx p = 0 := rfl
|
||||
|
||||
/-! ### indexOf -/
|
||||
|
||||
/-- Returns the index of the first element equal to `a`, or the length of the list otherwise. -/
|
||||
def indexOf [BEq α] (a : α) : List α → Nat := findIdx (· == a)
|
||||
|
||||
@[simp] theorem indexOf_nil [BEq α] : ([] : List α).indexOf x = 0 := rfl
|
||||
|
||||
/-! ### findIdx? -/
|
||||
|
||||
/-- Return the index of the first occurrence of an element satisfying `p`. -/
|
||||
def findIdx? (p : α → Bool) : List α → (start : Nat := 0) → Option Nat
|
||||
| [], _ => none
|
||||
| a :: l, i => if p a then some i else findIdx? p l (i + 1)
|
||||
|
||||
/-! ### indexOf? -/
|
||||
|
||||
/-- Return the index of the first occurrence of `a` in the list. -/
|
||||
@[inline] def indexOf? [BEq α] (a : α) : List α → Option Nat := findIdx? (· == a)
|
||||
|
||||
/-! ### countP -/
|
||||
|
||||
/-- `countP p l` is the number of elements of `l` that satisfy `p`. -/
|
||||
@[inline] def countP (p : α → Bool) (l : List α) : Nat := go l 0 where
|
||||
/-- Auxiliary for `countP`: `countP.go p l acc = countP p l + acc`. -/
|
||||
@[specialize] go : List α → Nat → Nat
|
||||
| [], acc => acc
|
||||
| x :: xs, acc => bif p x then go xs (acc + 1) else go xs acc
|
||||
|
||||
/-! ### count -/
|
||||
|
||||
/-- `count a l` is the number of occurrences of `a` in `l`. -/
|
||||
@[inline] def count [BEq α] (a : α) : List α → Nat := countP (· == a)
|
||||
|
||||
/-! ### lookup -/
|
||||
|
||||
/--
|
||||
@@ -1231,6 +1344,14 @@ def unzip : List (α × β) → List α × List β
|
||||
|
||||
/-! ## Ranges and enumeration -/
|
||||
|
||||
/-- Sum of a list of natural numbers. -/
|
||||
-- This is not in the `List` namespace as later `List.sum` will be defined polymorphically.
|
||||
protected def _root_.Nat.sum (l : List Nat) : Nat := l.foldr (·+·) 0
|
||||
|
||||
@[simp] theorem _root_.Nat.sum_nil : Nat.sum ([] : List Nat) = 0 := rfl
|
||||
@[simp] theorem _root_.Nat.sum_cons (a : Nat) (l : List Nat) :
|
||||
Nat.sum (a::l) = a + Nat.sum l := rfl
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
/--
|
||||
@@ -1246,6 +1367,14 @@ where
|
||||
|
||||
@[simp] theorem range_zero : range 0 = [] := rfl
|
||||
|
||||
/-! ### range' -/
|
||||
|
||||
/-- `range' start len step` is the list of numbers `[start, start+step, ..., start+(len-1)*step]`.
|
||||
It is intended mainly for proving properties of `range` and `iota`. -/
|
||||
def range' : (start len : Nat) → (step : Nat := 1) → List Nat
|
||||
| _, 0, _ => []
|
||||
| s, n+1, step => s :: range' (s+step) n step
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -127,12 +127,12 @@ results `y` for which `f x` returns `some y`.
|
||||
@[inline]
|
||||
def filterMapM {m : Type u → Type v} [Monad m] {α β : Type u} (f : α → m (Option β)) (as : List α) : m (List β) :=
|
||||
let rec @[specialize] loop
|
||||
| [], bs => pure bs
|
||||
| [], bs => pure bs.reverse
|
||||
| a :: as, bs => do
|
||||
match (← f a) with
|
||||
| none => loop as bs
|
||||
| some b => loop as (b::bs)
|
||||
loop as.reverse []
|
||||
loop as []
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from left to right:
|
||||
@@ -227,6 +227,8 @@ def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
|
||||
instance : ForIn m (List α) α where
|
||||
forIn := List.forIn
|
||||
|
||||
@[simp] theorem forIn_eq_forIn [Monad m] : @List.forIn α β m _ = forIn := rfl
|
||||
|
||||
@[simp] theorem forIn_nil [Monad m] (f : α → β → m (ForInStep β)) (b : β) : forIn [] b f = pure b :=
|
||||
rfl
|
||||
|
||||
|
||||
242
src/Init/Data/List/Count.lean
Normal file
242
src/Init/Data/List/Count.lean
Normal file
@@ -0,0 +1,242 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Sublist
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.countP` and `List.count`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### countP -/
|
||||
section countP
|
||||
|
||||
variable (p q : α → Bool)
|
||||
|
||||
@[simp] theorem countP_nil : countP p [] = 0 := rfl
|
||||
|
||||
protected theorem countP_go_eq_add (l) : countP.go p l n = n + countP.go p l 0 := by
|
||||
induction l generalizing n with
|
||||
| nil => rfl
|
||||
| cons head tail ih =>
|
||||
unfold countP.go
|
||||
rw [ih (n := n + 1), ih (n := n), ih (n := 1)]
|
||||
if h : p head then simp [h, Nat.add_assoc] else simp [h]
|
||||
|
||||
@[simp] theorem countP_cons_of_pos (l) (pa : p a) : countP p (a :: l) = countP p l + 1 := by
|
||||
have : countP.go p (a :: l) 0 = countP.go p l 1 := show cond .. = _ by rw [pa]; rfl
|
||||
unfold countP
|
||||
rw [this, Nat.add_comm, List.countP_go_eq_add]
|
||||
|
||||
@[simp] theorem countP_cons_of_neg (l) (pa : ¬p a) : countP p (a :: l) = countP p l := by
|
||||
simp [countP, countP.go, pa]
|
||||
|
||||
theorem countP_cons (a : α) (l) : countP p (a :: l) = countP p l + if p a then 1 else 0 := by
|
||||
by_cases h : p a <;> simp [h]
|
||||
|
||||
theorem length_eq_countP_add_countP (l) : length l = countP p l + countP (fun a => ¬p a) l := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x h ih =>
|
||||
if h : p x then
|
||||
rw [countP_cons_of_pos _ _ h, countP_cons_of_neg _ _ _, length, ih]
|
||||
· rw [Nat.add_assoc, Nat.add_comm _ 1, Nat.add_assoc]
|
||||
· simp only [h, not_true_eq_false, decide_False, not_false_eq_true]
|
||||
else
|
||||
rw [countP_cons_of_pos (fun a => ¬p a) _ _, countP_cons_of_neg _ _ h, length, ih]
|
||||
· rfl
|
||||
· simp only [h, not_false_eq_true, decide_True]
|
||||
|
||||
theorem countP_eq_length_filter (l) : countP p l = length (filter p l) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x l ih =>
|
||||
if h : p x
|
||||
then rw [countP_cons_of_pos p l h, ih, filter_cons_of_pos h, length]
|
||||
else rw [countP_cons_of_neg p l h, ih, filter_cons_of_neg h]
|
||||
|
||||
theorem countP_le_length : countP p l ≤ l.length := by
|
||||
simp only [countP_eq_length_filter]
|
||||
apply length_filter_le
|
||||
|
||||
@[simp] theorem countP_append (l₁ l₂) : countP p (l₁ ++ l₂) = countP p l₁ + countP p l₂ := by
|
||||
simp only [countP_eq_length_filter, filter_append, length_append]
|
||||
|
||||
theorem countP_pos : 0 < countP p l ↔ ∃ a ∈ l, p a := by
|
||||
simp only [countP_eq_length_filter, length_pos_iff_exists_mem, mem_filter, exists_prop]
|
||||
|
||||
theorem countP_eq_zero : countP p l = 0 ↔ ∀ a ∈ l, ¬p a := by
|
||||
simp only [countP_eq_length_filter, length_eq_zero, filter_eq_nil]
|
||||
|
||||
theorem countP_eq_length : countP p l = l.length ↔ ∀ a ∈ l, p a := by
|
||||
rw [countP_eq_length_filter, filter_length_eq_length]
|
||||
|
||||
theorem Sublist.countP_le (s : l₁ <+ l₂) : countP p l₁ ≤ countP p l₂ := by
|
||||
simp only [countP_eq_length_filter]
|
||||
apply s.filter _ |>.length_le
|
||||
|
||||
theorem countP_filter (l : List α) :
|
||||
countP p (filter q l) = countP (fun a => p a ∧ q a) l := by
|
||||
simp only [countP_eq_length_filter, filter_filter]
|
||||
|
||||
@[simp] theorem countP_true {l : List α} : (l.countP fun _ => true) = l.length := by
|
||||
rw [countP_eq_length]
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_false {l : List α} : (l.countP fun _ => false) = 0 := by
|
||||
rw [countP_eq_zero]
|
||||
simp
|
||||
|
||||
@[simp] theorem countP_map (p : β → Bool) (f : α → β) :
|
||||
∀ l, countP p (map f l) = countP (p ∘ f) l
|
||||
| [] => rfl
|
||||
| a :: l => by rw [map_cons, countP_cons, countP_cons, countP_map p f l]; rfl
|
||||
|
||||
variable {p q}
|
||||
|
||||
theorem countP_mono_left (h : ∀ x ∈ l, p x → q x) : countP p l ≤ countP q l := by
|
||||
induction l with
|
||||
| nil => apply Nat.le_refl
|
||||
| cons a l ihl =>
|
||||
rw [forall_mem_cons] at h
|
||||
have ⟨ha, hl⟩ := h
|
||||
simp [countP_cons]
|
||||
cases h : p a
|
||||
· simp only [Bool.false_eq_true, ↓reduceIte, Nat.add_zero]
|
||||
apply Nat.le_trans ?_ (Nat.le_add_right _ _)
|
||||
apply ihl hl
|
||||
· simp only [↓reduceIte, ha h, succ_le_succ_iff]
|
||||
apply ihl hl
|
||||
|
||||
theorem countP_congr (h : ∀ x ∈ l, p x ↔ q x) : countP p l = countP q l :=
|
||||
Nat.le_antisymm
|
||||
(countP_mono_left fun x hx => (h x hx).1)
|
||||
(countP_mono_left fun x hx => (h x hx).2)
|
||||
|
||||
end countP
|
||||
|
||||
/-! ### count -/
|
||||
section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem count_nil (a : α) : count a [] = 0 := rfl
|
||||
|
||||
theorem count_cons (a b : α) (l : List α) :
|
||||
count a (b :: l) = count a l + if b == a then 1 else 0 := by
|
||||
simp [count, countP_cons]
|
||||
|
||||
theorem count_tail : ∀ (l : List α) (a : α) (h : l ≠ []),
|
||||
l.tail.count a = l.count a - if l.head h == a then 1 else 0
|
||||
| head :: tail, a, _ => by simp [count_cons]
|
||||
|
||||
theorem count_le_length (a : α) (l : List α) : count a l ≤ l.length := countP_le_length _
|
||||
|
||||
theorem Sublist.count_le (h : l₁ <+ l₂) (a : α) : count a l₁ ≤ count a l₂ := h.countP_le _
|
||||
|
||||
theorem count_le_count_cons (a b : α) (l : List α) : count a l ≤ count a (b :: l) :=
|
||||
(sublist_cons_self _ _).count_le _
|
||||
|
||||
theorem count_singleton (a b : α) : count a [b] = if b == a then 1 else 0 := by
|
||||
simp [count_cons]
|
||||
|
||||
@[simp] theorem count_append (a : α) : ∀ l₁ l₂, count a (l₁ ++ l₂) = count a l₁ + count a l₂ :=
|
||||
countP_append _
|
||||
|
||||
variable [LawfulBEq α]
|
||||
|
||||
@[simp] theorem count_cons_self (a : α) (l : List α) : count a (a :: l) = count a l + 1 := by
|
||||
simp [count_cons]
|
||||
|
||||
@[simp] theorem count_cons_of_ne (h : a ≠ b) (l : List α) : count a (b :: l) = count a l := by
|
||||
simp only [count_cons, cond_eq_if, beq_iff_eq]
|
||||
split <;> simp_all
|
||||
|
||||
theorem count_singleton_self (a : α) : count a [a] = 1 := by simp
|
||||
|
||||
theorem count_concat_self (a : α) (l : List α) :
|
||||
count a (concat l a) = (count a l) + 1 := by simp
|
||||
|
||||
@[simp]
|
||||
theorem count_pos_iff_mem {a : α} {l : List α} : 0 < count a l ↔ a ∈ l := by
|
||||
simp only [count, countP_pos, beq_iff_eq, exists_eq_right]
|
||||
|
||||
theorem count_eq_zero_of_not_mem {a : α} {l : List α} (h : a ∉ l) : count a l = 0 :=
|
||||
Decidable.byContradiction fun h' => h <| count_pos_iff_mem.1 (Nat.pos_of_ne_zero h')
|
||||
|
||||
theorem not_mem_of_count_eq_zero {a : α} {l : List α} (h : count a l = 0) : a ∉ l :=
|
||||
fun h' => Nat.ne_of_lt (count_pos_iff_mem.2 h') h.symm
|
||||
|
||||
theorem count_eq_zero {l : List α} : count a l = 0 ↔ a ∉ l :=
|
||||
⟨not_mem_of_count_eq_zero, count_eq_zero_of_not_mem⟩
|
||||
|
||||
theorem count_eq_length {l : List α} : count a l = l.length ↔ ∀ b ∈ l, a = b := by
|
||||
rw [count, countP_eq_length]
|
||||
refine ⟨fun h b hb => Eq.symm ?_, fun h b hb => ?_⟩
|
||||
· simpa using h b hb
|
||||
· rw [h b hb, beq_self_eq_true]
|
||||
|
||||
@[simp] theorem count_replicate_self (a : α) (n : Nat) : count a (replicate n a) = n :=
|
||||
(count_eq_length.2 <| fun _ h => (eq_of_mem_replicate h).symm).trans (length_replicate ..)
|
||||
|
||||
theorem count_replicate (a b : α) (n : Nat) : count a (replicate n b) = if b == a then n else 0 := by
|
||||
split <;> (rename_i h; simp only [beq_iff_eq] at h)
|
||||
· exact ‹b = a› ▸ count_replicate_self ..
|
||||
· exact count_eq_zero.2 <| mt eq_of_mem_replicate (Ne.symm h)
|
||||
|
||||
theorem filter_beq (l : List α) (a : α) : l.filter (· == a) = replicate (count a l) a := by
|
||||
simp only [count, countP_eq_length_filter, eq_replicate, mem_filter, beq_iff_eq]
|
||||
exact ⟨trivial, fun _ h => h.2⟩
|
||||
|
||||
theorem filter_eq {α} [DecidableEq α] (l : List α) (a : α) : l.filter (· = a) = replicate (count a l) a :=
|
||||
filter_beq l a
|
||||
|
||||
theorem le_count_iff_replicate_sublist {l : List α} : n ≤ count a l ↔ replicate n a <+ l := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· exact ((replicate_sublist_replicate a).2 h).trans <| filter_beq l a ▸ filter_sublist _
|
||||
· simpa only [count_replicate_self] using h.count_le a
|
||||
|
||||
theorem replicate_count_eq_of_count_eq_length {l : List α} (h : count a l = length l) :
|
||||
replicate (count a l) a = l :=
|
||||
(le_count_iff_replicate_sublist.mp (Nat.le_refl _)).eq_of_length <|
|
||||
(length_replicate (count a l) a).trans h
|
||||
|
||||
@[simp] theorem count_filter {l : List α} (h : p a) : count a (filter p l) = count a l := by
|
||||
rw [count, countP_filter]; congr; funext b
|
||||
simp; rintro rfl; exact h
|
||||
|
||||
theorem count_le_count_map [DecidableEq β] (l : List α) (f : α → β) (x : α) :
|
||||
count x l ≤ count (f x) (map f l) := by
|
||||
rw [count, count, countP_map]
|
||||
apply countP_mono_left; simp (config := { contextual := true })
|
||||
|
||||
theorem count_erase (a b : α) :
|
||||
∀ l : List α, count a (l.erase b) = count a l - if b == a then 1 else 0
|
||||
| [] => by simp
|
||||
| c :: l => by
|
||||
rw [erase_cons]
|
||||
if hc : c = b then
|
||||
have hc_beq := (beq_iff_eq _ _).mpr hc
|
||||
rw [if_pos hc_beq, hc, count_cons, Nat.add_sub_cancel]
|
||||
else
|
||||
have hc_beq := beq_false_of_ne hc
|
||||
simp only [hc_beq, if_false, count_cons, count_cons, count_erase a b l]
|
||||
if ha : b = a then
|
||||
rw [ha, eq_comm] at hc
|
||||
rw [if_pos ((beq_iff_eq _ _).2 ha), if_neg (by simpa using Ne.symm hc), Nat.add_zero, Nat.add_zero]
|
||||
else
|
||||
rw [if_neg (by simpa using ha), Nat.sub_zero, Nat.sub_zero]
|
||||
|
||||
@[simp] theorem count_erase_self (a : α) (l : List α) :
|
||||
count a (List.erase l a) = count a l - 1 := by rw [count_erase, if_pos (by simp)]
|
||||
|
||||
@[simp] theorem count_erase_of_ne (ab : a ≠ b) (l : List α) : count a (l.erase b) = count a l := by
|
||||
rw [count_erase, if_neg (by simpa using ab.symm), Nat.sub_zero]
|
||||
|
||||
end count
|
||||
445
src/Init/Data/List/Erase.lean
Normal file
445
src/Init/Data/List/Erase.lean
Normal file
@@ -0,0 +1,445 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
|
||||
Yury Kudryashov
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Pairwise
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.eraseP` and `List.erase`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### eraseP -/
|
||||
|
||||
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
|
||||
|
||||
theorem eraseP_cons (a : α) (l : List α) :
|
||||
(a :: l).eraseP p = bif p a then l else a :: l.eraseP p := rfl
|
||||
|
||||
@[simp] theorem eraseP_cons_of_pos {l : List α} {p} (h : p a) : (a :: l).eraseP p = l := by
|
||||
simp [eraseP_cons, h]
|
||||
|
||||
@[simp] theorem eraseP_cons_of_neg {l : List α} {p} (h : ¬p a) :
|
||||
(a :: l).eraseP p = a :: l.eraseP p := by simp [eraseP_cons, h]
|
||||
|
||||
theorem eraseP_of_forall_not {l : List α} (h : ∀ a, a ∈ l → ¬p a) : l.eraseP p = l := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons _ _ ih => simp [h _ (.head ..), ih (forall_mem_cons.1 h).2]
|
||||
|
||||
theorem exists_of_eraseP : ∀ {l : List α} {a} (al : a ∈ l) (pa : p a),
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬p b) ∧ p a ∧ l = l₁ ++ a :: l₂ ∧ l.eraseP p = l₁ ++ l₂
|
||||
| b :: l, a, al, pa =>
|
||||
if pb : p b then
|
||||
⟨b, [], l, forall_mem_nil _, pb, by simp [pb]⟩
|
||||
else
|
||||
match al with
|
||||
| .head .. => nomatch pb pa
|
||||
| .tail _ al =>
|
||||
let ⟨c, l₁, l₂, h₁, h₂, h₃, h₄⟩ := exists_of_eraseP al pa
|
||||
⟨c, b::l₁, l₂, (forall_mem_cons ..).2 ⟨pb, h₁⟩,
|
||||
h₂, by rw [h₃, cons_append], by simp [pb, h₄]⟩
|
||||
|
||||
theorem exists_or_eq_self_of_eraseP (p) (l : List α) :
|
||||
l.eraseP p = l ∨
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬p b) ∧ p a ∧ l = l₁ ++ a :: l₂ ∧ l.eraseP p = l₁ ++ l₂ :=
|
||||
if h : ∃ a ∈ l, p a then
|
||||
let ⟨_, ha, pa⟩ := h
|
||||
.inr (exists_of_eraseP ha pa)
|
||||
else
|
||||
.inl (eraseP_of_forall_not (h ⟨·, ·, ·⟩))
|
||||
|
||||
@[simp] theorem length_eraseP_of_mem (al : a ∈ l) (pa : p a) :
|
||||
length (l.eraseP p) = length l - 1 := by
|
||||
let ⟨_, l₁, l₂, _, _, e₁, e₂⟩ := exists_of_eraseP al pa
|
||||
rw [e₂]; simp [length_append, e₁]; rfl
|
||||
|
||||
theorem length_eraseP {l : List α} : (l.eraseP p).length = if l.any p then l.length - 1 else l.length := by
|
||||
split <;> rename_i h
|
||||
· simp only [any_eq_true] at h
|
||||
obtain ⟨x, m, h⟩ := h
|
||||
simp [length_eraseP_of_mem m h]
|
||||
· simp only [any_eq_true] at h
|
||||
rw [eraseP_of_forall_not]
|
||||
simp_all
|
||||
|
||||
theorem eraseP_sublist (l : List α) : l.eraseP p <+ l := by
|
||||
match exists_or_eq_self_of_eraseP p l with
|
||||
| .inl h => rw [h]; apply Sublist.refl
|
||||
| .inr ⟨c, l₁, l₂, _, _, h₃, h₄⟩ => rw [h₄, h₃]; simp
|
||||
|
||||
theorem eraseP_subset (l : List α) : l.eraseP p ⊆ l := (eraseP_sublist l).subset
|
||||
|
||||
protected theorem Sublist.eraseP : l₁ <+ l₂ → l₁.eraseP p <+ l₂.eraseP p
|
||||
| .slnil => Sublist.refl _
|
||||
| .cons a s => by
|
||||
by_cases h : p a
|
||||
· simpa [h] using s.eraseP.trans (eraseP_sublist _)
|
||||
· simpa [h] using s.eraseP.cons _
|
||||
| .cons₂ a s => by
|
||||
by_cases h : p a
|
||||
· simpa [h] using s
|
||||
· simpa [h] using s.eraseP
|
||||
|
||||
theorem length_eraseP_le (l : List α) : (l.eraseP p).length ≤ l.length :=
|
||||
l.eraseP_sublist.length_le
|
||||
|
||||
theorem mem_of_mem_eraseP {l : List α} : a ∈ l.eraseP p → a ∈ l := (eraseP_subset _ ·)
|
||||
|
||||
@[simp] theorem mem_eraseP_of_neg {l : List α} (pa : ¬p a) : a ∈ l.eraseP p ↔ a ∈ l := by
|
||||
refine ⟨mem_of_mem_eraseP, fun al => ?_⟩
|
||||
match exists_or_eq_self_of_eraseP p l with
|
||||
| .inl h => rw [h]; assumption
|
||||
| .inr ⟨c, l₁, l₂, h₁, h₂, h₃, h₄⟩ =>
|
||||
rw [h₄]; rw [h₃] at al
|
||||
have : a ≠ c := fun h => (h ▸ pa).elim h₂
|
||||
simp [this] at al; simp [al]
|
||||
|
||||
@[simp] theorem eraseP_eq_self_iff {p} {l : List α} : l.eraseP p = l ↔ ∀ a ∈ l, ¬ p a := by
|
||||
rw [← Sublist.length_eq (eraseP_sublist l), length_eraseP]
|
||||
split <;> rename_i h
|
||||
· simp only [any_eq_true, length_eq_zero] at h
|
||||
constructor
|
||||
· intro; simp_all [Nat.sub_one_eq_self]
|
||||
· intro; obtain ⟨x, m, h⟩ := h; simp_all
|
||||
· simp_all
|
||||
|
||||
theorem eraseP_map (f : β → α) : ∀ (l : List β), (map f l).eraseP p = map f (l.eraseP (p ∘ f))
|
||||
| [] => rfl
|
||||
| b::l => by by_cases h : p (f b) <;> simp [h, eraseP_map f l, eraseP_cons_of_pos]
|
||||
|
||||
theorem eraseP_filterMap (f : α → Option β) : ∀ (l : List α),
|
||||
(filterMap f l).eraseP p = filterMap f (l.eraseP (fun x => match f x with | some y => p y | none => false))
|
||||
| [] => rfl
|
||||
| a::l => by
|
||||
rw [filterMap_cons, eraseP_cons]
|
||||
split <;> rename_i h
|
||||
· simp [h, eraseP_filterMap]
|
||||
· rename_i b
|
||||
rw [h, eraseP_cons]
|
||||
by_cases w : p b
|
||||
· simp [w]
|
||||
· simp only [w, cond_false]
|
||||
rw [filterMap_cons_some h, eraseP_filterMap]
|
||||
|
||||
theorem eraseP_filter (f : α → Bool) (l : List α) :
|
||||
(filter f l).eraseP p = filter f (l.eraseP (fun x => p x && f x)) := by
|
||||
rw [← filterMap_eq_filter, eraseP_filterMap]
|
||||
congr
|
||||
ext x
|
||||
simp only [Option.guard]
|
||||
split <;> split at * <;> simp_all
|
||||
|
||||
theorem eraseP_append_left {a : α} (pa : p a) :
|
||||
∀ {l₁ : List α} l₂, a ∈ l₁ → (l₁++l₂).eraseP p = l₁.eraseP p ++ l₂
|
||||
| x :: xs, l₂, h => by
|
||||
by_cases h' : p x <;> simp [h']
|
||||
rw [eraseP_append_left pa l₂ ((mem_cons.1 h).resolve_left (mt _ h'))]
|
||||
intro | rfl => exact pa
|
||||
|
||||
theorem eraseP_append_right :
|
||||
∀ {l₁ : List α} l₂, (∀ b ∈ l₁, ¬p b) → eraseP p (l₁++l₂) = l₁ ++ l₂.eraseP p
|
||||
| [], l₂, _ => rfl
|
||||
| x :: xs, l₂, h => by
|
||||
simp [(forall_mem_cons.1 h).1, eraseP_append_right _ (forall_mem_cons.1 h).2]
|
||||
|
||||
theorem eraseP_append (l₁ l₂ : List α) :
|
||||
(l₁ ++ l₂).eraseP p = if l₁.any p then l₁.eraseP p ++ l₂ else l₁ ++ l₂.eraseP p := by
|
||||
split <;> rename_i h
|
||||
· simp only [any_eq_true] at h
|
||||
obtain ⟨x, m, h⟩ := h
|
||||
rw [eraseP_append_left h _ m]
|
||||
· simp only [any_eq_true] at h
|
||||
rw [eraseP_append_right _]
|
||||
simp_all
|
||||
|
||||
theorem eraseP_eq_iff {p} {l : List α} :
|
||||
l.eraseP p = l' ↔
|
||||
((∀ a ∈ l, ¬ p a) ∧ l = l') ∨
|
||||
∃ a l₁ l₂, (∀ b ∈ l₁, ¬ p b) ∧ p a ∧ l = l₁ ++ a :: l₂ ∧ l' = l₁ ++ l₂ := by
|
||||
cases exists_or_eq_self_of_eraseP p l with
|
||||
| inl h =>
|
||||
constructor
|
||||
· intro h'
|
||||
left
|
||||
exact ⟨eraseP_eq_self_iff.1 h, by simp_all⟩
|
||||
· rintro (⟨-, rfl⟩ | ⟨a, l₁, l₂, h₁, h₂, rfl, rfl⟩)
|
||||
· assumption
|
||||
· rw [eraseP_append_right _ h₁, eraseP_cons_of_pos h₂]
|
||||
| inr h =>
|
||||
obtain ⟨a, l₁, l₂, h₁, h₂, w₁, w₂⟩ := h
|
||||
rw [w₂]
|
||||
subst w₁
|
||||
constructor
|
||||
· rintro rfl
|
||||
right
|
||||
refine ⟨a, l₁, l₂, ?_⟩
|
||||
simp_all
|
||||
· rintro (h | h)
|
||||
· simp_all
|
||||
· obtain ⟨a', l₁', l₂', h₁', h₂', h, rfl⟩ := h
|
||||
have p : l₁ = l₁' := by
|
||||
have q : l₁ = takeWhile (fun x => !p x) (l₁ ++ a :: l₂) := by
|
||||
rw [takeWhile_append_of_pos (by simp_all),
|
||||
takeWhile_cons_of_neg (by simp [h₂]), append_nil]
|
||||
have q' : l₁' = takeWhile (fun x => !p x) (l₁' ++ a' :: l₂') := by
|
||||
rw [takeWhile_append_of_pos (by simpa using h₁'),
|
||||
takeWhile_cons_of_neg (by simp [h₂']), append_nil]
|
||||
simp [h] at q
|
||||
rw [q', q]
|
||||
subst p
|
||||
simp_all
|
||||
|
||||
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
|
||||
(replicate n a).eraseP p = replicate (n - 1) a := by
|
||||
cases n <;> simp [replicate_succ, h]
|
||||
|
||||
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
|
||||
(replicate n a).eraseP p = replicate n a := by
|
||||
rw [eraseP_of_forall_not (by simp_all)]
|
||||
|
||||
theorem Nodup.eraseP (p) : Nodup l → Nodup (l.eraseP p) :=
|
||||
Nodup.sublist <| eraseP_sublist _
|
||||
|
||||
theorem eraseP_comm {l : List α} (h : ∀ a ∈ l, ¬ p a ∨ ¬ q a) :
|
||||
(l.eraseP p).eraseP q = (l.eraseP q).eraseP p := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons a l ih =>
|
||||
simp only [eraseP_cons]
|
||||
by_cases h₁ : p a
|
||||
· by_cases h₂ : q a
|
||||
· simp_all
|
||||
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
|
||||
· by_cases h₂ : q a
|
||||
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
|
||||
· simp [h₁, h₂, ih (fun b m => h b (mem_cons_of_mem _ m))]
|
||||
|
||||
/-! ### erase -/
|
||||
section erase
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem erase_cons_head [LawfulBEq α] (a : α) (l : List α) : (a :: l).erase a = l := by
|
||||
simp [erase_cons]
|
||||
|
||||
@[simp] theorem erase_cons_tail {a b : α} {l : List α} (h : ¬(b == a)) :
|
||||
(b :: l).erase a = b :: l.erase a := by simp only [erase_cons, if_neg h]
|
||||
|
||||
theorem erase_of_not_mem [LawfulBEq α] {a : α} : ∀ {l : List α}, a ∉ l → l.erase a = l
|
||||
| [], _ => rfl
|
||||
| b :: l, h => by
|
||||
rw [mem_cons, not_or] at h
|
||||
simp only [erase_cons, if_neg, erase_of_not_mem h.2, beq_iff_eq, Ne.symm h.1, not_false_eq_true]
|
||||
|
||||
theorem erase_eq_eraseP' (a : α) (l : List α) : l.erase a = l.eraseP (· == a) := by
|
||||
induction l
|
||||
· simp
|
||||
· next b t ih =>
|
||||
rw [erase_cons, eraseP_cons, ih]
|
||||
if h : b == a then simp [h] else simp [h]
|
||||
|
||||
theorem erase_eq_eraseP [LawfulBEq α] (a : α) : ∀ l : List α, l.erase a = l.eraseP (a == ·)
|
||||
| [] => rfl
|
||||
| b :: l => by
|
||||
if h : a = b then simp [h] else simp [h, Ne.symm h, erase_eq_eraseP a l]
|
||||
|
||||
theorem exists_erase_eq [LawfulBEq α] {a : α} {l : List α} (h : a ∈ l) :
|
||||
∃ l₁ l₂, a ∉ l₁ ∧ l = l₁ ++ a :: l₂ ∧ l.erase a = l₁ ++ l₂ := by
|
||||
let ⟨_, l₁, l₂, h₁, e, h₂, h₃⟩ := exists_of_eraseP h (beq_self_eq_true _)
|
||||
rw [erase_eq_eraseP]; exact ⟨l₁, l₂, fun h => h₁ _ h (beq_self_eq_true _), eq_of_beq e ▸ h₂, h₃⟩
|
||||
|
||||
@[simp] theorem length_erase_of_mem [LawfulBEq α] {a : α} {l : List α} (h : a ∈ l) :
|
||||
length (l.erase a) = length l - 1 := by
|
||||
rw [erase_eq_eraseP]; exact length_eraseP_of_mem h (beq_self_eq_true a)
|
||||
|
||||
theorem length_erase [LawfulBEq α] (a : α) (l : List α) :
|
||||
length (l.erase a) = if a ∈ l then length l - 1 else length l := by
|
||||
rw [erase_eq_eraseP, length_eraseP]
|
||||
split <;> split <;> simp_all
|
||||
|
||||
theorem erase_sublist (a : α) (l : List α) : l.erase a <+ l :=
|
||||
erase_eq_eraseP' a l ▸ eraseP_sublist ..
|
||||
|
||||
theorem erase_subset (a : α) (l : List α) : l.erase a ⊆ l := (erase_sublist a l).subset
|
||||
|
||||
theorem Sublist.erase (a : α) {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₁.erase a <+ l₂.erase a := by
|
||||
simp only [erase_eq_eraseP']; exact h.eraseP
|
||||
|
||||
theorem length_erase_le (a : α) (l : List α) : (l.erase a).length ≤ l.length :=
|
||||
(erase_sublist a l).length_le
|
||||
|
||||
theorem mem_of_mem_erase {a b : α} {l : List α} (h : a ∈ l.erase b) : a ∈ l := erase_subset _ _ h
|
||||
|
||||
@[simp] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {l : List α} (ab : a ≠ b) :
|
||||
a ∈ l.erase b ↔ a ∈ l :=
|
||||
erase_eq_eraseP b l ▸ mem_eraseP_of_neg (mt eq_of_beq ab.symm)
|
||||
|
||||
@[simp] theorem erase_eq_self_iff [LawfulBEq α] {l : List α} : l.erase a = l ↔ a ∉ l := by
|
||||
rw [erase_eq_eraseP', eraseP_eq_self_iff]
|
||||
simp
|
||||
|
||||
theorem erase_filter [LawfulBEq α] (f : α → Bool) (l : List α) :
|
||||
(filter f l).erase a = filter f (l.erase a) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
by_cases h : a = x
|
||||
· rw [erase_cons]
|
||||
simp only [h, beq_self_eq_true, ↓reduceIte]
|
||||
rw [filter_cons]
|
||||
split
|
||||
· rw [erase_cons_head]
|
||||
· rw [erase_of_not_mem]
|
||||
simp_all [mem_filter]
|
||||
· rw [erase_cons_tail (by simpa using Ne.symm h), filter_cons, filter_cons]
|
||||
split
|
||||
· rw [erase_cons_tail (by simpa using Ne.symm h), ih]
|
||||
· rw [ih]
|
||||
|
||||
theorem erase_append_left [LawfulBEq α] {l₁ : List α} (l₂) (h : a ∈ l₁) :
|
||||
(l₁ ++ l₂).erase a = l₁.erase a ++ l₂ := by
|
||||
simp [erase_eq_eraseP]; exact eraseP_append_left (beq_self_eq_true a) l₂ h
|
||||
|
||||
theorem erase_append_right [LawfulBEq α] {a : α} {l₁ : List α} (l₂ : List α) (h : a ∉ l₁) :
|
||||
(l₁ ++ l₂).erase a = (l₁ ++ l₂.erase a) := by
|
||||
rw [erase_eq_eraseP, erase_eq_eraseP, eraseP_append_right]
|
||||
intros b h' h''; rw [eq_of_beq h''] at h; exact h h'
|
||||
|
||||
theorem erase_append [LawfulBEq α] {a : α} {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).erase a = if a ∈ l₁ then l₁.erase a ++ l₂ else l₁ ++ l₂.erase a := by
|
||||
simp [erase_eq_eraseP, eraseP_append]
|
||||
|
||||
theorem erase_comm [LawfulBEq α] (a b : α) (l : List α) :
|
||||
(l.erase a).erase b = (l.erase b).erase a := by
|
||||
if ab : a == b then rw [eq_of_beq ab] else ?_
|
||||
if ha : a ∈ l then ?_ else
|
||||
simp only [erase_of_not_mem ha, erase_of_not_mem (mt mem_of_mem_erase ha)]
|
||||
if hb : b ∈ l then ?_ else
|
||||
simp only [erase_of_not_mem hb, erase_of_not_mem (mt mem_of_mem_erase hb)]
|
||||
match l, l.erase a, exists_erase_eq ha with
|
||||
| _, _, ⟨l₁, l₂, ha', rfl, rfl⟩ =>
|
||||
if h₁ : b ∈ l₁ then
|
||||
rw [erase_append_left _ h₁, erase_append_left _ h₁,
|
||||
erase_append_right _ (mt mem_of_mem_erase ha'), erase_cons_head]
|
||||
else
|
||||
rw [erase_append_right _ h₁, erase_append_right _ h₁, erase_append_right _ ha',
|
||||
erase_cons_tail ab, erase_cons_head]
|
||||
|
||||
theorem erase_eq_iff [LawfulBEq α] {a : α} {l : List α} :
|
||||
l.erase a = l' ↔
|
||||
(a ∉ l ∧ l = l') ∨
|
||||
∃ l₁ l₂, a ∉ l₁ ∧ l = l₁ ++ a :: l₂ ∧ l' = l₁ ++ l₂ := by
|
||||
rw [erase_eq_eraseP', eraseP_eq_iff]
|
||||
simp only [beq_iff_eq, forall_mem_ne', exists_and_left]
|
||||
constructor
|
||||
· rintro (⟨h, rfl⟩ | ⟨a', l', h, rfl, x, rfl, rfl⟩)
|
||||
· left; simp_all
|
||||
· right; refine ⟨l', h, x, by simp⟩
|
||||
· rintro (⟨h, rfl⟩ | ⟨l₁, h, x, rfl, rfl⟩)
|
||||
· left; simp_all
|
||||
· right; refine ⟨a, l₁, h, by simp⟩
|
||||
|
||||
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
|
||||
(replicate n a).erase a = replicate (n - 1) a := by
|
||||
cases n <;> simp [replicate_succ]
|
||||
|
||||
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
|
||||
(replicate n a).erase b = replicate n a := by
|
||||
rw [erase_of_not_mem]
|
||||
simp_all
|
||||
|
||||
theorem Nodup.erase_eq_filter [BEq α] [LawfulBEq α] {l} (d : Nodup l) (a : α) : l.erase a = l.filter (· != a) := by
|
||||
induction d with
|
||||
| nil => rfl
|
||||
| cons m _n ih =>
|
||||
rename_i b l
|
||||
by_cases h : b = a
|
||||
· subst h
|
||||
rw [erase_cons_head, filter_cons_of_neg (by simp)]
|
||||
apply Eq.symm
|
||||
rw [filter_eq_self]
|
||||
simpa [@eq_comm α] using m
|
||||
· simp [beq_false_of_ne h, ih, h]
|
||||
|
||||
theorem Nodup.mem_erase_iff [BEq α] [LawfulBEq α] {a : α} (d : Nodup l) : a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l := by
|
||||
rw [Nodup.erase_eq_filter d, mem_filter, and_comm, bne_iff_ne]
|
||||
|
||||
theorem Nodup.not_mem_erase [BEq α] [LawfulBEq α] {a : α} (h : Nodup l) : a ∉ l.erase a := fun H => by
|
||||
simpa using ((Nodup.mem_erase_iff h).mp H).left
|
||||
|
||||
theorem Nodup.erase [BEq α] [LawfulBEq α] (a : α) : Nodup l → Nodup (l.erase a) :=
|
||||
Nodup.sublist <| erase_sublist _ _
|
||||
|
||||
end erase
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem length_eraseIdx : ∀ {l i}, i < length l → length (@eraseIdx α l i) = length l - 1
|
||||
| [], _, _ => rfl
|
||||
| _::_, 0, _ => by simp [eraseIdx]
|
||||
| x::xs, i+1, h => by
|
||||
have : i < length xs := Nat.lt_of_succ_lt_succ h
|
||||
simp [eraseIdx, ← Nat.add_one]
|
||||
rw [length_eraseIdx this, Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) this)]
|
||||
|
||||
@[simp] theorem eraseIdx_zero (l : List α) : eraseIdx l 0 = tail l := by cases l <;> rfl
|
||||
|
||||
theorem eraseIdx_eq_take_drop_succ :
|
||||
∀ (l : List α) (i : Nat), l.eraseIdx i = l.take i ++ l.drop (i + 1)
|
||||
| nil, _ => by simp
|
||||
| a::l, 0 => by simp
|
||||
| a::l, i + 1 => by simp [eraseIdx_eq_take_drop_succ l i]
|
||||
|
||||
theorem eraseIdx_sublist : ∀ (l : List α) (k : Nat), eraseIdx l k <+ l
|
||||
| [], _ => by simp
|
||||
| a::l, 0 => by simp
|
||||
| a::l, k + 1 => by simp [eraseIdx_sublist l k]
|
||||
|
||||
theorem eraseIdx_subset (l : List α) (k : Nat) : eraseIdx l k ⊆ l := (eraseIdx_sublist l k).subset
|
||||
|
||||
@[simp]
|
||||
theorem eraseIdx_eq_self : ∀ {l : List α} {k : Nat}, eraseIdx l k = l ↔ length l ≤ k
|
||||
| [], _ => by simp
|
||||
| a::l, 0 => by simp [(cons_ne_self _ _).symm]
|
||||
| a::l, k + 1 => by simp [eraseIdx_eq_self]
|
||||
|
||||
theorem eraseIdx_of_length_le {l : List α} {k : Nat} (h : length l ≤ k) : eraseIdx l k = l := by
|
||||
rw [eraseIdx_eq_self.2 h]
|
||||
|
||||
theorem eraseIdx_append_of_lt_length {l : List α} {k : Nat} (hk : k < length l) (l' : List α) :
|
||||
eraseIdx (l ++ l') k = eraseIdx l k ++ l' := by
|
||||
induction l generalizing k with
|
||||
| nil => simp_all
|
||||
| cons x l ih =>
|
||||
cases k with
|
||||
| zero => rfl
|
||||
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_lt_succ_iff]
|
||||
|
||||
theorem eraseIdx_append_of_length_le {l : List α} {k : Nat} (hk : length l ≤ k) (l' : List α) :
|
||||
eraseIdx (l ++ l') k = l ++ eraseIdx l' (k - length l) := by
|
||||
induction l generalizing k with
|
||||
| nil => simp_all
|
||||
| cons x l ih =>
|
||||
cases k with
|
||||
| zero => simp_all
|
||||
| succ k => simp_all [eraseIdx_cons_succ, Nat.succ_sub_succ]
|
||||
|
||||
protected theorem IsPrefix.eraseIdx {l l' : List α} (h : l <+: l') (k : Nat) :
|
||||
eraseIdx l k <+: eraseIdx l' k := by
|
||||
rcases h with ⟨t, rfl⟩
|
||||
if hkl : k < length l then
|
||||
simp [eraseIdx_append_of_lt_length hkl]
|
||||
else
|
||||
rw [Nat.not_lt] at hkl
|
||||
simp [eraseIdx_append_of_length_le hkl, eraseIdx_of_length_le hkl]
|
||||
|
||||
-- See also `mem_eraseIdx_iff_getElem` and `mem_eraseIdx_iff_getElem?` in
|
||||
-- `Init/Data/List/Nat/Basic.lean`.
|
||||
|
||||
end List
|
||||
229
src/Init/Data/List/Find.lean
Normal file
229
src/Init/Data/List/Find.lean
Normal file
@@ -0,0 +1,229 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.find?`, `List.findSome?`, `List.findIdx`, `List.findIdx?`, and `List.indexOf`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### find? -/
|
||||
|
||||
@[simp] theorem find?_cons_of_pos (l) (h : p a) : find? p (a :: l) = some a := by
|
||||
simp [find?, h]
|
||||
|
||||
@[simp] theorem find?_cons_of_neg (l) (h : ¬p a) : find? p (a :: l) = find? p l := by
|
||||
simp [find?, h]
|
||||
|
||||
@[simp] theorem find?_eq_none : find? p l = none ↔ ∀ x ∈ l, ¬ p x := by
|
||||
induction l <;> simp [find?_cons]; split <;> simp [*]
|
||||
|
||||
theorem find?_some : ∀ {l}, find? p l = some a → p a
|
||||
| b :: l, H => by
|
||||
by_cases h : p b <;> simp [find?, h] at H
|
||||
· exact H ▸ h
|
||||
· exact find?_some H
|
||||
|
||||
@[simp] theorem mem_of_find?_eq_some : ∀ {l}, find? p l = some a → a ∈ l
|
||||
| b :: l, H => by
|
||||
by_cases h : p b <;> simp [find?, h] at H
|
||||
· exact H ▸ .head _
|
||||
· exact .tail _ (mem_of_find?_eq_some H)
|
||||
|
||||
@[simp] theorem find?_map (f : β → α) (l : List β) : find? p (l.map f) = (l.find? (p ∘ f)).map f := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [map_cons, find?]
|
||||
by_cases h : p (f x) <;> simp [h, ih]
|
||||
|
||||
theorem find?_replicate : find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
|
||||
cases n
|
||||
· simp
|
||||
· by_cases p a <;> simp_all [replicate_succ]
|
||||
|
||||
@[simp] theorem find?_replicate_of_length_pos (h : 0 < n) : find? p (replicate n a) = if p a then some a else none := by
|
||||
simp [find?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
@[simp] theorem find?_replicate_of_pos (h : p a) : find? p (replicate n a) = if n = 0 then none else some a := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
|
||||
simp [find?_replicate, h]
|
||||
|
||||
theorem find?_isSome_of_sublist {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.find? p).isSome → (l₂.find? p).isSome := by
|
||||
induction h with
|
||||
| slnil => simp
|
||||
| cons a h ih
|
||||
| cons₂ a h ih =>
|
||||
simp only [find?]
|
||||
split <;> simp_all
|
||||
|
||||
/-! ### findSome? -/
|
||||
|
||||
@[simp] theorem findSome?_cons_of_isSome (l) (h : (f a).isSome) : findSome? f (a :: l) = f a := by
|
||||
simp only [findSome?]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem findSome?_cons_of_isNone (l) (h : (f a).isNone) : findSome? f (a :: l) = findSome? f l := by
|
||||
simp only [findSome?]
|
||||
split <;> simp_all
|
||||
|
||||
theorem exists_of_findSome?_eq_some {l : List α} {f : α → Option β} (w : l.findSome? f = some b) :
|
||||
∃ a, a ∈ l ∧ f a = b := by
|
||||
induction l with
|
||||
| nil => simp_all
|
||||
| cons h l ih =>
|
||||
simp_all only [findSome?_cons, mem_cons, exists_eq_or_imp]
|
||||
split at w <;> simp_all
|
||||
|
||||
@[simp] theorem findSome?_map (f : β → γ) (l : List β) : findSome? p (l.map f) = l.findSome? (p ∘ f) := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [map_cons, findSome?]
|
||||
split <;> simp_all
|
||||
|
||||
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate_succ, findSome?_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
|
||||
simp [findSome?_replicate, Nat.ne_of_gt h]
|
||||
|
||||
-- Argument is unused, but used to decide whether `simp` should unfold.
|
||||
@[simp] theorem find?_replicate_of_isSome (_ : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
|
||||
simp [findSome?_replicate]
|
||||
|
||||
@[simp] theorem find?_replicate_of_isNone (h : (f a).isNone) : findSome? f (replicate n a) = none := by
|
||||
rw [Option.isNone_iff_eq_none] at h
|
||||
simp [findSome?_replicate, h]
|
||||
|
||||
theorem findSome?_isSome_of_sublist {l₁ l₂ : List α} (h : l₁ <+ l₂) :
|
||||
(l₁.findSome? f).isSome → (l₂.findSome? f).isSome := by
|
||||
induction h with
|
||||
| slnil => simp
|
||||
| cons a h ih
|
||||
| cons₂ a h ih =>
|
||||
simp only [findSome?]
|
||||
split <;> simp_all
|
||||
|
||||
/-! ### findIdx -/
|
||||
|
||||
theorem findIdx_cons (p : α → Bool) (b : α) (l : List α) :
|
||||
(b :: l).findIdx p = bif p b then 0 else (l.findIdx p) + 1 := by
|
||||
cases H : p b with
|
||||
| true => simp [H, findIdx, findIdx.go]
|
||||
| false => simp [H, findIdx, findIdx.go, findIdx_go_succ]
|
||||
where
|
||||
findIdx_go_succ (p : α → Bool) (l : List α) (n : Nat) :
|
||||
List.findIdx.go p l (n + 1) = (findIdx.go p l n) + 1 := by
|
||||
cases l with
|
||||
| nil => unfold findIdx.go; exact Nat.succ_eq_add_one n
|
||||
| cons head tail =>
|
||||
unfold findIdx.go
|
||||
cases p head <;> simp only [cond_false, cond_true]
|
||||
exact findIdx_go_succ p tail (n + 1)
|
||||
|
||||
theorem findIdx_of_get?_eq_some {xs : List α} (w : xs.get? (xs.findIdx p) = some y) : p y := by
|
||||
induction xs with
|
||||
| nil => simp_all
|
||||
| cons x xs ih => by_cases h : p x <;> simp_all [findIdx_cons]
|
||||
|
||||
theorem findIdx_get {xs : List α} {w : xs.findIdx p < xs.length} :
|
||||
p (xs.get ⟨xs.findIdx p, w⟩) :=
|
||||
xs.findIdx_of_get?_eq_some (get?_eq_get w)
|
||||
|
||||
theorem findIdx_lt_length_of_exists {xs : List α} (h : ∃ x ∈ xs, p x) :
|
||||
xs.findIdx p < xs.length := by
|
||||
induction xs with
|
||||
| nil => simp_all
|
||||
| cons x xs ih =>
|
||||
by_cases p x
|
||||
· simp_all only [forall_exists_index, and_imp, mem_cons, exists_eq_or_imp, true_or,
|
||||
findIdx_cons, cond_true, length_cons]
|
||||
apply Nat.succ_pos
|
||||
· simp_all [findIdx_cons]
|
||||
refine Nat.succ_lt_succ ?_
|
||||
obtain ⟨x', m', h'⟩ := h
|
||||
exact ih x' m' h'
|
||||
|
||||
theorem findIdx_get?_eq_get_of_exists {xs : List α} (h : ∃ x ∈ xs, p x) :
|
||||
xs.get? (xs.findIdx p) = some (xs.get ⟨xs.findIdx p, xs.findIdx_lt_length_of_exists h⟩) :=
|
||||
get?_eq_get (findIdx_lt_length_of_exists h)
|
||||
|
||||
/-! ### findIdx? -/
|
||||
|
||||
@[simp] theorem findIdx?_nil : ([] : List α).findIdx? p i = none := rfl
|
||||
|
||||
@[simp] theorem findIdx?_cons :
|
||||
(x :: xs).findIdx? p i = if p x then some i else findIdx? p xs (i + 1) := rfl
|
||||
|
||||
@[simp] theorem findIdx?_succ :
|
||||
(xs : List α).findIdx? p (i+1) = (xs.findIdx? p i).map fun i => i + 1 := by
|
||||
induction xs generalizing i with simp
|
||||
| cons _ _ _ => split <;> simp_all
|
||||
|
||||
theorem findIdx?_eq_some_iff (xs : List α) (p : α → Bool) :
|
||||
xs.findIdx? p = some i ↔ (xs.take (i + 1)).map p = replicate i false ++ [true] := by
|
||||
induction xs generalizing i with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [findIdx?_cons, Nat.zero_add, findIdx?_succ, take_succ_cons, map_cons]
|
||||
split <;> cases i <;> simp_all [replicate_succ, succ_inj']
|
||||
|
||||
theorem findIdx?_of_eq_some {xs : List α} {p : α → Bool} (w : xs.findIdx? p = some i) :
|
||||
match xs.get? i with | some a => p a | none => false := by
|
||||
induction xs generalizing i with
|
||||
| nil => simp_all
|
||||
| cons x xs ih =>
|
||||
simp_all only [findIdx?_cons, Nat.zero_add, findIdx?_succ]
|
||||
split at w <;> cases i <;> simp_all [succ_inj']
|
||||
|
||||
theorem findIdx?_of_eq_none {xs : List α} {p : α → Bool} (w : xs.findIdx? p = none) :
|
||||
∀ i, match xs.get? i with | some a => ¬ p a | none => true := by
|
||||
intro i
|
||||
induction xs generalizing i with
|
||||
| nil => simp_all
|
||||
| cons x xs ih =>
|
||||
simp_all only [Bool.not_eq_true, findIdx?_cons, Nat.zero_add, findIdx?_succ]
|
||||
cases i with
|
||||
| zero =>
|
||||
split at w <;> simp_all
|
||||
| succ i =>
|
||||
simp only [get?_cons_succ]
|
||||
apply ih
|
||||
split at w <;> simp_all
|
||||
|
||||
@[simp] theorem findIdx?_append :
|
||||
(xs ++ ys : List α).findIdx? p =
|
||||
(xs.findIdx? p <|> (ys.findIdx? p).map fun i => i + xs.length) := by
|
||||
induction xs with simp
|
||||
| cons _ _ _ => split <;> simp_all [Option.map_orElse, Option.map_map]; rfl
|
||||
|
||||
@[simp] theorem findIdx?_replicate :
|
||||
(replicate n a).findIdx? p = if 0 < n ∧ p a then some 0 else none := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate, findIdx?_cons, Nat.zero_add, findIdx?_succ, Nat.zero_lt_succ, true_and]
|
||||
split <;> simp_all
|
||||
|
||||
/-! ### indexOf -/
|
||||
|
||||
theorem indexOf_cons [BEq α] :
|
||||
(x :: xs : List α).indexOf y = bif x == y then 0 else xs.indexOf y + 1 := by
|
||||
dsimp [indexOf]
|
||||
simp [findIdx_cons]
|
||||
|
||||
end List
|
||||
@@ -193,6 +193,17 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
|
||||
apply funext; intro α; apply funext; intro n; apply funext; intro a
|
||||
exact (replicateTR_loop_replicate_eq _ 0 n).symm
|
||||
|
||||
/-! ## Additional functions -/
|
||||
|
||||
/-! ### leftpad -/
|
||||
|
||||
/-- Optimized version of `leftpad`. -/
|
||||
@[inline] def leftpadTR (n : Nat) (a : α) (l : List α) : List α :=
|
||||
replicateTR.loop a (n - length l) l
|
||||
|
||||
@[csimp] theorem leftpad_eq_leftpadTR : @leftpad = @leftpadTR := by
|
||||
funext α n a l; simp [leftpad, leftpadTR, replicateTR_loop_eq]
|
||||
|
||||
/-! ## Sublists -/
|
||||
|
||||
/-! ### take -/
|
||||
@@ -295,6 +306,24 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
|
||||
· rw [IH] <;> simp_all
|
||||
· simp
|
||||
|
||||
/-- Tail-recursive version of `eraseP`. -/
|
||||
@[inline] def erasePTR (p : α → Bool) (l : List α) : List α := go l #[] where
|
||||
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
|
||||
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
|
||||
@[specialize] go : List α → Array α → List α
|
||||
| [], _ => l
|
||||
| a :: l, acc => bif p a then acc.toListAppend l else go l (acc.push a)
|
||||
|
||||
@[csimp] theorem eraseP_eq_erasePTR : @eraseP = @erasePTR := by
|
||||
funext α p l; simp [erasePTR]
|
||||
let rec go (acc) : ∀ xs, l = acc.data ++ xs →
|
||||
erasePTR.go p l xs acc = acc.data ++ xs.eraseP p
|
||||
| [] => fun h => by simp [erasePTR.go, eraseP, h]
|
||||
| x::xs => by
|
||||
simp [erasePTR.go, eraseP]; cases p x <;> simp
|
||||
· intro h; rw [go _ xs]; {simp}; simp [h]
|
||||
exact (go #[] _ rfl).symm
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
/-- Tail recursive version of `List.eraseIdx`. -/
|
||||
@@ -348,6 +377,26 @@ def unzipTR (l : List (α × β)) : List α × List β :=
|
||||
|
||||
/-! ## Ranges and enumeration -/
|
||||
|
||||
/-! ### range' -/
|
||||
|
||||
/-- Optimized version of `range'`. -/
|
||||
@[inline] def range'TR (s n : Nat) (step : Nat := 1) : List Nat := go n (s + step * n) [] where
|
||||
/-- Auxiliary for `range'TR`: `range'TR.go n e = [e-n, ..., e-1] ++ acc`. -/
|
||||
go : Nat → Nat → List Nat → List Nat
|
||||
| 0, _, acc => acc
|
||||
| n+1, e, acc => go n (e-step) ((e-step) :: acc)
|
||||
|
||||
@[csimp] theorem range'_eq_range'TR : @range' = @range'TR := by
|
||||
funext s n step
|
||||
let rec go (s) : ∀ n m,
|
||||
range'TR.go step n (s + step * n) (range' (s + step * n) m step) = range' s (n + m) step
|
||||
| 0, m => by simp [range'TR.go]
|
||||
| n+1, m => by
|
||||
simp [range'TR.go]
|
||||
rw [Nat.mul_succ, ← Nat.add_assoc, Nat.add_sub_cancel, Nat.add_right_comm n]
|
||||
exact go s n (m + 1)
|
||||
exact (go s n 0).symm
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
/-- Tail-recursive version of `List.iota`. -/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
153
src/Init/Data/List/MinMax.lean
Normal file
153
src/Init/Data/List/MinMax.lean
Normal file
@@ -0,0 +1,153 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.minimum?` and `List.maximum?.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Minima and maxima -/
|
||||
|
||||
/-! ### minimum? -/
|
||||
|
||||
@[simp] theorem minimum?_nil [Min α] : ([] : List α).minimum? = none := rfl
|
||||
|
||||
-- We don't put `@[simp]` on `minimum?_cons`,
|
||||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||||
theorem minimum?_cons [Min α] {xs : List α} : (x :: xs).minimum? = foldl min x xs := rfl
|
||||
|
||||
@[simp] theorem minimum?_eq_none_iff {xs : List α} [Min α] : xs.minimum? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [minimum?]
|
||||
|
||||
theorem minimum?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b) :
|
||||
{xs : List α} → xs.minimum? = some a → a ∈ xs := by
|
||||
intro xs
|
||||
match xs with
|
||||
| nil => simp
|
||||
| x :: xs =>
|
||||
simp only [minimum?_cons, Option.some.injEq, List.mem_cons]
|
||||
intro eq
|
||||
induction xs generalizing x with
|
||||
| nil =>
|
||||
simp at eq
|
||||
simp [eq]
|
||||
| cons y xs ind =>
|
||||
simp at eq
|
||||
have p := ind _ eq
|
||||
cases p with
|
||||
| inl p =>
|
||||
cases min_eq_or x y with | _ q => simp [p, q]
|
||||
| inr p => simp [p, mem_cons]
|
||||
|
||||
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
|
||||
|
||||
theorem le_minimum?_iff [Min α] [LE α]
|
||||
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) :
|
||||
{xs : List α} → xs.minimum? = some a → ∀ x, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [minimum?]
|
||||
intro eq y
|
||||
simp only [Option.some.injEq] at eq
|
||||
induction xs generalizing x with
|
||||
| nil =>
|
||||
simp at eq
|
||||
simp [eq]
|
||||
| cons z xs ih =>
|
||||
simp at eq
|
||||
simp [ih _ eq, le_min_iff, and_assoc]
|
||||
|
||||
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
|
||||
-- and `le_min_iff`.
|
||||
theorem minimum?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
(le_refl : ∀ a : α, a ≤ a)
|
||||
(min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b)
|
||||
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) {xs : List α} :
|
||||
xs.minimum? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||||
refine ⟨fun h => ⟨minimum?_mem min_eq_or h, (le_minimum?_iff le_min_iff h _).1 (le_refl _)⟩, ?_⟩
|
||||
intro ⟨h₁, h₂⟩
|
||||
cases xs with
|
||||
| nil => simp at h₁
|
||||
| cons x xs =>
|
||||
exact congrArg some <| anti.1
|
||||
((le_minimum?_iff le_min_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
|
||||
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
|
||||
|
||||
theorem minimum?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
|
||||
(replicate n a).minimum? = if n = 0 then none else some a := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, minimum?_cons]
|
||||
|
||||
@[simp] theorem minimum?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
|
||||
(replicate n a).minimum? = some a := by
|
||||
simp [minimum?_replicate, Nat.ne_of_gt h, w]
|
||||
|
||||
/-! ### maximum? -/
|
||||
|
||||
@[simp] theorem maximum?_nil [Max α] : ([] : List α).maximum? = none := rfl
|
||||
|
||||
-- We don't put `@[simp]` on `maximum?_cons`,
|
||||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||||
theorem maximum?_cons [Max α] {xs : List α} : (x :: xs).maximum? = foldl max x xs := rfl
|
||||
|
||||
@[simp] theorem maximum?_eq_none_iff {xs : List α} [Max α] : xs.maximum? = none ↔ xs = [] := by
|
||||
cases xs <;> simp [maximum?]
|
||||
|
||||
theorem maximum?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a ∨ max a b = b) :
|
||||
{xs : List α} → xs.maximum? = some a → a ∈ xs
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [maximum?]; rintro ⟨⟩
|
||||
induction xs generalizing x with simp at *
|
||||
| cons y xs ih =>
|
||||
rcases ih (max x y) with h | h <;> simp [h]
|
||||
simp [← or_assoc, min_eq_or x y]
|
||||
|
||||
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
|
||||
|
||||
theorem maximum?_le_iff [Max α] [LE α]
|
||||
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) :
|
||||
{xs : List α} → xs.maximum? = some a → ∀ x, a ≤ x ↔ ∀ b ∈ xs, b ≤ x
|
||||
| nil => by simp
|
||||
| cons x xs => by
|
||||
rw [maximum?]; rintro ⟨⟩ y
|
||||
induction xs generalizing x with
|
||||
| nil => simp
|
||||
| cons y xs ih => simp [ih, max_le_iff, and_assoc]
|
||||
|
||||
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `max_eq_or`,
|
||||
-- and `le_min_iff`.
|
||||
theorem maximum?_eq_some_iff [Max α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
|
||||
(le_refl : ∀ a : α, a ≤ a)
|
||||
(max_eq_or : ∀ a b : α, max a b = a ∨ max a b = b)
|
||||
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) {xs : List α} :
|
||||
xs.maximum? = some a ↔ a ∈ xs ∧ ∀ b ∈ xs, b ≤ a := by
|
||||
refine ⟨fun h => ⟨maximum?_mem max_eq_or h, (maximum?_le_iff max_le_iff h _).1 (le_refl _)⟩, ?_⟩
|
||||
intro ⟨h₁, h₂⟩
|
||||
cases xs with
|
||||
| nil => simp at h₁
|
||||
| cons x xs =>
|
||||
exact congrArg some <| anti.1
|
||||
(h₂ _ (maximum?_mem max_eq_or (xs := x::xs) rfl))
|
||||
((maximum?_le_iff max_le_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
|
||||
|
||||
theorem maximum?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
|
||||
(replicate n a).maximum? = if n = 0 then none else some a := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => cases n <;> simp_all [replicate_succ, maximum?_cons]
|
||||
|
||||
@[simp] theorem maximum?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
|
||||
(replicate n a).maximum? = some a := by
|
||||
simp [maximum?_replicate, Nat.ne_of_gt h, w]
|
||||
|
||||
end List
|
||||
69
src/Init/Data/List/Monadic.lean
Normal file
69
src/Init/Data/List/Monadic.lean
Normal file
@@ -0,0 +1,69 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.TakeDrop
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.mapM` and `List.forM`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Monadic operations -/
|
||||
|
||||
-- We may want to replace these `simp` attributes with explicit equational lemmas,
|
||||
-- as we already have for all the non-monadic functions.
|
||||
attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
|
||||
|
||||
-- Previously `mapM.loop`, `filterMapM.loop`, `forIn.loop`, `forIn'.loop`
|
||||
-- had attribute `@[simp]`.
|
||||
-- We don't currently provide simp lemmas,
|
||||
-- as this is an internal implementation and they don't seem to be needed.
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
/-- Alternate (non-tail-recursive) form of mapM for proofs. -/
|
||||
def mapM' [Monad m] (f : α → m β) : List α → m (List β)
|
||||
| [] => pure []
|
||||
| a :: l => return (← f a) :: (← l.mapM' f)
|
||||
|
||||
@[simp] theorem mapM'_nil [Monad m] {f : α → m β} : mapM' f [] = pure [] := rfl
|
||||
@[simp] theorem mapM'_cons [Monad m] {f : α → m β} :
|
||||
mapM' f (a :: l) = return ((← f a) :: (← l.mapM' f)) :=
|
||||
rfl
|
||||
|
||||
theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α) :
|
||||
mapM' f l = mapM f l := by simp [go, mapM] where
|
||||
go : ∀ l acc, mapM.loop f l acc = return acc.reverse ++ (← mapM' f l)
|
||||
| [], acc => by simp [mapM.loop, mapM']
|
||||
| a::l, acc => by simp [go l, mapM.loop, mapM']
|
||||
|
||||
@[simp] theorem mapM_nil [Monad m] (f : α → m β) : [].mapM f = pure [] := rfl
|
||||
|
||||
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α → m β) :
|
||||
(a :: l).mapM f = (return (← f a) :: (← l.mapM f)) := by simp [← mapM'_eq_mapM, mapM']
|
||||
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α → m β) {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).mapM f = (return (← l₁.mapM f) ++ (← l₂.mapM f)) := by induction l₁ <;> simp [*]
|
||||
|
||||
/-! ### forM -/
|
||||
|
||||
-- We use `List.forM` as the simp normal form, rather that `ForM.forM`.
|
||||
-- As such we need to replace `List.forM_nil` and `List.forM_cons`:
|
||||
|
||||
@[simp] theorem forM_nil' [Monad m] : ([] : List α).forM f = (pure .unit : m PUnit) := rfl
|
||||
|
||||
@[simp] theorem forM_cons' [Monad m] :
|
||||
(a::as).forM f = (f a >>= fun _ => as.forM f : m PUnit) :=
|
||||
List.forM_cons _ _ _
|
||||
|
||||
@[simp] theorem forM_append [Monad m] [LawfulMonad m] (l₁ l₂ : List α) (f : α → m PUnit) :
|
||||
(l₁ ++ l₂).forM f = (do l₁.forM f; l₂.forM f) := by
|
||||
induction l₁ <;> simp [*]
|
||||
|
||||
end List
|
||||
10
src/Init/Data/List/Nat.lean
Normal file
10
src/Init/Data/List/Nat.lean
Normal file
@@ -0,0 +1,10 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Nat.Basic
|
||||
import Init.Data.List.Nat.Pairwise
|
||||
import Init.Data.List.Nat.Range
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
125
src/Init/Data/List/Nat/Basic.lean
Normal file
125
src/Init/Data/List/Nat/Basic.lean
Normal file
@@ -0,0 +1,125 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Count
|
||||
import Init.Data.List.MinMax
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
# Miscellaneous `List` lemmas, that require more `Nat` lemmas than are available in `Init.Data.List.Lemmas`.
|
||||
|
||||
In particular, `omega` is available here.
|
||||
-/
|
||||
|
||||
open Nat
|
||||
|
||||
namespace List
|
||||
|
||||
/-! ### filter -/
|
||||
|
||||
theorem length_filter_lt_length_iff_exists (l) :
|
||||
length (filter p l) < length l ↔ ∃ x ∈ l, ¬p x := by
|
||||
simpa [length_eq_countP_add_countP p l, countP_eq_length_filter] using
|
||||
countP_pos (fun x => ¬p x) (l := l)
|
||||
|
||||
/-! ### leftpad -/
|
||||
|
||||
/-- The length of the List returned by `List.leftpad n a l` is equal
|
||||
to the larger of `n` and `l.length` -/
|
||||
@[simp]
|
||||
theorem leftpad_length (n : Nat) (a : α) (l : List α) :
|
||||
(leftpad n a l).length = max n l.length := by
|
||||
simp only [leftpad, length_append, length_replicate, Nat.sub_add_eq_max]
|
||||
|
||||
/-! ### eraseIdx -/
|
||||
|
||||
theorem mem_eraseIdx_iff_getElem {x : α} :
|
||||
∀ {l} {k}, x ∈ eraseIdx l k ↔ ∃ i h, i ≠ k ∧ l[i]'h = x
|
||||
| [], _ => by
|
||||
simp only [eraseIdx, not_mem_nil, false_iff]
|
||||
rintro ⟨i, h, -⟩
|
||||
exact Nat.not_lt_zero _ h
|
||||
| a::l, 0 => by simp [mem_iff_getElem, Nat.succ_lt_succ_iff]
|
||||
| a::l, k+1 => by
|
||||
rw [← Nat.or_exists_add_one]
|
||||
simp [mem_eraseIdx_iff_getElem, @eq_comm _ a, succ_inj', Nat.succ_lt_succ_iff]
|
||||
|
||||
theorem mem_eraseIdx_iff_getElem? {x : α} {l} {k} : x ∈ eraseIdx l k ↔ ∃ i ≠ k, l[i]? = some x := by
|
||||
simp only [mem_eraseIdx_iff_getElem, getElem_eq_iff, exists_and_left]
|
||||
refine exists_congr fun i => and_congr_right' ?_
|
||||
constructor
|
||||
· rintro ⟨_, h⟩; exact h
|
||||
· rintro h;
|
||||
obtain ⟨h', -⟩ := getElem?_eq_some.1 h
|
||||
exact ⟨h', h⟩
|
||||
|
||||
/-! ### minimum? -/
|
||||
|
||||
-- A specialization of `minimum?_eq_some_iff` to Nat.
|
||||
theorem minimum?_eq_some_iff' {xs : List Nat} :
|
||||
xs.minimum? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, a ≤ b) :=
|
||||
minimum?_eq_some_iff
|
||||
(le_refl := Nat.le_refl)
|
||||
(min_eq_or := fun _ _ => by omega)
|
||||
(le_min_iff := fun _ _ _ => by omega)
|
||||
|
||||
-- This could be generalized,
|
||||
-- but will first require further work on order typeclasses in the core repository.
|
||||
theorem minimum?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).minimum? = some (match l.minimum? with
|
||||
| none => a
|
||||
| some m => min a m) := by
|
||||
rw [minimum?_eq_some_iff']
|
||||
split <;> rename_i h m
|
||||
· simp_all
|
||||
· rw [minimum?_eq_some_iff'] at m
|
||||
obtain ⟨m, le⟩ := m
|
||||
rw [Nat.min_def]
|
||||
constructor
|
||||
· split
|
||||
· exact mem_cons_self a l
|
||||
· exact mem_cons_of_mem a m
|
||||
· intro b m
|
||||
cases List.mem_cons.1 m with
|
||||
| inl => split <;> omega
|
||||
| inr h =>
|
||||
specialize le b h
|
||||
split <;> omega
|
||||
|
||||
/-! ### maximum? -/
|
||||
|
||||
-- A specialization of `maximum?_eq_some_iff` to Nat.
|
||||
theorem maximum?_eq_some_iff' {xs : List Nat} :
|
||||
xs.maximum? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, b ≤ a) :=
|
||||
maximum?_eq_some_iff
|
||||
(le_refl := Nat.le_refl)
|
||||
(max_eq_or := fun _ _ => by omega)
|
||||
(max_le_iff := fun _ _ _ => by omega)
|
||||
|
||||
-- This could be generalized,
|
||||
-- but will first require further work on order typeclasses in the core repository.
|
||||
theorem maximum?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).maximum? = some (match l.maximum? with
|
||||
| none => a
|
||||
| some m => max a m) := by
|
||||
rw [maximum?_eq_some_iff']
|
||||
split <;> rename_i h m
|
||||
· simp_all
|
||||
· rw [maximum?_eq_some_iff'] at m
|
||||
obtain ⟨m, le⟩ := m
|
||||
rw [Nat.max_def]
|
||||
constructor
|
||||
· split
|
||||
· exact mem_cons_of_mem a m
|
||||
· exact mem_cons_self a l
|
||||
· intro b m
|
||||
cases List.mem_cons.1 m with
|
||||
| inl => split <;> omega
|
||||
| inr h =>
|
||||
specialize le b h
|
||||
split <;> omega
|
||||
|
||||
end List
|
||||
73
src/Init/Data/List/Nat/Pairwise.lean
Normal file
73
src/Init/Data/List/Nat/Pairwise.lean
Normal file
@@ -0,0 +1,73 @@
|
||||
/-
|
||||
Copyright (c) 2018 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro, James Gallicchio
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Fin.Lemmas
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Pairwise
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.Pairwise`
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
/-- Given a list `is` of monotonically increasing indices into `l`, getting each index
|
||||
produces a sublist of `l`. -/
|
||||
theorem map_getElem_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (· < ·)) :
|
||||
is.map (l[·]) <+ l := by
|
||||
suffices ∀ n l', l' = l.drop n → (∀ i ∈ is, n ≤ i) → map (l[·]) is <+ l'
|
||||
from this 0 l (by simp) (by simp)
|
||||
rintro n l' rfl his
|
||||
induction is generalizing n with
|
||||
| nil => simp
|
||||
| cons hd tl IH =>
|
||||
simp only [Fin.getElem_fin, map_cons]
|
||||
have := IH h.of_cons (hd+1) (pairwise_cons.mp h).1
|
||||
specialize his hd (.head _)
|
||||
have := (drop_eq_getElem_cons ..).symm ▸ this.cons₂ (get l hd)
|
||||
have := Sublist.append (nil_sublist (take hd l |>.drop n)) this
|
||||
rwa [nil_append, ← (drop_append_of_le_length ?_), take_append_drop] at this
|
||||
simp [Nat.min_eq_left (Nat.le_of_lt hd.isLt), his]
|
||||
|
||||
@[deprecated map_getElem_sublist (since := "2024-07-30")]
|
||||
theorem map_get_sublist {l : List α} {is : List (Fin l.length)} (h : is.Pairwise (·.val < ·.val)) :
|
||||
is.map (get l) <+ l := by
|
||||
simpa using map_getElem_sublist h
|
||||
|
||||
/-- Given a sublist `l' <+ l`, there exists an increasing list of indices `is` such that
|
||||
`l' = is.map fun i => l[i]`. -/
|
||||
theorem sublist_eq_map_getElem {l l' : List α} (h : l' <+ l) : ∃ is : List (Fin l.length),
|
||||
l' = is.map (l[·]) ∧ is.Pairwise (· < ·) := by
|
||||
induction h with
|
||||
| slnil => exact ⟨[], by simp⟩
|
||||
| cons _ _ IH =>
|
||||
let ⟨is, IH⟩ := IH
|
||||
refine ⟨is.map (·.succ), ?_⟩
|
||||
simpa [Function.comp_def, pairwise_map]
|
||||
| cons₂ _ _ IH =>
|
||||
rcases IH with ⟨is,IH⟩
|
||||
refine ⟨⟨0, by simp [Nat.zero_lt_succ]⟩ :: is.map (·.succ), ?_⟩
|
||||
simp [Function.comp_def, pairwise_map, IH, ← get_eq_getElem]
|
||||
|
||||
@[deprecated sublist_eq_map_getElem (since := "2024-07-30")]
|
||||
theorem sublist_eq_map_get (h : l' <+ l) : ∃ is : List (Fin l.length),
|
||||
l' = map (get l) is ∧ is.Pairwise (· < ·) := by
|
||||
simpa using sublist_eq_map_getElem h
|
||||
|
||||
theorem pairwise_iff_getElem : Pairwise R l ↔
|
||||
∀ (i j : Nat) (_hi : i < l.length) (_hj : j < l.length) (_hij : i < j), R l[i] l[j] := by
|
||||
rw [pairwise_iff_forall_sublist]
|
||||
constructor <;> intro h
|
||||
· intros i j hi hj h'
|
||||
apply h
|
||||
simpa [h'] using map_getElem_sublist (is := [⟨i, hi⟩, ⟨j, hj⟩])
|
||||
· intros a b h'
|
||||
have ⟨is, h', hij⟩ := sublist_eq_map_getElem h'
|
||||
rcases is with ⟨⟩ | ⟨a', ⟨⟩ | ⟨b', ⟨⟩⟩⟩ <;> simp at h'
|
||||
rcases h' with ⟨rfl, rfl⟩
|
||||
apply h; simpa using hij
|
||||
|
||||
end List
|
||||
387
src/Init/Data/List/Nat/Range.lean
Normal file
387
src/Init/Data/List/Nat/Range.lean
Normal file
@@ -0,0 +1,387 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Pairwise
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.range` and `List.enum`
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Ranges and enumeration -/
|
||||
|
||||
/-! ### range' -/
|
||||
|
||||
theorem range'_succ (s n step) : range' s (n + 1) step = s :: range' (s + step) n step := by
|
||||
simp [range', Nat.add_succ, Nat.mul_succ]
|
||||
|
||||
@[simp] theorem range'_one {s step : Nat} : range' s 1 step = [s] := rfl
|
||||
|
||||
@[simp] theorem length_range' (s step) : ∀ n : Nat, length (range' s n step) = n
|
||||
| 0 => rfl
|
||||
| _ + 1 => congrArg succ (length_range' _ _ _)
|
||||
|
||||
@[simp] theorem range'_eq_nil : range' s n step = [] ↔ n = 0 := by
|
||||
rw [← length_eq_zero, length_range']
|
||||
|
||||
theorem mem_range' : ∀{n}, m ∈ range' s n step ↔ ∃ i < n, m = s + step * i
|
||||
| 0 => by simp [range', Nat.not_lt_zero]
|
||||
| n + 1 => by
|
||||
have h (i) : i ≤ n ↔ i = 0 ∨ ∃ j, i = succ j ∧ j < n := by
|
||||
cases i <;> simp [Nat.succ_le, Nat.succ_inj']
|
||||
simp [range', mem_range', Nat.lt_succ, h]; simp only [← exists_and_right, and_assoc]
|
||||
rw [exists_comm]; simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
|
||||
|
||||
@[simp] theorem mem_range'_1 : m ∈ range' s n ↔ s ≤ m ∧ m < s + n := by
|
||||
simp [mem_range']; exact ⟨
|
||||
fun ⟨i, h, e⟩ => e ▸ ⟨Nat.le_add_right .., Nat.add_lt_add_left h _⟩,
|
||||
fun ⟨h₁, h₂⟩ => ⟨m - s, Nat.sub_lt_left_of_lt_add h₁ h₂, (Nat.add_sub_cancel' h₁).symm⟩⟩
|
||||
|
||||
theorem pairwise_lt_range' s n (step := 1) (pos : 0 < step := by simp) :
|
||||
Pairwise (· < ·) (range' s n step) :=
|
||||
match s, n, step, pos with
|
||||
| _, 0, _, _ => Pairwise.nil
|
||||
| s, n + 1, step, pos => by
|
||||
simp only [range'_succ, pairwise_cons]
|
||||
constructor
|
||||
· intros n m
|
||||
rw [mem_range'] at m
|
||||
omega
|
||||
· exact pairwise_lt_range' (s + step) n step pos
|
||||
|
||||
theorem pairwise_le_range' s n (step := 1) :
|
||||
Pairwise (· ≤ ·) (range' s n step) :=
|
||||
match s, n, step with
|
||||
| _, 0, _ => Pairwise.nil
|
||||
| s, n + 1, step => by
|
||||
simp only [range'_succ, pairwise_cons]
|
||||
constructor
|
||||
· intros n m
|
||||
rw [mem_range'] at m
|
||||
omega
|
||||
· exact pairwise_le_range' (s + step) n step
|
||||
|
||||
theorem nodup_range' (s n : Nat) (step := 1) (h : 0 < step := by simp) : Nodup (range' s n step) :=
|
||||
(pairwise_lt_range' s n step h).imp Nat.ne_of_lt
|
||||
|
||||
@[simp]
|
||||
theorem map_add_range' (a) : ∀ s n step, map (a + ·) (range' s n step) = range' (a + s) n step
|
||||
| _, 0, _ => rfl
|
||||
| s, n + 1, step => by simp [range', map_add_range' _ (s + step) n step, Nat.add_assoc]
|
||||
|
||||
theorem map_sub_range' (a s n : Nat) (h : a ≤ s) :
|
||||
map (· - a) (range' s n step) = range' (s - a) n step := by
|
||||
conv => lhs; rw [← Nat.add_sub_cancel' h]
|
||||
rw [← map_add_range', map_map, (?_ : _∘_ = _), map_id]
|
||||
funext x; apply Nat.add_sub_cancel_left
|
||||
|
||||
theorem range'_append : ∀ s m n step : Nat,
|
||||
range' s m step ++ range' (s + step * m) n step = range' s (n + m) step
|
||||
| s, 0, n, step => rfl
|
||||
| s, m + 1, n, step => by
|
||||
simpa [range', Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
|
||||
using range'_append (s + step) m n step
|
||||
|
||||
@[simp] theorem range'_append_1 (s m n : Nat) :
|
||||
range' s m ++ range' (s + m) n = range' s (n + m) := by simpa using range'_append s m n 1
|
||||
|
||||
theorem range'_sublist_right {s m n : Nat} : range' s m step <+ range' s n step ↔ m ≤ n :=
|
||||
⟨fun h => by simpa only [length_range'] using h.length_le,
|
||||
fun h => by rw [← Nat.sub_add_cancel h, ← range'_append]; apply sublist_append_left⟩
|
||||
|
||||
theorem range'_subset_right {s m n : Nat} (step0 : 0 < step) :
|
||||
range' s m step ⊆ range' s n step ↔ m ≤ n := by
|
||||
refine ⟨fun h => Nat.le_of_not_lt fun hn => ?_, fun h => (range'_sublist_right.2 h).subset⟩
|
||||
have ⟨i, h', e⟩ := mem_range'.1 <| h <| mem_range'.2 ⟨_, hn, rfl⟩
|
||||
exact Nat.ne_of_gt h' (Nat.eq_of_mul_eq_mul_left step0 (Nat.add_left_cancel e))
|
||||
|
||||
theorem range'_subset_right_1 {s m n : Nat} : range' s m ⊆ range' s n ↔ m ≤ n :=
|
||||
range'_subset_right (by decide)
|
||||
|
||||
theorem getElem?_range' (s step) :
|
||||
∀ {m n : Nat}, m < n → (range' s n step)[m]? = some (s + step * m)
|
||||
| 0, n + 1, _ => by simp [range'_succ]
|
||||
| m + 1, n + 1, h => by
|
||||
simp only [range'_succ, getElem?_cons_succ]
|
||||
exact (getElem?_range' (s + step) step (Nat.lt_of_add_lt_add_right h)).trans <| by
|
||||
simp [Nat.mul_succ, Nat.add_assoc, Nat.add_comm]
|
||||
|
||||
@[simp] theorem getElem_range' {n m step} (i) (H : i < (range' n m step).length) :
|
||||
(range' n m step)[i] = n + step * i :=
|
||||
(getElem?_eq_some.1 <| getElem?_range' n step (by simpa using H)).2
|
||||
|
||||
theorem range'_concat (s n : Nat) : range' s (n + 1) step = range' s n step ++ [s + step * n] := by
|
||||
rw [Nat.add_comm n 1]; exact (range'_append s n 1 step).symm
|
||||
|
||||
theorem range'_1_concat (s n : Nat) : range' s (n + 1) = range' s n ++ [s + n] := by
|
||||
simp [range'_concat]
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
theorem range_loop_range' : ∀ s n : Nat, range.loop s (range' s n) = range' 0 (n + s)
|
||||
| 0, n => rfl
|
||||
| s + 1, n => by rw [← Nat.add_assoc, Nat.add_right_comm n s 1]; exact range_loop_range' s (n + 1)
|
||||
|
||||
theorem range_eq_range' (n : Nat) : range n = range' 0 n :=
|
||||
(range_loop_range' n 0).trans <| by rw [Nat.zero_add]
|
||||
|
||||
theorem range_succ_eq_map (n : Nat) : range (n + 1) = 0 :: map succ (range n) := by
|
||||
rw [range_eq_range', range_eq_range', range', Nat.add_comm, ← map_add_range']
|
||||
congr; exact funext (Nat.add_comm 1)
|
||||
|
||||
theorem reverse_range' : ∀ s n : Nat, reverse (range' s n) = map (s + n - 1 - ·) (range n)
|
||||
| s, 0 => rfl
|
||||
| s, n + 1 => by
|
||||
rw [range'_1_concat, reverse_append, range_succ_eq_map,
|
||||
show s + (n + 1) - 1 = s + n from rfl, map, map_map]
|
||||
simp [reverse_range', Nat.sub_right_comm, Nat.sub_sub]
|
||||
|
||||
theorem range'_eq_map_range (s n : Nat) : range' s n = map (s + ·) (range n) := by
|
||||
rw [range_eq_range', map_add_range']; rfl
|
||||
|
||||
@[simp] theorem length_range (n : Nat) : length (range n) = n := by
|
||||
simp only [range_eq_range', length_range']
|
||||
|
||||
@[simp] theorem range_eq_nil {n : Nat} : range n = [] ↔ n = 0 := by
|
||||
rw [← length_eq_zero, length_range]
|
||||
|
||||
@[simp]
|
||||
theorem range_sublist {m n : Nat} : range m <+ range n ↔ m ≤ n := by
|
||||
simp only [range_eq_range', range'_sublist_right]
|
||||
|
||||
@[simp]
|
||||
theorem range_subset {m n : Nat} : range m ⊆ range n ↔ m ≤ n := by
|
||||
simp only [range_eq_range', range'_subset_right, lt_succ_self]
|
||||
|
||||
@[simp]
|
||||
theorem mem_range {m n : Nat} : m ∈ range n ↔ m < n := by
|
||||
simp only [range_eq_range', mem_range'_1, Nat.zero_le, true_and, Nat.zero_add]
|
||||
|
||||
theorem not_mem_range_self {n : Nat} : n ∉ range n := by simp
|
||||
|
||||
theorem self_mem_range_succ (n : Nat) : n ∈ range (n + 1) := by simp
|
||||
|
||||
theorem pairwise_lt_range (n : Nat) : Pairwise (· < ·) (range n) := by
|
||||
simp (config := {decide := true}) only [range_eq_range', pairwise_lt_range']
|
||||
|
||||
theorem pairwise_le_range (n : Nat) : Pairwise (· ≤ ·) (range n) :=
|
||||
Pairwise.imp Nat.le_of_lt (pairwise_lt_range _)
|
||||
|
||||
theorem getElem?_range {m n : Nat} (h : m < n) : (range n)[m]? = some m := by
|
||||
simp [range_eq_range', getElem?_range' _ _ h]
|
||||
|
||||
@[simp] theorem getElem_range {n : Nat} (m) (h : m < (range n).length) : (range n)[m] = m := by
|
||||
simp [range_eq_range']
|
||||
|
||||
theorem range_succ (n : Nat) : range (succ n) = range n ++ [n] := by
|
||||
simp only [range_eq_range', range'_1_concat, Nat.zero_add]
|
||||
|
||||
theorem range_add (a b : Nat) : range (a + b) = range a ++ (range b).map (a + ·) := by
|
||||
rw [← range'_eq_map_range]
|
||||
simpa [range_eq_range', Nat.add_comm] using (range'_append_1 0 a b).symm
|
||||
|
||||
theorem take_range (m n : Nat) : take m (range n) = range (min m n) := by
|
||||
apply List.ext_getElem
|
||||
· simp
|
||||
· simp (config := { contextual := true }) [← getElem_take, Nat.lt_min]
|
||||
|
||||
theorem nodup_range (n : Nat) : Nodup (range n) := by
|
||||
simp (config := {decide := true}) only [range_eq_range', nodup_range']
|
||||
|
||||
/-! ### iota -/
|
||||
|
||||
theorem iota_eq_reverse_range' : ∀ n : Nat, iota n = reverse (range' 1 n)
|
||||
| 0 => rfl
|
||||
| n + 1 => by simp [iota, range'_concat, iota_eq_reverse_range' n, reverse_append, Nat.add_comm]
|
||||
|
||||
@[simp] theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
|
||||
|
||||
@[simp]
|
||||
theorem mem_iota {m n : Nat} : m ∈ iota n ↔ 1 ≤ m ∧ m ≤ n := by
|
||||
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
|
||||
|
||||
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
|
||||
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range' 1 n
|
||||
|
||||
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
|
||||
(pairwise_gt_iota n).imp Nat.ne_of_gt
|
||||
|
||||
/-! ### enumFrom -/
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_singleton (x : α) (n : Nat) : enumFrom n [x] = [(n, x)] :=
|
||||
rfl
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_eq_nil {n : Nat} {l : List α} : List.enumFrom n l = [] ↔ l = [] := by
|
||||
cases l <;> simp
|
||||
|
||||
@[simp] theorem enumFrom_length : ∀ {n} {l : List α}, (enumFrom n l).length = l.length
|
||||
| _, [] => rfl
|
||||
| _, _ :: _ => congrArg Nat.succ enumFrom_length
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_enumFrom :
|
||||
∀ n (l : List α) m, (enumFrom n l)[m]? = l[m]?.map fun a => (n + m, a)
|
||||
| n, [], m => rfl
|
||||
| n, a :: l, 0 => by simp
|
||||
| n, a :: l, m + 1 => by
|
||||
simp only [enumFrom_cons, getElem?_cons_succ]
|
||||
exact (getElem?_enumFrom (n + 1) l m).trans <| by rw [Nat.add_right_comm]; rfl
|
||||
|
||||
@[simp]
|
||||
theorem getElem_enumFrom (l : List α) (n) (i : Nat) (h : i < (l.enumFrom n).length) :
|
||||
(l.enumFrom n)[i] = (n + i, l[i]'(by simpa [enumFrom_length] using h)) := by
|
||||
simp only [enumFrom_length] at h
|
||||
rw [getElem_eq_getElem?]
|
||||
simp only [getElem?_enumFrom, getElem?_eq_getElem h]
|
||||
simp
|
||||
|
||||
theorem mk_add_mem_enumFrom_iff_getElem? {n i : Nat} {x : α} {l : List α} :
|
||||
(n + i, x) ∈ enumFrom n l ↔ l[i]? = some x := by
|
||||
simp [mem_iff_get?]
|
||||
|
||||
theorem mk_mem_enumFrom_iff_le_and_getElem?_sub {n i : Nat} {x : α} {l : List α} :
|
||||
(i, x) ∈ enumFrom n l ↔ n ≤ i ∧ l[i - n]? = x := by
|
||||
if h : n ≤ i then
|
||||
rcases Nat.exists_eq_add_of_le h with ⟨i, rfl⟩
|
||||
simp [mk_add_mem_enumFrom_iff_getElem?, Nat.add_sub_cancel_left]
|
||||
else
|
||||
have : ∀ k, n + k ≠ i := by rintro k rfl; simp at h
|
||||
simp [h, mem_iff_get?, this]
|
||||
|
||||
theorem le_fst_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x ∈ enumFrom n l) :
|
||||
n ≤ x.1 :=
|
||||
(mk_mem_enumFrom_iff_le_and_getElem?_sub.1 h).1
|
||||
|
||||
theorem fst_lt_add_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x ∈ enumFrom n l) :
|
||||
x.1 < n + length l := by
|
||||
rcases mem_iff_get.1 h with ⟨i, rfl⟩
|
||||
simpa using i.isLt
|
||||
|
||||
theorem map_enumFrom (f : α → β) (n : Nat) (l : List α) :
|
||||
map (Prod.map id f) (enumFrom n l) = enumFrom n (map f l) := by
|
||||
induction l generalizing n <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_map_fst (n) :
|
||||
∀ (l : List α), map Prod.fst (enumFrom n l) = range' n l.length
|
||||
| [] => rfl
|
||||
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
|
||||
|
||||
@[simp]
|
||||
theorem enumFrom_map_snd : ∀ (n) (l : List α), map Prod.snd (enumFrom n l) = l
|
||||
| _, [] => rfl
|
||||
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
|
||||
|
||||
theorem snd_mem_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x ∈ enumFrom n l) : x.2 ∈ l :=
|
||||
enumFrom_map_snd n l ▸ mem_map_of_mem _ h
|
||||
|
||||
theorem mem_enumFrom {x : α} {i j : Nat} (xs : List α) (h : (i, x) ∈ xs.enumFrom j) :
|
||||
j ≤ i ∧ i < j + xs.length ∧ x ∈ xs :=
|
||||
⟨le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_mem_of_mem_enumFrom h⟩
|
||||
|
||||
theorem map_fst_add_enumFrom_eq_enumFrom (l : List α) (n k : Nat) :
|
||||
map (Prod.map (· + n) id) (enumFrom k l) = enumFrom (n + k) l :=
|
||||
ext_getElem? fun i ↦ by simp [(· ∘ ·), Nat.add_comm, Nat.add_left_comm]; rfl
|
||||
|
||||
theorem map_fst_add_enum_eq_enumFrom (l : List α) (n : Nat) :
|
||||
map (Prod.map (· + n) id) (enum l) = enumFrom n l :=
|
||||
map_fst_add_enumFrom_eq_enumFrom l _ _
|
||||
|
||||
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
|
||||
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
|
||||
rw [enumFrom_cons, Nat.add_comm, ← map_fst_add_enumFrom_eq_enumFrom]
|
||||
|
||||
theorem enumFrom_map (n : Nat) (l : List α) (f : α → β) :
|
||||
enumFrom n (l.map f) = (enumFrom n l).map (Prod.map id f) := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons hd tl IH =>
|
||||
rw [map_cons, enumFrom_cons', enumFrom_cons', map_cons, map_map, IH, map_map]
|
||||
rfl
|
||||
|
||||
theorem enumFrom_append (xs ys : List α) (n : Nat) :
|
||||
enumFrom n (xs ++ ys) = enumFrom n xs ++ enumFrom (n + xs.length) ys := by
|
||||
induction xs generalizing ys n with
|
||||
| nil => simp
|
||||
| cons x xs IH =>
|
||||
rw [cons_append, enumFrom_cons, IH, ← cons_append, ← enumFrom_cons, length, Nat.add_right_comm,
|
||||
Nat.add_assoc]
|
||||
|
||||
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
|
||||
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
|
||||
|
||||
@[simp]
|
||||
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
|
||||
(l.enumFrom n).unzip = (range' n l.length, l) := by
|
||||
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
|
||||
|
||||
/-! ### enum -/
|
||||
|
||||
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
|
||||
|
||||
theorem enum_cons' (x : α) (xs : List α) :
|
||||
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
|
||||
enumFrom_cons' _ _ _
|
||||
|
||||
@[simp]
|
||||
theorem enum_eq_nil {l : List α} : List.enum l = [] ↔ l = [] := enumFrom_eq_nil
|
||||
|
||||
@[simp] theorem enum_singleton (x : α) : enum [x] = [(0, x)] := rfl
|
||||
|
||||
@[simp] theorem enum_length : (enum l).length = l.length :=
|
||||
enumFrom_length
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_enum (l : List α) (n : Nat) : (enum l)[n]? = l[n]?.map fun a => (n, a) := by
|
||||
rw [enum, getElem?_enumFrom, Nat.zero_add]
|
||||
|
||||
@[simp]
|
||||
theorem getElem_enum (l : List α) (i : Nat) (h : i < l.enum.length) :
|
||||
l.enum[i] = (i, l[i]'(by simpa [enum_length] using h)) := by
|
||||
simp [enum]
|
||||
|
||||
theorem mk_mem_enum_iff_getElem? {i : Nat} {x : α} {l : List α} : (i, x) ∈ enum l ↔ l[i]? = x := by
|
||||
simp [enum, mk_mem_enumFrom_iff_le_and_getElem?_sub]
|
||||
|
||||
theorem mem_enum_iff_getElem? {x : Nat × α} {l : List α} : x ∈ enum l ↔ l[x.1]? = some x.2 :=
|
||||
mk_mem_enum_iff_getElem?
|
||||
|
||||
theorem fst_lt_of_mem_enum {x : Nat × α} {l : List α} (h : x ∈ enum l) : x.1 < length l := by
|
||||
simpa using fst_lt_add_of_mem_enumFrom h
|
||||
|
||||
theorem snd_mem_of_mem_enum {x : Nat × α} {l : List α} (h : x ∈ enum l) : x.2 ∈ l :=
|
||||
snd_mem_of_mem_enumFrom h
|
||||
|
||||
theorem map_enum (f : α → β) (l : List α) : map (Prod.map id f) (enum l) = enum (map f l) :=
|
||||
map_enumFrom f 0 l
|
||||
|
||||
@[simp] theorem enum_map_fst (l : List α) : map Prod.fst (enum l) = range l.length := by
|
||||
simp only [enum, enumFrom_map_fst, range_eq_range']
|
||||
|
||||
@[simp]
|
||||
theorem enum_map_snd (l : List α) : map Prod.snd (enum l) = l :=
|
||||
enumFrom_map_snd _ _
|
||||
|
||||
theorem enum_map (l : List α) (f : α → β) : (l.map f).enum = l.enum.map (Prod.map id f) :=
|
||||
enumFrom_map _ _ _
|
||||
|
||||
theorem enum_append (xs ys : List α) : enum (xs ++ ys) = enum xs ++ enumFrom xs.length ys := by
|
||||
simp [enum, enumFrom_append]
|
||||
|
||||
theorem enum_eq_zip_range (l : List α) : l.enum = (range l.length).zip l :=
|
||||
zip_of_prod (enum_map_fst _) (enum_map_snd _)
|
||||
|
||||
@[simp]
|
||||
theorem unzip_enum_eq_prod (l : List α) : l.enum.unzip = (range l.length, l) := by
|
||||
simp only [enum_eq_zip_range, unzip_zip, length_range]
|
||||
|
||||
end List
|
||||
503
src/Init/Data/List/Nat/TakeDrop.lean
Normal file
503
src/Init/Data/List/Nat/TakeDrop.lean
Normal file
@@ -0,0 +1,503 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Zip
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
# Further lemmas about `List.take`, `List.drop`, `List.zip` and `List.zipWith`.
|
||||
|
||||
These are in a separate file from most of the list lemmas
|
||||
as they required importing more lemmas about natural numbers, and use `omega`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### take -/
|
||||
|
||||
@[simp] theorem length_take : ∀ (i : Nat) (l : List α), length (take i l) = min i (length l)
|
||||
| 0, l => by simp [Nat.zero_min]
|
||||
| succ n, [] => by simp [Nat.min_zero]
|
||||
| succ n, _ :: l => by simp [Nat.succ_min_succ, length_take]
|
||||
|
||||
theorem length_take_le (n) (l : List α) : length (take n l) ≤ n := by simp [Nat.min_le_left]
|
||||
|
||||
theorem length_take_le' (n) (l : List α) : length (take n l) ≤ l.length :=
|
||||
by simp [Nat.min_le_right]
|
||||
|
||||
theorem length_take_of_le (h : n ≤ length l) : length (take n l) = n := by simp [Nat.min_eq_left h]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the big list to the small list. -/
|
||||
theorem getElem_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
|
||||
L[i] = (L.take j)[i]'(length_take .. ▸ Nat.lt_min.mpr ⟨hj, hi⟩) :=
|
||||
getElem_of_eq (take_append_drop j L).symm _ ▸ getElem_append ..
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
theorem getElem_take' (L : List α) {j i : Nat} {h : i < (L.take j).length} :
|
||||
(L.take j)[i] =
|
||||
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
|
||||
rw [length_take, Nat.lt_min] at h; rw [getElem_take L _ h.1]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the big list to the small list. -/
|
||||
@[deprecated getElem_take (since := "2024-06-12")]
|
||||
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
|
||||
get L ⟨i, hi⟩ = get (L.take j) ⟨i, length_take .. ▸ Nat.lt_min.mpr ⟨hj, hi⟩⟩ := by
|
||||
simp [getElem_take _ hi hj]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
@[deprecated getElem_take (since := "2024-06-12")]
|
||||
theorem get_take' (L : List α) {j i} :
|
||||
get (L.take j) i =
|
||||
get L ⟨i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _)⟩ := by
|
||||
simp [getElem_take']
|
||||
|
||||
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n)[m]? = none :=
|
||||
getElem?_eq_none <| Nat.le_trans (length_take_le _ _) h
|
||||
|
||||
@[deprecated getElem?_take_eq_none (since := "2024-06-12")]
|
||||
theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n).get? m = none := by
|
||||
simp [getElem?_take_eq_none h]
|
||||
|
||||
theorem getElem?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n)[m]? = if m < n then l[m]? else none := by
|
||||
split
|
||||
· next h => exact getElem?_take h
|
||||
· next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
|
||||
|
||||
@[deprecated getElem?_take_eq_if (since := "2024-06-12")]
|
||||
theorem get?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n).get? m = if m < n then l.get? m else none := by
|
||||
simp [getElem?_take_eq_if]
|
||||
|
||||
theorem head?_take {l : List α} {n : Nat} :
|
||||
(l.take n).head? = if n = 0 then none else l.head? := by
|
||||
simp [head?_eq_getElem?, getElem?_take_eq_if]
|
||||
split
|
||||
· rw [if_neg (by omega)]
|
||||
· rw [if_pos (by omega)]
|
||||
|
||||
theorem head_take {l : List α} {n : Nat} (h : l.take n ≠ []) :
|
||||
(l.take n).head h = l.head (by simp_all) := by
|
||||
apply Option.some_inj.1
|
||||
rw [← head?_eq_head, ← head?_eq_head, head?_take, if_neg]
|
||||
simp_all
|
||||
|
||||
theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none else l[n - 1]?.or l.getLast? := by
|
||||
rw [getLast?_eq_getElem?, getElem?_take_eq_if, length_take]
|
||||
split
|
||||
· rw [if_neg (by omega)]
|
||||
rw [Nat.min_def]
|
||||
split
|
||||
· rw [getElem?_eq_getElem (by omega)]
|
||||
simp
|
||||
· rw [← getLast?_eq_getElem?, getElem?_eq_none (by omega)]
|
||||
simp
|
||||
· rw [if_pos]
|
||||
omega
|
||||
|
||||
theorem getLast_take {l : List α} (h : l.take n ≠ []) :
|
||||
(l.take n).getLast h = l[n - 1]?.getD (l.getLast (by simp_all)) := by
|
||||
rw [getLast_eq_getElem, getElem_take']
|
||||
simp [length_take, Nat.min_def]
|
||||
simp at h
|
||||
split
|
||||
· rw [getElem?_eq_getElem (by omega)]
|
||||
simp
|
||||
· rw [getElem?_eq_none (by omega), getLast_eq_getElem]
|
||||
simp
|
||||
|
||||
theorem take_take : ∀ (n m) (l : List α), take n (take m l) = take (min n m) l
|
||||
| n, 0, l => by rw [Nat.min_zero, take_zero, take_nil]
|
||||
| 0, m, l => by rw [Nat.zero_min, take_zero, take_zero]
|
||||
| succ n, succ m, nil => by simp only [take_nil]
|
||||
| succ n, succ m, a :: l => by
|
||||
simp only [take, succ_min_succ, take_take n m l]
|
||||
|
||||
theorem take_set_of_lt (a : α) {n m : Nat} (l : List α) (h : m < n) :
|
||||
(l.set n a).take m = l.take m :=
|
||||
List.ext_getElem? fun i => by
|
||||
rw [getElem?_take_eq_if, getElem?_take_eq_if]
|
||||
split
|
||||
· next h' => rw [getElem?_set_ne (by omega)]
|
||||
· rfl
|
||||
|
||||
@[simp] theorem take_replicate (a : α) : ∀ n m : Nat, take n (replicate m a) = replicate (min n m) a
|
||||
| n, 0 => by simp [Nat.min_zero]
|
||||
| 0, m => by simp [Nat.zero_min]
|
||||
| succ n, succ m => by simp [replicate_succ, succ_min_succ, take_replicate]
|
||||
|
||||
@[simp] theorem drop_replicate (a : α) : ∀ n m : Nat, drop n (replicate m a) = replicate (m - n) a
|
||||
| n, 0 => by simp
|
||||
| 0, m => by simp
|
||||
| succ n, succ m => by simp [replicate_succ, succ_sub_succ, drop_replicate]
|
||||
|
||||
/-- Taking the first `n` elements in `l₁ ++ l₂` is the same as appending the first `n` elements
|
||||
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
|
||||
theorem take_append_eq_append_take {l₁ l₂ : List α} {n : Nat} :
|
||||
take n (l₁ ++ l₂) = take n l₁ ++ take (n - l₁.length) l₂ := by
|
||||
induction l₁ generalizing n
|
||||
· simp
|
||||
· cases n
|
||||
· simp [*]
|
||||
· simp only [cons_append, take_succ_cons, length_cons, succ_eq_add_one, cons.injEq,
|
||||
append_cancel_left_eq, true_and, *]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem take_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n ≤ l₁.length) :
|
||||
(l₁ ++ l₂).take n = l₁.take n := by
|
||||
simp [take_append_eq_append_take, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
|
||||
`i` elements of `l₂` to `l₁`. -/
|
||||
theorem take_append {l₁ l₂ : List α} (i : Nat) :
|
||||
take (l₁.length + i) (l₁ ++ l₂) = l₁ ++ take i l₂ := by
|
||||
rw [take_append_eq_append_take, take_of_length_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
|
||||
|
||||
@[simp]
|
||||
theorem take_eq_take :
|
||||
∀ {l : List α} {m n : Nat}, l.take m = l.take n ↔ min m l.length = min n l.length
|
||||
| [], m, n => by simp [Nat.min_zero]
|
||||
| _ :: xs, 0, 0 => by simp
|
||||
| x :: xs, m + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
|
||||
| x :: xs, 0, n + 1 => by simp [Nat.zero_min, succ_min_succ]
|
||||
| x :: xs, m + 1, n + 1 => by simp [succ_min_succ, take_eq_take]
|
||||
|
||||
theorem take_add (l : List α) (m n : Nat) : l.take (m + n) = l.take m ++ (l.drop m).take n := by
|
||||
suffices take (m + n) (take m l ++ drop m l) = take m l ++ take n (drop m l) by
|
||||
rw [take_append_drop] at this
|
||||
assumption
|
||||
rw [take_append_eq_append_take, take_of_length_le, append_right_inj]
|
||||
· simp only [take_eq_take, length_take, length_drop]
|
||||
omega
|
||||
apply Nat.le_trans (m := m)
|
||||
· apply length_take_le
|
||||
· apply Nat.le_add_right
|
||||
|
||||
theorem dropLast_take {n : Nat} {l : List α} (h : n < l.length) :
|
||||
(l.take n).dropLast = l.take (n - 1) := by
|
||||
simp only [dropLast_eq_take, length_take, Nat.le_of_lt h, Nat.min_eq_left, take_take, sub_le]
|
||||
|
||||
theorem map_eq_append_split {f : α → β} {l : List α} {s₁ s₂ : List β}
|
||||
(h : map f l = s₁ ++ s₂) : ∃ l₁ l₂, l = l₁ ++ l₂ ∧ map f l₁ = s₁ ∧ map f l₂ = s₂ := by
|
||||
have := h
|
||||
rw [← take_append_drop (length s₁) l] at this ⊢
|
||||
rw [map_append] at this
|
||||
refine ⟨_, _, rfl, append_inj this ?_⟩
|
||||
rw [length_map, length_take, Nat.min_eq_left]
|
||||
rw [← length_map l f, h, length_append]
|
||||
apply Nat.le_add_right
|
||||
|
||||
/-! ### drop -/
|
||||
|
||||
theorem lt_length_drop (L : List α) {i j : Nat} (h : i + j < L.length) : j < (L.drop i).length := by
|
||||
have A : i < L.length := Nat.lt_of_le_of_lt (Nat.le.intro rfl) h
|
||||
rw [(take_append_drop i L).symm] at h
|
||||
simpa only [Nat.le_of_lt A, Nat.min_eq_left, Nat.add_lt_add_iff_left, length_take,
|
||||
length_append] using h
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
|
||||
theorem getElem_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
|
||||
L[i + j] = (L.drop i)[j]'(lt_length_drop L h) := by
|
||||
have : i ≤ L.length := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_lt h)
|
||||
rw [getElem_of_eq (take_append_drop i L).symm h, getElem_append_right'] <;>
|
||||
simp [Nat.min_eq_left this, Nat.add_sub_cancel_left, Nat.le_add_right]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
|
||||
@[deprecated getElem_drop (since := "2024-06-12")]
|
||||
theorem get_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
|
||||
get L ⟨i + j, h⟩ = get (L.drop i) ⟨j, lt_length_drop L h⟩ := by
|
||||
simp [getElem_drop]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
|
||||
theorem getElem_drop' (L : List α) {i : Nat} {j : Nat} {h : j < (L.drop i).length} :
|
||||
(L.drop i)[j] = L[i + j]'(by
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub (length_drop i L ▸ h)) := by
|
||||
rw [getElem_drop]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
|
||||
@[deprecated getElem_drop' (since := "2024-06-12")]
|
||||
theorem get_drop' (L : List α) {i j} :
|
||||
get (L.drop i) j = get L ⟨i + j, by
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub (length_drop i L ▸ j.2)⟩ := by
|
||||
simp [getElem_drop']
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? := by
|
||||
ext
|
||||
simp only [getElem?_eq_some, getElem_drop', Option.mem_def]
|
||||
constructor <;> intro ⟨h, ha⟩
|
||||
· exact ⟨_, ha⟩
|
||||
· refine ⟨?_, ha⟩
|
||||
rw [length_drop]
|
||||
rw [Nat.add_comm] at h
|
||||
apply Nat.lt_sub_of_add_lt h
|
||||
|
||||
@[deprecated getElem?_drop (since := "2024-06-12")]
|
||||
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
|
||||
simp
|
||||
|
||||
theorem head?_drop (l : List α) (n : Nat) :
|
||||
(l.drop n).head? = l[n]? := by
|
||||
rw [head?_eq_getElem?, getElem?_drop, Nat.add_zero]
|
||||
|
||||
theorem head_drop {l : List α} {n : Nat} (h : l.drop n ≠ []) :
|
||||
(l.drop n).head h = l[n]'(by simp_all) := by
|
||||
have w : n < l.length := length_lt_of_drop_ne_nil h
|
||||
simpa [head?_eq_head, getElem?_eq_getElem, h, w] using head?_drop l n
|
||||
|
||||
theorem getLast?_drop {l : List α} : (l.drop n).getLast? = if l.length ≤ n then none else l.getLast? := by
|
||||
rw [getLast?_eq_getElem?, getElem?_drop]
|
||||
rw [length_drop]
|
||||
split
|
||||
· rw [getElem?_eq_none (by omega)]
|
||||
· rw [getLast?_eq_getElem?]
|
||||
congr
|
||||
omega
|
||||
|
||||
theorem getLast_drop {l : List α} (h : l.drop n ≠ []) :
|
||||
(l.drop n).getLast h = l.getLast (ne_nil_of_length_pos (by simp at h; omega)) := by
|
||||
simp only [ne_eq, drop_eq_nil_iff_le] at h
|
||||
apply Option.some_inj.1
|
||||
simp only [← getLast?_eq_getLast, getLast?_drop, ite_eq_right_iff]
|
||||
omega
|
||||
|
||||
theorem drop_length_cons {l : List α} (h : l ≠ []) (a : α) :
|
||||
(a :: l).drop l.length = [l.getLast h] := by
|
||||
induction l generalizing a with
|
||||
| nil =>
|
||||
cases h rfl
|
||||
| cons y l ih =>
|
||||
simp only [drop, length]
|
||||
by_cases h₁ : l = []
|
||||
· simp [h₁]
|
||||
rw [getLast_cons h₁]
|
||||
exact ih h₁ y
|
||||
|
||||
/-- Dropping the elements up to `n` in `l₁ ++ l₂` is the same as dropping the elements up to `n`
|
||||
in `l₁`, dropping the elements up to `n - l₁.length` in `l₂`, and appending them. -/
|
||||
theorem drop_append_eq_append_drop {l₁ l₂ : List α} {n : Nat} :
|
||||
drop n (l₁ ++ l₂) = drop n l₁ ++ drop (n - l₁.length) l₂ := by
|
||||
induction l₁ generalizing n
|
||||
· simp
|
||||
· cases n
|
||||
· simp [*]
|
||||
· simp only [cons_append, drop_succ_cons, length_cons, succ_eq_add_one, append_cancel_left_eq, *]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem drop_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n ≤ l₁.length) :
|
||||
(l₁ ++ l₂).drop n = l₁.drop n ++ l₂ := by
|
||||
simp [drop_append_eq_append_drop, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
|
||||
up to `i` in `l₂`. -/
|
||||
@[simp]
|
||||
theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
|
||||
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
|
||||
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
|
||||
|
||||
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
|
||||
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
|
||||
split <;> rename_i h
|
||||
· ext1 m
|
||||
by_cases h' : m < n
|
||||
· rw [getElem?_append (by simp [length_take]; omega), getElem?_set_ne (by omega),
|
||||
getElem?_take h']
|
||||
· by_cases h'' : m = n
|
||||
· subst h''
|
||||
rw [getElem?_set_eq ‹_›, getElem?_append_right, length_take,
|
||||
Nat.min_eq_left (by omega), Nat.sub_self, getElem?_cons_zero]
|
||||
rw [length_take]
|
||||
exact Nat.min_le_left m l.length
|
||||
· have h''' : n < m := by omega
|
||||
rw [getElem?_set_ne (by omega), getElem?_append_right, length_take,
|
||||
Nat.min_eq_left (by omega)]
|
||||
· obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h'''
|
||||
have p : n + k + 1 - n = k + 1 := by omega
|
||||
rw [p]
|
||||
rw [getElem?_cons_succ, getElem?_drop]
|
||||
congr 1
|
||||
omega
|
||||
· rw [length_take]
|
||||
exact Nat.le_trans (Nat.min_le_left _ _) (by omega)
|
||||
· rw [set_eq_of_length_le]
|
||||
omega
|
||||
|
||||
theorem exists_of_set {n : Nat} {a' : α} {l : List α} (h : n < l.length) :
|
||||
∃ l₁ l₂, l = l₁ ++ l[n] :: l₂ ∧ l₁.length = n ∧ l.set n a' = l₁ ++ a' :: l₂ := by
|
||||
refine ⟨l.take n, l.drop (n + 1), ⟨by simp, ⟨length_take_of_le (Nat.le_of_lt h), ?_⟩⟩⟩
|
||||
simp [set_eq_take_append_cons_drop, h]
|
||||
|
||||
theorem drop_set_of_lt (a : α) {n m : Nat} (l : List α)
|
||||
(hnm : n < m) : drop m (l.set n a) = l.drop m :=
|
||||
ext_getElem? fun k => by simpa only [getElem?_drop] using getElem?_set_ne (by omega)
|
||||
|
||||
theorem drop_take : ∀ (m n : Nat) (l : List α), drop n (take m l) = take (m - n) (drop n l)
|
||||
| 0, _, _ => by simp
|
||||
| _, 0, _ => by simp
|
||||
| _, _, [] => by simp
|
||||
| m+1, n+1, h :: t => by
|
||||
simp [take_succ_cons, drop_succ_cons, drop_take m n t]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem take_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
xs.reverse.take n = (xs.drop (xs.length - n)).reverse := by
|
||||
induction xs generalizing n <;>
|
||||
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
|
||||
next xs_hd xs_tl xs_ih =>
|
||||
cases Nat.lt_or_eq_of_le h with
|
||||
| inl h' =>
|
||||
have h' := Nat.le_of_succ_le_succ h'
|
||||
rw [take_append_of_le_length, xs_ih h']
|
||||
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
|
||||
· rwa [succ_eq_add_one, Nat.sub_add_comm]
|
||||
· rwa [length_reverse]
|
||||
| inr h' =>
|
||||
subst h'
|
||||
rw [length, Nat.sub_self, drop]
|
||||
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
|
||||
rw [this, take_length, reverse_cons]
|
||||
rw [length_append, length_reverse]
|
||||
rfl
|
||||
|
||||
@[deprecated (since := "2024-06-15")] abbrev reverse_take := @take_reverse
|
||||
|
||||
theorem drop_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
xs.reverse.drop n = (xs.take (xs.length - n)).reverse := by
|
||||
conv =>
|
||||
rhs
|
||||
rw [← reverse_reverse xs]
|
||||
rw [← reverse_reverse xs] at h
|
||||
generalize xs.reverse = xs' at h ⊢
|
||||
rw [take_reverse]
|
||||
· simp only [length_reverse, reverse_reverse] at *
|
||||
congr
|
||||
omega
|
||||
· simp only [length_reverse, sub_le]
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
@[simp] theorem rotateLeft_replicate (n) (a : α) : rotateLeft (replicate m a) n = replicate m a := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
suffices 1 < m → m - (n + 1) % m + min ((n + 1) % m) m = m by
|
||||
simpa [rotateLeft]
|
||||
intro h
|
||||
rw [Nat.min_eq_left (Nat.le_of_lt (Nat.mod_lt _ (by omega)))]
|
||||
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
|
||||
omega
|
||||
|
||||
/-! ### rotateRight -/
|
||||
|
||||
@[simp] theorem rotateRight_replicate (n) (a : α) : rotateRight (replicate m a) n = replicate m a := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
suffices 1 < m → m - (m - (n + 1) % m) + min (m - (n + 1) % m) m = m by
|
||||
simpa [rotateRight]
|
||||
intro h
|
||||
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
|
||||
rw [Nat.min_eq_left (by omega)]
|
||||
omega
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
@[simp] theorem length_zipWith (f : α → β → γ) (l₁ l₂) :
|
||||
length (zipWith f l₁ l₂) = min (length l₁) (length l₂) := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;>
|
||||
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
|
||||
|
||||
theorem lt_length_left_of_zipWith {f : α → β → γ} {i : Nat} {l : List α} {l' : List β}
|
||||
(h : i < (zipWith f l l').length) : i < l.length := by rw [length_zipWith] at h; omega
|
||||
|
||||
theorem lt_length_right_of_zipWith {f : α → β → γ} {i : Nat} {l : List α} {l' : List β}
|
||||
(h : i < (zipWith f l l').length) : i < l'.length := by rw [length_zipWith] at h; omega
|
||||
|
||||
@[simp]
|
||||
theorem getElem_zipWith {f : α → β → γ} {l : List α} {l' : List β}
|
||||
{i : Nat} {h : i < (zipWith f l l').length} :
|
||||
(zipWith f l l')[i] =
|
||||
f (l[i]'(lt_length_left_of_zipWith h))
|
||||
(l'[i]'(lt_length_right_of_zipWith h)) := by
|
||||
rw [← Option.some_inj, ← getElem?_eq_getElem, getElem?_zipWith_eq_some]
|
||||
exact
|
||||
⟨l[i]'(lt_length_left_of_zipWith h), l'[i]'(lt_length_right_of_zipWith h),
|
||||
by rw [getElem?_eq_getElem], by rw [getElem?_eq_getElem]; exact ⟨rfl, rfl⟩⟩
|
||||
|
||||
theorem zipWith_eq_zipWith_take_min : ∀ (l₁ : List α) (l₂ : List β),
|
||||
zipWith f l₁ l₂ = zipWith f (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
|
||||
| [], _ => by simp
|
||||
| _, [] => by simp
|
||||
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zipWith_eq_zipWith_take_min l₁ l₂]
|
||||
|
||||
theorem reverse_zipWith (h : l.length = l'.length) :
|
||||
(zipWith f l l').reverse = zipWith f l.reverse l'.reverse := by
|
||||
induction l generalizing l' with
|
||||
| nil => simp
|
||||
| cons hd tl hl =>
|
||||
cases l' with
|
||||
| nil => simp
|
||||
| cons hd' tl' =>
|
||||
simp only [Nat.add_right_cancel_iff, length] at h
|
||||
have : tl.reverse.length = tl'.reverse.length := by simp [h]
|
||||
simp [hl h, zipWith_append _ _ _ _ _ this]
|
||||
|
||||
@[deprecated reverse_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_reverse := @reverse_zipWith
|
||||
|
||||
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
|
||||
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
|
||||
rw [zipWith_eq_zipWith_take_min]
|
||||
simp
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
@[simp] theorem length_zip (l₁ : List α) (l₂ : List β) :
|
||||
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
|
||||
simp [zip]
|
||||
|
||||
theorem lt_length_left_of_zip {i : Nat} {l : List α} {l' : List β} (h : i < (zip l l').length) :
|
||||
i < l.length :=
|
||||
lt_length_left_of_zipWith h
|
||||
|
||||
theorem lt_length_right_of_zip {i : Nat} {l : List α} {l' : List β} (h : i < (zip l l').length) :
|
||||
i < l'.length :=
|
||||
lt_length_right_of_zipWith h
|
||||
|
||||
@[simp]
|
||||
theorem getElem_zip {l : List α} {l' : List β} {i : Nat} {h : i < (zip l l').length} :
|
||||
(zip l l')[i] =
|
||||
(l[i]'(lt_length_left_of_zip h), l'[i]'(lt_length_right_of_zip h)) :=
|
||||
getElem_zipWith (h := h)
|
||||
|
||||
theorem zip_eq_zip_take_min : ∀ (l₁ : List α) (l₂ : List β),
|
||||
zip l₁ l₂ = zip (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
|
||||
| [], _ => by simp
|
||||
| _, [] => by simp
|
||||
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zip_eq_zip_take_min l₁ l₂]
|
||||
|
||||
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
|
||||
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
|
||||
rw [zip_eq_zip_take_min]
|
||||
simp
|
||||
|
||||
end List
|
||||
269
src/Init/Data/List/Pairwise.lean
Normal file
269
src/Init/Data/List/Pairwise.lean
Normal file
@@ -0,0 +1,269 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Sublist
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.Pairwise` and `List.Nodup`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Pairwise and Nodup -/
|
||||
|
||||
/-! ### Pairwise -/
|
||||
|
||||
theorem Pairwise.sublist : l₁ <+ l₂ → l₂.Pairwise R → l₁.Pairwise R
|
||||
| .slnil, h => h
|
||||
| .cons _ s, .cons _ h₂ => h₂.sublist s
|
||||
| .cons₂ _ s, .cons h₁ h₂ => (h₂.sublist s).cons fun _ h => h₁ _ (s.subset h)
|
||||
|
||||
theorem Pairwise.imp {α R S} (H : ∀ {a b}, R a b → S a b) :
|
||||
∀ {l : List α}, l.Pairwise R → l.Pairwise S
|
||||
| _, .nil => .nil
|
||||
| _, .cons h₁ h₂ => .cons (H ∘ h₁ ·) (h₂.imp H)
|
||||
|
||||
theorem rel_of_pairwise_cons (p : (a :: l).Pairwise R) : ∀ {a'}, a' ∈ l → R a a' :=
|
||||
(pairwise_cons.1 p).1 _
|
||||
|
||||
theorem Pairwise.of_cons (p : (a :: l).Pairwise R) : Pairwise R l :=
|
||||
(pairwise_cons.1 p).2
|
||||
|
||||
theorem Pairwise.tail : ∀ {l : List α} (_p : Pairwise R l), Pairwise R l.tail
|
||||
| [], h => h
|
||||
| _ :: _, h => h.of_cons
|
||||
|
||||
theorem Pairwise.imp_of_mem {S : α → α → Prop}
|
||||
(H : ∀ {a b}, a ∈ l → b ∈ l → R a b → S a b) (p : Pairwise R l) : Pairwise S l := by
|
||||
induction p with
|
||||
| nil => constructor
|
||||
| @cons a l r _ ih =>
|
||||
constructor
|
||||
· exact fun x h => H (mem_cons_self ..) (mem_cons_of_mem _ h) <| r x h
|
||||
· exact ih fun m m' => H (mem_cons_of_mem _ m) (mem_cons_of_mem _ m')
|
||||
|
||||
theorem Pairwise.and (hR : Pairwise R l) (hS : Pairwise S l) :
|
||||
l.Pairwise fun a b => R a b ∧ S a b := by
|
||||
induction hR with
|
||||
| nil => simp only [Pairwise.nil]
|
||||
| cons R1 _ IH =>
|
||||
simp only [Pairwise.nil, pairwise_cons] at hS ⊢
|
||||
exact ⟨fun b bl => ⟨R1 b bl, hS.1 b bl⟩, IH hS.2⟩
|
||||
|
||||
theorem pairwise_and_iff : l.Pairwise (fun a b => R a b ∧ S a b) ↔ Pairwise R l ∧ Pairwise S l :=
|
||||
⟨fun h => ⟨h.imp fun h => h.1, h.imp fun h => h.2⟩, fun ⟨hR, hS⟩ => hR.and hS⟩
|
||||
|
||||
theorem Pairwise.imp₂ (H : ∀ a b, R a b → S a b → T a b)
|
||||
(hR : Pairwise R l) (hS : l.Pairwise S) : l.Pairwise T :=
|
||||
(hR.and hS).imp fun ⟨h₁, h₂⟩ => H _ _ h₁ h₂
|
||||
|
||||
theorem Pairwise.iff_of_mem {S : α → α → Prop} {l : List α}
|
||||
(H : ∀ {a b}, a ∈ l → b ∈ l → (R a b ↔ S a b)) : Pairwise R l ↔ Pairwise S l :=
|
||||
⟨Pairwise.imp_of_mem fun m m' => (H m m').1, Pairwise.imp_of_mem fun m m' => (H m m').2⟩
|
||||
|
||||
theorem Pairwise.iff {S : α → α → Prop} (H : ∀ a b, R a b ↔ S a b) {l : List α} :
|
||||
Pairwise R l ↔ Pairwise S l :=
|
||||
Pairwise.iff_of_mem fun _ _ => H ..
|
||||
|
||||
theorem pairwise_of_forall {l : List α} (H : ∀ x y, R x y) : Pairwise R l := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
theorem Pairwise.and_mem {l : List α} :
|
||||
Pairwise R l ↔ Pairwise (fun x y => x ∈ l ∧ y ∈ l ∧ R x y) l :=
|
||||
Pairwise.iff_of_mem <| by simp (config := { contextual := true })
|
||||
|
||||
theorem Pairwise.imp_mem {l : List α} :
|
||||
Pairwise R l ↔ Pairwise (fun x y => x ∈ l → y ∈ l → R x y) l :=
|
||||
Pairwise.iff_of_mem <| by simp (config := { contextual := true })
|
||||
|
||||
theorem Pairwise.forall_of_forall_of_flip (h₁ : ∀ x ∈ l, R x x) (h₂ : Pairwise R l)
|
||||
(h₃ : l.Pairwise (flip R)) : ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → R x y := by
|
||||
induction l with
|
||||
| nil => exact forall_mem_nil _
|
||||
| cons a l ih =>
|
||||
rw [pairwise_cons] at h₂ h₃
|
||||
simp only [mem_cons]
|
||||
rintro x (rfl | hx) y (rfl | hy)
|
||||
· exact h₁ _ (l.mem_cons_self _)
|
||||
· exact h₂.1 _ hy
|
||||
· exact h₃.1 _ hx
|
||||
· exact ih (fun x hx => h₁ _ <| mem_cons_of_mem _ hx) h₂.2 h₃.2 hx hy
|
||||
|
||||
theorem pairwise_singleton (R) (a : α) : Pairwise R [a] := by simp
|
||||
|
||||
theorem pairwise_pair {a b : α} : Pairwise R [a, b] ↔ R a b := by simp
|
||||
|
||||
theorem pairwise_map {l : List α} :
|
||||
(l.map f).Pairwise R ↔ l.Pairwise fun a b => R (f a) (f b) := by
|
||||
induction l
|
||||
· simp
|
||||
· simp only [map, pairwise_cons, forall_mem_map, *]
|
||||
|
||||
theorem Pairwise.of_map {S : β → β → Prop} (f : α → β) (H : ∀ a b : α, S (f a) (f b) → R a b)
|
||||
(p : Pairwise S (map f l)) : Pairwise R l :=
|
||||
(pairwise_map.1 p).imp (H _ _)
|
||||
|
||||
theorem Pairwise.map {S : β → β → Prop} (f : α → β) (H : ∀ a b : α, R a b → S (f a) (f b))
|
||||
(p : Pairwise R l) : Pairwise S (map f l) :=
|
||||
pairwise_map.2 <| p.imp (H _ _)
|
||||
|
||||
theorem pairwise_filterMap (f : β → Option α) {l : List β} :
|
||||
Pairwise R (filterMap f l) ↔ Pairwise (fun a a' : β => ∀ b ∈ f a, ∀ b' ∈ f a', R b b') l := by
|
||||
let _S (a a' : β) := ∀ b ∈ f a, ∀ b' ∈ f a', R b b'
|
||||
simp only [Option.mem_def]
|
||||
induction l with
|
||||
| nil => simp only [filterMap, Pairwise.nil]
|
||||
| cons a l IH => ?_
|
||||
match e : f a with
|
||||
| none =>
|
||||
rw [filterMap_cons_none e, pairwise_cons]
|
||||
simp only [e, false_implies, implies_true, true_and, IH]
|
||||
| some b =>
|
||||
rw [filterMap_cons_some e]
|
||||
simpa [IH, e] using fun _ =>
|
||||
⟨fun h a ha b hab => h _ _ ha hab, fun h a b ha hab => h _ ha _ hab⟩
|
||||
|
||||
theorem Pairwise.filterMap {S : β → β → Prop} (f : α → Option β)
|
||||
(H : ∀ a a' : α, R a a' → ∀ b ∈ f a, ∀ b' ∈ f a', S b b') {l : List α} (p : Pairwise R l) :
|
||||
Pairwise S (filterMap f l) :=
|
||||
(pairwise_filterMap _).2 <| p.imp (H _ _)
|
||||
|
||||
@[deprecated Pairwise.filterMap (since := "2024-07-29")] abbrev Pairwise.filter_map := @Pairwise.filterMap
|
||||
|
||||
theorem pairwise_filter (p : α → Prop) [DecidablePred p] {l : List α} :
|
||||
Pairwise R (filter p l) ↔ Pairwise (fun x y => p x → p y → R x y) l := by
|
||||
rw [← filterMap_eq_filter, pairwise_filterMap]
|
||||
simp
|
||||
|
||||
theorem Pairwise.filter (p : α → Bool) : Pairwise R l → Pairwise R (filter p l) :=
|
||||
Pairwise.sublist (filter_sublist _)
|
||||
|
||||
theorem pairwise_append {l₁ l₂ : List α} :
|
||||
(l₁ ++ l₂).Pairwise R ↔ l₁.Pairwise R ∧ l₂.Pairwise R ∧ ∀ a ∈ l₁, ∀ b ∈ l₂, R a b := by
|
||||
induction l₁ <;> simp [*, or_imp, forall_and, and_assoc, and_left_comm]
|
||||
|
||||
theorem pairwise_append_comm {R : α → α → Prop} (s : ∀ {x y}, R x y → R y x) {l₁ l₂ : List α} :
|
||||
Pairwise R (l₁ ++ l₂) ↔ Pairwise R (l₂ ++ l₁) := by
|
||||
have (l₁ l₂ : List α) (H : ∀ x : α, x ∈ l₁ → ∀ y : α, y ∈ l₂ → R x y)
|
||||
(x : α) (xm : x ∈ l₂) (y : α) (ym : y ∈ l₁) : R x y := s (H y ym x xm)
|
||||
simp only [pairwise_append, and_left_comm]; rw [Iff.intro (this l₁ l₂) (this l₂ l₁)]
|
||||
|
||||
theorem pairwise_middle {R : α → α → Prop} (s : ∀ {x y}, R x y → R y x) {a : α} {l₁ l₂ : List α} :
|
||||
Pairwise R (l₁ ++ a :: l₂) ↔ Pairwise R (a :: (l₁ ++ l₂)) := by
|
||||
show Pairwise R (l₁ ++ ([a] ++ l₂)) ↔ Pairwise R ([a] ++ l₁ ++ l₂)
|
||||
rw [← append_assoc, pairwise_append, @pairwise_append _ _ ([a] ++ l₁), pairwise_append_comm s]
|
||||
simp only [mem_append, or_comm]
|
||||
|
||||
theorem pairwise_join {L : List (List α)} :
|
||||
Pairwise R (join L) ↔
|
||||
(∀ l ∈ L, Pairwise R l) ∧ Pairwise (fun l₁ l₂ => ∀ x ∈ l₁, ∀ y ∈ l₂, R x y) L := by
|
||||
induction L with
|
||||
| nil => simp
|
||||
| cons l L IH =>
|
||||
simp only [join, pairwise_append, IH, mem_join, exists_imp, and_imp, forall_mem_cons,
|
||||
pairwise_cons, and_assoc, and_congr_right_iff]
|
||||
rw [and_comm, and_congr_left_iff]
|
||||
intros; exact ⟨fun h a b c d e => h c d e a b, fun h c d e a b => h a b c d e⟩
|
||||
|
||||
theorem pairwise_bind {R : β → β → Prop} {l : List α} {f : α → List β} :
|
||||
List.Pairwise R (l.bind f) ↔
|
||||
(∀ a ∈ l, Pairwise R (f a)) ∧ Pairwise (fun a₁ a₂ => ∀ x ∈ f a₁, ∀ y ∈ f a₂, R x y) l := by
|
||||
simp [List.bind, pairwise_join, pairwise_map]
|
||||
|
||||
theorem pairwise_reverse {l : List α} :
|
||||
l.reverse.Pairwise R ↔ l.Pairwise (fun a b => R b a) := by
|
||||
induction l <;> simp [*, pairwise_append, and_comm]
|
||||
|
||||
@[simp] theorem pairwise_replicate {n : Nat} {a : α} :
|
||||
(replicate n a).Pairwise R ↔ n ≤ 1 ∨ R a a := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate_succ, pairwise_cons, mem_replicate, ne_eq, and_imp,
|
||||
forall_eq_apply_imp_iff, ih]
|
||||
constructor
|
||||
· rintro ⟨h, h' | h'⟩
|
||||
· by_cases w : n = 0
|
||||
· left
|
||||
subst w
|
||||
simp
|
||||
· right
|
||||
exact h w
|
||||
· right
|
||||
exact h'
|
||||
· rintro (h | h)
|
||||
· obtain rfl := eq_zero_of_le_zero (le_of_lt_succ h)
|
||||
simp
|
||||
· exact ⟨fun _ => h, Or.inr h⟩
|
||||
|
||||
theorem Pairwise.drop {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.drop n) :=
|
||||
h.sublist (drop_sublist _ _)
|
||||
|
||||
theorem Pairwise.take {l : List α} {n : Nat} (h : List.Pairwise R l) : List.Pairwise R (l.take n) :=
|
||||
h.sublist (take_sublist _ _)
|
||||
|
||||
theorem pairwise_iff_forall_sublist : l.Pairwise R ↔ (∀ {a b}, [a,b] <+ l → R a b) := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons hd tl IH =>
|
||||
rw [List.pairwise_cons]
|
||||
constructor <;> intro h
|
||||
· intro
|
||||
| a, b, .cons _ hab => exact IH.mp h.2 hab
|
||||
| _, b, .cons₂ _ hab => refine h.1 _ (hab.subset ?_); simp
|
||||
· constructor
|
||||
· intro x hx
|
||||
apply h
|
||||
rw [List.cons_sublist_cons, List.singleton_sublist]
|
||||
exact hx
|
||||
· apply IH.mpr
|
||||
intro a b hab
|
||||
apply h; exact hab.cons _
|
||||
|
||||
/-! ### Nodup -/
|
||||
|
||||
@[simp]
|
||||
theorem nodup_nil : @Nodup α [] :=
|
||||
Pairwise.nil
|
||||
|
||||
@[simp]
|
||||
theorem nodup_cons {a : α} {l : List α} : Nodup (a :: l) ↔ a ∉ l ∧ Nodup l := by
|
||||
simp only [Nodup, pairwise_cons, forall_mem_ne]
|
||||
|
||||
theorem Nodup.sublist : l₁ <+ l₂ → Nodup l₂ → Nodup l₁ :=
|
||||
Pairwise.sublist
|
||||
|
||||
theorem Sublist.nodup : l₁ <+ l₂ → Nodup l₂ → Nodup l₁ :=
|
||||
Nodup.sublist
|
||||
|
||||
theorem getElem?_inj {xs : List α}
|
||||
(h₀ : i < xs.length) (h₁ : Nodup xs) (h₂ : xs[i]? = xs[j]?) : i = j := by
|
||||
induction xs generalizing i j with
|
||||
| nil => cases h₀
|
||||
| cons x xs ih =>
|
||||
match i, j with
|
||||
| 0, 0 => rfl
|
||||
| i+1, j+1 =>
|
||||
cases h₁ with
|
||||
| cons ha h₁ =>
|
||||
simp only [getElem?_cons_succ] at h₂
|
||||
exact congrArg (· + 1) (ih (Nat.lt_of_succ_lt_succ h₀) h₁ h₂)
|
||||
| i+1, 0 => ?_
|
||||
| 0, j+1 => ?_
|
||||
all_goals
|
||||
simp only [get?_eq_getElem?, getElem?_cons_zero, getElem?_cons_succ] at h₂
|
||||
cases h₁; rename_i h' h
|
||||
have := h x ?_ rfl; cases this
|
||||
rw [mem_iff_get?]
|
||||
simp only [get?_eq_getElem?]
|
||||
exact ⟨_, h₂⟩; exact ⟨_ , h₂.symm⟩
|
||||
|
||||
@[simp] theorem nodup_replicate {n : Nat} {a : α} :
|
||||
(replicate n a).Nodup ↔ n ≤ 1 := by simp [Nodup]
|
||||
|
||||
end List
|
||||
754
src/Init/Data/List/Sublist.lean
Normal file
754
src/Init/Data/List/Sublist.lean
Normal file
@@ -0,0 +1,754 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.TakeDrop
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.Subset`, `List.Sublist`, `List.IsPrefix`, `List.IsSuffix`, and `List.IsInfix`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### isPrefixOf -/
|
||||
section isPrefixOf
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem isPrefixOf_cons₂_self [LawfulBEq α] {a : α} :
|
||||
isPrefixOf (a::as) (a::bs) = isPrefixOf as bs := by simp [isPrefixOf_cons₂]
|
||||
|
||||
@[simp] theorem isPrefixOf_length_pos_nil {L : List α} (h : 0 < L.length) : isPrefixOf L [] = false := by
|
||||
cases L <;> simp_all [isPrefixOf]
|
||||
|
||||
@[simp] theorem isPrefixOf_replicate {a : α} :
|
||||
isPrefixOf l (replicate n a) = (decide (l.length ≤ n) && l.all (· == a)) := by
|
||||
induction l generalizing n with
|
||||
| nil => simp
|
||||
| cons h t ih =>
|
||||
cases n
|
||||
· simp
|
||||
· simp [replicate_succ, isPrefixOf_cons₂, ih, Nat.succ_le_succ_iff, Bool.and_left_comm]
|
||||
|
||||
end isPrefixOf
|
||||
|
||||
/-! ### isSuffixOf -/
|
||||
section isSuffixOf
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem isSuffixOf_cons_nil : isSuffixOf (a::as) ([] : List α) = false := by
|
||||
simp [isSuffixOf]
|
||||
|
||||
@[simp] theorem isSuffixOf_replicate {a : α} :
|
||||
isSuffixOf l (replicate n a) = (decide (l.length ≤ n) && l.all (· == a)) := by
|
||||
simp [isSuffixOf, all_eq]
|
||||
|
||||
end isSuffixOf
|
||||
|
||||
/-! ### Subset -/
|
||||
|
||||
/-! ### List subset -/
|
||||
|
||||
theorem subset_def {l₁ l₂ : List α} : l₁ ⊆ l₂ ↔ ∀ {a : α}, a ∈ l₁ → a ∈ l₂ := .rfl
|
||||
|
||||
@[simp] theorem nil_subset (l : List α) : [] ⊆ l := nofun
|
||||
|
||||
@[simp] theorem Subset.refl (l : List α) : l ⊆ l := fun _ i => i
|
||||
|
||||
theorem Subset.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ ⊆ l₂) (h₂ : l₂ ⊆ l₃) : l₁ ⊆ l₃ :=
|
||||
fun _ i => h₂ (h₁ i)
|
||||
|
||||
instance : Trans (Membership.mem : α → List α → Prop) Subset Membership.mem :=
|
||||
⟨fun h₁ h₂ => h₂ h₁⟩
|
||||
|
||||
instance : Trans (Subset : List α → List α → Prop) Subset Subset :=
|
||||
⟨Subset.trans⟩
|
||||
|
||||
@[simp] theorem subset_cons_self (a : α) (l : List α) : l ⊆ a :: l := fun _ => Mem.tail _
|
||||
|
||||
theorem subset_of_cons_subset {a : α} {l₁ l₂ : List α} : a :: l₁ ⊆ l₂ → l₁ ⊆ l₂ :=
|
||||
fun s _ i => s (mem_cons_of_mem _ i)
|
||||
|
||||
theorem subset_cons_of_subset (a : α) {l₁ l₂ : List α} : l₁ ⊆ l₂ → l₁ ⊆ a :: l₂ :=
|
||||
fun s _ i => .tail _ (s i)
|
||||
|
||||
theorem cons_subset_cons {l₁ l₂ : List α} (a : α) (s : l₁ ⊆ l₂) : a :: l₁ ⊆ a :: l₂ :=
|
||||
fun _ => by simp only [mem_cons]; exact Or.imp_right (@s _)
|
||||
|
||||
@[simp] theorem cons_subset : a :: l ⊆ m ↔ a ∈ m ∧ l ⊆ m := by
|
||||
simp only [subset_def, mem_cons, or_imp, forall_and, forall_eq]
|
||||
|
||||
@[simp] theorem subset_nil {l : List α} : l ⊆ [] ↔ l = [] :=
|
||||
⟨fun h => match l with | [] => rfl | _::_ => (nomatch h (.head ..)), fun | rfl => Subset.refl _⟩
|
||||
|
||||
theorem map_subset {l₁ l₂ : List α} {f : α → β} (h : l₁ ⊆ l₂) : map f l₁ ⊆ map f l₂ :=
|
||||
fun x => by simp only [mem_map]; exact .imp fun a => .imp_left (@h _)
|
||||
|
||||
theorem filter_subset {l₁ l₂ : List α} (p : α → Bool) (H : l₁ ⊆ l₂) : filter p l₁ ⊆ filter p l₂ :=
|
||||
fun x => by simp_all [mem_filter, subset_def.1 H]
|
||||
|
||||
theorem filterMap_subset {l₁ l₂ : List α} (f : α → Option β) (H : l₁ ⊆ l₂) :
|
||||
filterMap f l₁ ⊆ filterMap f l₂ := by
|
||||
intro x
|
||||
simp only [mem_filterMap]
|
||||
rintro ⟨a, h, w⟩
|
||||
exact ⟨a, H h, w⟩
|
||||
|
||||
@[simp] theorem subset_append_left (l₁ l₂ : List α) : l₁ ⊆ l₁ ++ l₂ := fun _ => mem_append_left _
|
||||
|
||||
@[simp] theorem subset_append_right (l₁ l₂ : List α) : l₂ ⊆ l₁ ++ l₂ := fun _ => mem_append_right _
|
||||
|
||||
theorem subset_append_of_subset_left (l₂ : List α) : l ⊆ l₁ → l ⊆ l₁ ++ l₂ :=
|
||||
fun s => Subset.trans s <| subset_append_left _ _
|
||||
|
||||
theorem subset_append_of_subset_right (l₁ : List α) : l ⊆ l₂ → l ⊆ l₁ ++ l₂ :=
|
||||
fun s => Subset.trans s <| subset_append_right _ _
|
||||
|
||||
@[simp] theorem append_subset {l₁ l₂ l : List α} :
|
||||
l₁ ++ l₂ ⊆ l ↔ l₁ ⊆ l ∧ l₂ ⊆ l := by simp [subset_def, or_imp, forall_and]
|
||||
|
||||
theorem replicate_subset {n : Nat} {a : α} {l : List α} : replicate n a ⊆ l ↔ n = 0 ∨ a ∈ l := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih => simp (config := {contextual := true}) [replicate_succ, ih, cons_subset]
|
||||
|
||||
theorem subset_replicate {n : Nat} {a : α} {l : List α} (h : n ≠ 0) : l ⊆ replicate n a ↔ ∀ x ∈ l, x = a := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [cons_subset, mem_replicate, ne_eq, ih, mem_cons, forall_eq_or_imp,
|
||||
and_congr_left_iff, and_iff_right_iff_imp]
|
||||
solve_by_elim
|
||||
|
||||
@[simp] theorem reverse_subset {l₁ l₂ : List α} : reverse l₁ ⊆ l₂ ↔ l₁ ⊆ l₂ := by
|
||||
simp [subset_def]
|
||||
|
||||
@[simp] theorem subset_reverse {l₁ l₂ : List α} : l₁ ⊆ reverse l₂ ↔ l₁ ⊆ l₂ := by
|
||||
simp [subset_def]
|
||||
|
||||
/-! ### Sublist and isSublist -/
|
||||
|
||||
@[simp] theorem nil_sublist : ∀ l : List α, [] <+ l
|
||||
| [] => .slnil
|
||||
| a :: l => (nil_sublist l).cons a
|
||||
|
||||
@[simp] theorem Sublist.refl : ∀ l : List α, l <+ l
|
||||
| [] => .slnil
|
||||
| a :: l => (Sublist.refl l).cons₂ a
|
||||
|
||||
theorem Sublist.trans {l₁ l₂ l₃ : List α} (h₁ : l₁ <+ l₂) (h₂ : l₂ <+ l₃) : l₁ <+ l₃ := by
|
||||
induction h₂ generalizing l₁ with
|
||||
| slnil => exact h₁
|
||||
| cons _ _ IH => exact (IH h₁).cons _
|
||||
| @cons₂ l₂ _ a _ IH =>
|
||||
generalize e : a :: l₂ = l₂'
|
||||
match e ▸ h₁ with
|
||||
| .slnil => apply nil_sublist
|
||||
| .cons a' h₁' => cases e; apply (IH h₁').cons
|
||||
| .cons₂ a' h₁' => cases e; apply (IH h₁').cons₂
|
||||
|
||||
instance : Trans (@Sublist α) Sublist Sublist := ⟨Sublist.trans⟩
|
||||
|
||||
@[simp] theorem sublist_cons_self (a : α) (l : List α) : l <+ a :: l := (Sublist.refl l).cons _
|
||||
|
||||
theorem sublist_of_cons_sublist : a :: l₁ <+ l₂ → l₁ <+ l₂ :=
|
||||
(sublist_cons_self a l₁).trans
|
||||
|
||||
@[simp]
|
||||
theorem cons_sublist_cons : a :: l₁ <+ a :: l₂ ↔ l₁ <+ l₂ :=
|
||||
⟨fun | .cons _ s => sublist_of_cons_sublist s | .cons₂ _ s => s, .cons₂ _⟩
|
||||
|
||||
theorem sublist_or_mem_of_sublist (h : l <+ l₁ ++ a :: l₂) : l <+ l₁ ++ l₂ ∨ a ∈ l := by
|
||||
induction l₁ generalizing l with
|
||||
| nil => match h with
|
||||
| .cons _ h => exact .inl h
|
||||
| .cons₂ _ h => exact .inr (.head ..)
|
||||
| cons b l₁ IH =>
|
||||
match h with
|
||||
| .cons _ h => exact (IH h).imp_left (Sublist.cons _)
|
||||
| .cons₂ _ h => exact (IH h).imp (Sublist.cons₂ _) (.tail _)
|
||||
|
||||
theorem Sublist.subset : l₁ <+ l₂ → l₁ ⊆ l₂
|
||||
| .slnil, _, h => h
|
||||
| .cons _ s, _, h => .tail _ (s.subset h)
|
||||
| .cons₂ .., _, .head .. => .head ..
|
||||
| .cons₂ _ s, _, .tail _ h => .tail _ (s.subset h)
|
||||
|
||||
instance : Trans (@Sublist α) Subset Subset :=
|
||||
⟨fun h₁ h₂ => trans h₁.subset h₂⟩
|
||||
|
||||
instance : Trans Subset (@Sublist α) Subset :=
|
||||
⟨fun h₁ h₂ => trans h₁ h₂.subset⟩
|
||||
|
||||
instance : Trans (Membership.mem : α → List α → Prop) Sublist Membership.mem :=
|
||||
⟨fun h₁ h₂ => h₂.subset h₁⟩
|
||||
|
||||
theorem mem_of_cons_sublist {a : α} {l₁ l₂ : List α} (s : a :: l₁ <+ l₂) : a ∈ l₂ :=
|
||||
(cons_subset.1 s.subset).1
|
||||
|
||||
@[simp] theorem sublist_nil {l : List α} : l <+ [] ↔ l = [] :=
|
||||
⟨fun s => subset_nil.1 s.subset, fun H => H ▸ Sublist.refl _⟩
|
||||
|
||||
theorem Sublist.length_le : l₁ <+ l₂ → length l₁ ≤ length l₂
|
||||
| .slnil => Nat.le_refl 0
|
||||
| .cons _l s => le_succ_of_le (length_le s)
|
||||
| .cons₂ _ s => succ_le_succ (length_le s)
|
||||
|
||||
theorem Sublist.eq_of_length : l₁ <+ l₂ → length l₁ = length l₂ → l₁ = l₂
|
||||
| .slnil, _ => rfl
|
||||
| .cons a s, h => nomatch Nat.not_lt.2 s.length_le (h ▸ lt_succ_self _)
|
||||
| .cons₂ a s, h => by rw [s.eq_of_length (succ.inj h)]
|
||||
|
||||
theorem Sublist.eq_of_length_le (s : l₁ <+ l₂) (h : length l₂ ≤ length l₁) : l₁ = l₂ :=
|
||||
s.eq_of_length <| Nat.le_antisymm s.length_le h
|
||||
|
||||
theorem Sublist.length_eq (s : l₁ <+ l₂) : length l₁ = length l₂ ↔ l₁ = l₂ :=
|
||||
⟨s.eq_of_length, congrArg _⟩
|
||||
|
||||
protected theorem Sublist.map (f : α → β) {l₁ l₂} (s : l₁ <+ l₂) : map f l₁ <+ map f l₂ := by
|
||||
induction s with
|
||||
| slnil => simp
|
||||
| cons a s ih =>
|
||||
simpa using cons (f a) ih
|
||||
| cons₂ a s ih =>
|
||||
simpa using cons₂ (f a) ih
|
||||
|
||||
protected theorem Sublist.filterMap (f : α → Option β) (s : l₁ <+ l₂) :
|
||||
filterMap f l₁ <+ filterMap f l₂ := by
|
||||
induction s <;> simp [filterMap_cons] <;> split <;> simp [*, cons, cons₂]
|
||||
|
||||
protected theorem Sublist.filter (p : α → Bool) {l₁ l₂} (s : l₁ <+ l₂) : filter p l₁ <+ filter p l₂ := by
|
||||
rw [← filterMap_eq_filter]; apply s.filterMap
|
||||
|
||||
theorem sublist_filterMap_iff {l₁ : List β} {f : α → Option β} :
|
||||
l₁ <+ l₂.filterMap f ↔ ∃ l', l' <+ l₂ ∧ l₁ = l'.filterMap f := by
|
||||
induction l₂ generalizing l₁ with
|
||||
| nil => simp
|
||||
| cons a l₂ ih =>
|
||||
simp only [filterMap_cons]
|
||||
split
|
||||
· simp only [ih]
|
||||
constructor
|
||||
· rintro ⟨l', h, rfl⟩
|
||||
exact ⟨l', Sublist.cons a h, rfl⟩
|
||||
· rintro ⟨l', h, rfl⟩
|
||||
cases h with
|
||||
| cons _ h =>
|
||||
exact ⟨l', h, rfl⟩
|
||||
| cons₂ _ h =>
|
||||
rename_i l'
|
||||
exact ⟨l', h, by simp_all⟩
|
||||
· constructor
|
||||
· intro w
|
||||
cases w with
|
||||
| cons _ h =>
|
||||
obtain ⟨l', s, rfl⟩ := ih.1 h
|
||||
exact ⟨l', Sublist.cons a s, rfl⟩
|
||||
| cons₂ _ h =>
|
||||
rename_i l'
|
||||
obtain ⟨l', s, rfl⟩ := ih.1 h
|
||||
refine ⟨a :: l', Sublist.cons₂ a s, ?_⟩
|
||||
rwa [filterMap_cons_some]
|
||||
· rintro ⟨l', h, rfl⟩
|
||||
replace h := h.filterMap f
|
||||
rwa [filterMap_cons_some] at h
|
||||
assumption
|
||||
|
||||
theorem sublist_map_iff {l₁ : List β} {f : α → β} :
|
||||
l₁ <+ l₂.map f ↔ ∃ l', l' <+ l₂ ∧ l₁ = l'.map f := by
|
||||
simp only [← filterMap_eq_map, sublist_filterMap_iff]
|
||||
|
||||
theorem sublist_filter_iff {l₁ : List α} {p : α → Bool} :
|
||||
l₁ <+ l₂.filter p ↔ ∃ l', l' <+ l₂ ∧ l₁ = l'.filter p := by
|
||||
simp only [← filterMap_eq_filter, sublist_filterMap_iff]
|
||||
|
||||
@[simp] theorem sublist_append_left : ∀ l₁ l₂ : List α, l₁ <+ l₁ ++ l₂
|
||||
| [], _ => nil_sublist _
|
||||
| _ :: l₁, l₂ => (sublist_append_left l₁ l₂).cons₂ _
|
||||
|
||||
@[simp] theorem sublist_append_right : ∀ l₁ l₂ : List α, l₂ <+ l₁ ++ l₂
|
||||
| [], _ => Sublist.refl _
|
||||
| _ :: l₁, l₂ => (sublist_append_right l₁ l₂).cons _
|
||||
|
||||
@[simp] theorem singleton_sublist {a : α} {l} : [a] <+ l ↔ a ∈ l := by
|
||||
refine ⟨fun h => h.subset (mem_singleton_self _), fun h => ?_⟩
|
||||
obtain ⟨_, _, rfl⟩ := append_of_mem h
|
||||
exact ((nil_sublist _).cons₂ _).trans (sublist_append_right ..)
|
||||
|
||||
theorem sublist_append_of_sublist_left (s : l <+ l₁) : l <+ l₁ ++ l₂ :=
|
||||
s.trans <| sublist_append_left ..
|
||||
|
||||
theorem sublist_append_of_sublist_right (s : l <+ l₂) : l <+ l₁ ++ l₂ :=
|
||||
s.trans <| sublist_append_right ..
|
||||
|
||||
@[simp] theorem append_sublist_append_left : ∀ l, l ++ l₁ <+ l ++ l₂ ↔ l₁ <+ l₂
|
||||
| [] => Iff.rfl
|
||||
| _ :: l => cons_sublist_cons.trans (append_sublist_append_left l)
|
||||
|
||||
theorem Sublist.append_left : l₁ <+ l₂ → ∀ l, l ++ l₁ <+ l ++ l₂ :=
|
||||
fun h l => (append_sublist_append_left l).mpr h
|
||||
|
||||
theorem Sublist.append_right : l₁ <+ l₂ → ∀ l, l₁ ++ l <+ l₂ ++ l
|
||||
| .slnil, _ => Sublist.refl _
|
||||
| .cons _ h, _ => (h.append_right _).cons _
|
||||
| .cons₂ _ h, _ => (h.append_right _).cons₂ _
|
||||
|
||||
theorem Sublist.append (hl : l₁ <+ l₂) (hr : r₁ <+ r₂) : l₁ ++ r₁ <+ l₂ ++ r₂ :=
|
||||
(hl.append_right _).trans ((append_sublist_append_left _).2 hr)
|
||||
|
||||
theorem sublist_cons_iff {a : α} {l l'} :
|
||||
l <+ a :: l' ↔ l <+ l' ∨ ∃ r, l = a :: r ∧ r <+ l' := by
|
||||
constructor
|
||||
· intro h
|
||||
cases h with
|
||||
| cons _ h => exact Or.inl h
|
||||
| cons₂ _ h => exact Or.inr ⟨_, rfl, h⟩
|
||||
· rintro (h | ⟨r, rfl, h⟩)
|
||||
· exact h.cons _
|
||||
· exact h.cons₂ _
|
||||
|
||||
theorem cons_sublist_iff {a : α} {l l'} :
|
||||
a :: l <+ l' ↔ ∃ r₁ r₂, l' = r₁ ++ r₂ ∧ a ∈ r₁ ∧ l <+ r₂ := by
|
||||
induction l' with
|
||||
| nil => simp
|
||||
| cons a' l' ih =>
|
||||
constructor
|
||||
· intro w
|
||||
cases w with
|
||||
| cons _ w =>
|
||||
obtain ⟨r₁, r₂, rfl, h₁, h₂⟩ := ih.1 w
|
||||
exact ⟨a' :: r₁, r₂, by simp, mem_cons_of_mem a' h₁, h₂⟩
|
||||
| cons₂ _ w =>
|
||||
exact ⟨[a], l', by simp, mem_singleton_self _, w⟩
|
||||
· rintro ⟨r₁, r₂, w, h₁, h₂⟩
|
||||
rw [w, ← singleton_append]
|
||||
exact Sublist.append (by simpa) h₂
|
||||
|
||||
theorem sublist_append_iff {l : List α} :
|
||||
l <+ r₁ ++ r₂ ↔ ∃ l₁ l₂, l = l₁ ++ l₂ ∧ l₁ <+ r₁ ∧ l₂ <+ r₂ := by
|
||||
induction r₁ generalizing l with
|
||||
| nil =>
|
||||
constructor
|
||||
· intro w
|
||||
refine ⟨[], l, by simp_all⟩
|
||||
· rintro ⟨l₁, l₂, rfl, w₁, w₂⟩
|
||||
simp_all
|
||||
| cons r r₁ ih =>
|
||||
constructor
|
||||
· intro w
|
||||
simp only [cons_append] at w
|
||||
cases w with
|
||||
| cons _ w =>
|
||||
obtain ⟨l₁, l₂, rfl, w₁, w₂⟩ := ih.1 w
|
||||
exact ⟨l₁, l₂, rfl, Sublist.cons r w₁, w₂⟩
|
||||
| cons₂ _ w =>
|
||||
rename_i l
|
||||
obtain ⟨l₁, l₂, rfl, w₁, w₂⟩ := ih.1 w
|
||||
refine ⟨r :: l₁, l₂, by simp, cons_sublist_cons.mpr w₁, w₂⟩
|
||||
· rintro ⟨l₁, l₂, rfl, w₁, w₂⟩
|
||||
cases w₁ with
|
||||
| cons _ w₁ =>
|
||||
exact Sublist.cons _ (Sublist.append w₁ w₂)
|
||||
| cons₂ _ w₁ =>
|
||||
rename_i l
|
||||
exact Sublist.cons₂ _ (Sublist.append w₁ w₂)
|
||||
|
||||
theorem append_sublist_iff {l₁ l₂ : List α} :
|
||||
l₁ ++ l₂ <+ r ↔ ∃ r₁ r₂, r = r₁ ++ r₂ ∧ l₁ <+ r₁ ∧ l₂ <+ r₂ := by
|
||||
induction l₁ generalizing r with
|
||||
| nil =>
|
||||
constructor
|
||||
· intro w
|
||||
refine ⟨[], r, by simp_all⟩
|
||||
· rintro ⟨r₁, r₂, rfl, -, w₂⟩
|
||||
simp only [nil_append]
|
||||
exact sublist_append_of_sublist_right w₂
|
||||
| cons a l₁ ih =>
|
||||
constructor
|
||||
· rw [cons_append, cons_sublist_iff]
|
||||
rintro ⟨r₁, r₂, rfl, h₁, h₂⟩
|
||||
obtain ⟨s₁, s₂, rfl, t₁, t₂⟩ := ih.1 h₂
|
||||
refine ⟨r₁ ++ s₁, s₂, by simp, ?_, t₂⟩
|
||||
rw [← singleton_append]
|
||||
exact Sublist.append (by simpa) t₁
|
||||
· rintro ⟨r₁, r₂, rfl, h₁, h₂⟩
|
||||
exact Sublist.append h₁ h₂
|
||||
|
||||
theorem Sublist.reverse : l₁ <+ l₂ → l₁.reverse <+ l₂.reverse
|
||||
| .slnil => Sublist.refl _
|
||||
| .cons _ h => by rw [reverse_cons]; exact sublist_append_of_sublist_left h.reverse
|
||||
| .cons₂ _ h => by rw [reverse_cons, reverse_cons]; exact h.reverse.append_right _
|
||||
|
||||
@[simp] theorem reverse_sublist : l₁.reverse <+ l₂.reverse ↔ l₁ <+ l₂ :=
|
||||
⟨fun h => l₁.reverse_reverse ▸ l₂.reverse_reverse ▸ h.reverse, Sublist.reverse⟩
|
||||
|
||||
theorem sublist_reverse_iff : l₁ <+ l₂.reverse ↔ l₁.reverse <+ l₂ :=
|
||||
by rw [← reverse_sublist, reverse_reverse]
|
||||
|
||||
@[simp] theorem append_sublist_append_right (l) : l₁ ++ l <+ l₂ ++ l ↔ l₁ <+ l₂ :=
|
||||
⟨fun h => by
|
||||
have := h.reverse
|
||||
simp only [reverse_append, append_sublist_append_left, reverse_sublist] at this
|
||||
exact this,
|
||||
fun h => h.append_right l⟩
|
||||
|
||||
@[simp] theorem replicate_sublist_replicate {m n} (a : α) :
|
||||
replicate m a <+ replicate n a ↔ m ≤ n := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· have := h.length_le; simp only [length_replicate] at this ⊢; exact this
|
||||
· induction h with
|
||||
| refl => apply Sublist.refl
|
||||
| step => simp [*, replicate, Sublist.cons]
|
||||
|
||||
theorem sublist_replicate_iff : l <+ replicate m a ↔ ∃ n, n ≤ m ∧ l = replicate n a := by
|
||||
induction l generalizing m with
|
||||
| nil =>
|
||||
simp only [nil_sublist, true_iff]
|
||||
exact ⟨0, zero_le m, by simp⟩
|
||||
| cons b l ih =>
|
||||
constructor
|
||||
· intro w
|
||||
cases m with
|
||||
| zero => simp at w
|
||||
| succ m =>
|
||||
simp [replicate_succ] at w
|
||||
cases w with
|
||||
| cons _ w =>
|
||||
obtain ⟨n, le, rfl⟩ := ih.1 (sublist_of_cons_sublist w)
|
||||
obtain rfl := (mem_replicate.1 (mem_of_cons_sublist w)).2
|
||||
exact ⟨n+1, Nat.add_le_add_right le 1, rfl⟩
|
||||
| cons₂ _ w =>
|
||||
obtain ⟨n, le, rfl⟩ := ih.1 w
|
||||
refine ⟨n+1, Nat.add_le_add_right le 1, by simp [replicate_succ]⟩
|
||||
· rintro ⟨n, le, w⟩
|
||||
rw [w]
|
||||
exact (replicate_sublist_replicate a).2 le
|
||||
|
||||
theorem sublist_join_of_mem {L : List (List α)} {l} (h : l ∈ L) : l <+ L.join := by
|
||||
induction L with
|
||||
| nil => cases h
|
||||
| cons l' L ih =>
|
||||
rcases mem_cons.1 h with (rfl | h)
|
||||
· simp [h]
|
||||
· simp [ih h, join_cons, sublist_append_of_sublist_right]
|
||||
|
||||
theorem sublist_join_iff {L : List (List α)} {l} :
|
||||
l <+ L.join ↔
|
||||
∃ L' : List (List α), l = L'.join ∧ ∀ i (_ : i < L'.length), L'[i] <+ L[i]?.getD [] := by
|
||||
induction L generalizing l with
|
||||
| nil =>
|
||||
constructor
|
||||
· intro w
|
||||
simp only [join_nil, sublist_nil] at w
|
||||
subst w
|
||||
exact ⟨[], by simp, fun i x => by cases x⟩
|
||||
· rintro ⟨L', rfl, h⟩
|
||||
simp only [join_nil, sublist_nil, join_eq_nil_iff]
|
||||
simp only [getElem?_nil, Option.getD_none, sublist_nil] at h
|
||||
exact (forall_getElem L' (· = [])).1 h
|
||||
| cons l' L ih =>
|
||||
simp only [join_cons, sublist_append_iff, ih]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, rfl, s, L', rfl, h⟩
|
||||
refine ⟨l₁ :: L', by simp, ?_⟩
|
||||
intro i lt
|
||||
cases i <;> simp_all
|
||||
· rintro ⟨L', rfl, h⟩
|
||||
cases L' with
|
||||
| nil =>
|
||||
exact ⟨[], [], by simp, by simp, [], by simp, fun i x => by cases x⟩
|
||||
| cons l₁ L' =>
|
||||
exact ⟨l₁, L'.join, by simp, by simpa using h 0 (by simp), L', rfl,
|
||||
fun i lt => by simpa using h (i+1) (Nat.add_lt_add_right lt 1)⟩
|
||||
|
||||
theorem join_sublist_iff {L : List (List α)} {l} :
|
||||
L.join <+ l ↔
|
||||
∃ L' : List (List α), l = L'.join ∧ ∀ i (_ : i < L.length), L[i] <+ L'[i]?.getD [] := by
|
||||
induction L generalizing l with
|
||||
| nil =>
|
||||
constructor
|
||||
· intro _
|
||||
exact ⟨[l], by simp, fun i x => by cases x⟩
|
||||
· rintro ⟨L', rfl, _⟩
|
||||
simp only [join_nil, nil_sublist]
|
||||
| cons l' L ih =>
|
||||
simp only [join_cons, append_sublist_iff, ih]
|
||||
constructor
|
||||
· rintro ⟨l₁, l₂, rfl, s, L', rfl, h⟩
|
||||
refine ⟨l₁ :: L', by simp, ?_⟩
|
||||
intro i lt
|
||||
cases i <;> simp_all
|
||||
· rintro ⟨L', rfl, h⟩
|
||||
cases L' with
|
||||
| nil =>
|
||||
exact ⟨[], [], by simp, by simpa using h 0 (by simp), [], by simp,
|
||||
fun i x => by simpa using h (i+1) (Nat.add_lt_add_right x 1)⟩
|
||||
| cons l₁ L' =>
|
||||
exact ⟨l₁, L'.join, by simp, by simpa using h 0 (by simp), L', rfl,
|
||||
fun i lt => by simpa using h (i+1) (Nat.add_lt_add_right lt 1)⟩
|
||||
|
||||
@[simp] theorem isSublist_iff_sublist [BEq α] [LawfulBEq α] {l₁ l₂ : List α} :
|
||||
l₁.isSublist l₂ ↔ l₁ <+ l₂ := by
|
||||
cases l₁ <;> cases l₂ <;> simp [isSublist]
|
||||
case cons.cons hd₁ tl₁ hd₂ tl₂ =>
|
||||
if h_eq : hd₁ = hd₂ then
|
||||
simp [h_eq, cons_sublist_cons, isSublist_iff_sublist]
|
||||
else
|
||||
simp only [beq_iff_eq, h_eq]
|
||||
constructor
|
||||
· intro h_sub
|
||||
apply Sublist.cons
|
||||
exact isSublist_iff_sublist.mp h_sub
|
||||
· intro h_sub
|
||||
cases h_sub
|
||||
case cons h_sub =>
|
||||
exact isSublist_iff_sublist.mpr h_sub
|
||||
case cons₂ =>
|
||||
contradiction
|
||||
|
||||
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <+ l₂) :=
|
||||
decidable_of_iff (l₁.isSublist l₂) isSublist_iff_sublist
|
||||
|
||||
/-! ### IsPrefix / IsSuffix / IsInfix -/
|
||||
|
||||
@[simp] theorem prefix_append (l₁ l₂ : List α) : l₁ <+: l₁ ++ l₂ := ⟨l₂, rfl⟩
|
||||
|
||||
@[simp] theorem suffix_append (l₁ l₂ : List α) : l₂ <:+ l₁ ++ l₂ := ⟨l₁, rfl⟩
|
||||
|
||||
theorem infix_append (l₁ l₂ l₃ : List α) : l₂ <:+: l₁ ++ l₂ ++ l₃ := ⟨l₁, l₃, rfl⟩
|
||||
|
||||
@[simp] theorem infix_append' (l₁ l₂ l₃ : List α) : l₂ <:+: l₁ ++ (l₂ ++ l₃) := by
|
||||
rw [← List.append_assoc]; apply infix_append
|
||||
|
||||
theorem IsPrefix.isInfix : l₁ <+: l₂ → l₁ <:+: l₂ := fun ⟨t, h⟩ => ⟨[], t, h⟩
|
||||
|
||||
theorem IsSuffix.isInfix : l₁ <:+ l₂ → l₁ <:+: l₂ := fun ⟨t, h⟩ => ⟨t, [], by rw [h, append_nil]⟩
|
||||
|
||||
@[simp] theorem nil_prefix (l : List α) : [] <+: l := ⟨l, rfl⟩
|
||||
|
||||
@[simp] theorem nil_suffix (l : List α) : [] <:+ l := ⟨l, append_nil _⟩
|
||||
|
||||
@[simp] theorem nil_infix (l : List α) : [] <:+: l := (nil_prefix _).isInfix
|
||||
|
||||
@[simp] theorem prefix_refl (l : List α) : l <+: l := ⟨[], append_nil _⟩
|
||||
|
||||
@[simp] theorem suffix_refl (l : List α) : l <:+ l := ⟨[], rfl⟩
|
||||
|
||||
@[simp] theorem infix_refl (l : List α) : l <:+: l := (prefix_refl l).isInfix
|
||||
|
||||
@[simp] theorem suffix_cons (a : α) : ∀ l, l <:+ a :: l := suffix_append [a]
|
||||
|
||||
theorem infix_cons : l₁ <:+: l₂ → l₁ <:+: a :: l₂ := fun ⟨L₁, L₂, h⟩ => ⟨a :: L₁, L₂, h ▸ rfl⟩
|
||||
|
||||
theorem infix_concat : l₁ <:+: l₂ → l₁ <:+: concat l₂ a := fun ⟨L₁, L₂, h⟩ =>
|
||||
⟨L₁, concat L₂ a, by simp [← h, concat_eq_append, append_assoc]⟩
|
||||
|
||||
theorem IsPrefix.trans : ∀ {l₁ l₂ l₃ : List α}, l₁ <+: l₂ → l₂ <+: l₃ → l₁ <+: l₃
|
||||
| _, _, _, ⟨r₁, rfl⟩, ⟨r₂, rfl⟩ => ⟨r₁ ++ r₂, (append_assoc _ _ _).symm⟩
|
||||
|
||||
theorem IsSuffix.trans : ∀ {l₁ l₂ l₃ : List α}, l₁ <:+ l₂ → l₂ <:+ l₃ → l₁ <:+ l₃
|
||||
| _, _, _, ⟨l₁, rfl⟩, ⟨l₂, rfl⟩ => ⟨l₂ ++ l₁, append_assoc _ _ _⟩
|
||||
|
||||
theorem IsInfix.trans : ∀ {l₁ l₂ l₃ : List α}, l₁ <:+: l₂ → l₂ <:+: l₃ → l₁ <:+: l₃
|
||||
| l, _, _, ⟨l₁, r₁, rfl⟩, ⟨l₂, r₂, rfl⟩ => ⟨l₂ ++ l₁, r₁ ++ r₂, by simp only [append_assoc]⟩
|
||||
|
||||
protected theorem IsInfix.sublist : l₁ <:+: l₂ → l₁ <+ l₂
|
||||
| ⟨_, _, h⟩ => h ▸ (sublist_append_right ..).trans (sublist_append_left ..)
|
||||
|
||||
protected theorem IsInfix.subset (hl : l₁ <:+: l₂) : l₁ ⊆ l₂ :=
|
||||
hl.sublist.subset
|
||||
|
||||
protected theorem IsPrefix.sublist (h : l₁ <+: l₂) : l₁ <+ l₂ :=
|
||||
h.isInfix.sublist
|
||||
|
||||
protected theorem IsPrefix.subset (hl : l₁ <+: l₂) : l₁ ⊆ l₂ :=
|
||||
hl.sublist.subset
|
||||
|
||||
protected theorem IsSuffix.sublist (h : l₁ <:+ l₂) : l₁ <+ l₂ :=
|
||||
h.isInfix.sublist
|
||||
|
||||
protected theorem IsSuffix.subset (hl : l₁ <:+ l₂) : l₁ ⊆ l₂ :=
|
||||
hl.sublist.subset
|
||||
|
||||
@[simp] theorem reverse_suffix : reverse l₁ <:+ reverse l₂ ↔ l₁ <+: l₂ :=
|
||||
⟨fun ⟨r, e⟩ => ⟨reverse r, by rw [← reverse_reverse l₁, ← reverse_append, e, reverse_reverse]⟩,
|
||||
fun ⟨r, e⟩ => ⟨reverse r, by rw [← reverse_append, e]⟩⟩
|
||||
|
||||
@[simp] theorem reverse_prefix : reverse l₁ <+: reverse l₂ ↔ l₁ <:+ l₂ := by
|
||||
rw [← reverse_suffix]; simp only [reverse_reverse]
|
||||
|
||||
@[simp] theorem reverse_infix : reverse l₁ <:+: reverse l₂ ↔ l₁ <:+: l₂ := by
|
||||
refine ⟨fun ⟨s, t, e⟩ => ⟨reverse t, reverse s, ?_⟩, fun ⟨s, t, e⟩ => ⟨reverse t, reverse s, ?_⟩⟩
|
||||
· rw [← reverse_reverse l₁, append_assoc, ← reverse_append, ← reverse_append, e,
|
||||
reverse_reverse]
|
||||
· rw [append_assoc, ← reverse_append, ← reverse_append, e]
|
||||
|
||||
theorem IsInfix.length_le (h : l₁ <:+: l₂) : l₁.length ≤ l₂.length :=
|
||||
h.sublist.length_le
|
||||
|
||||
theorem IsPrefix.length_le (h : l₁ <+: l₂) : l₁.length ≤ l₂.length :=
|
||||
h.sublist.length_le
|
||||
|
||||
theorem IsSuffix.length_le (h : l₁ <:+ l₂) : l₁.length ≤ l₂.length :=
|
||||
h.sublist.length_le
|
||||
|
||||
@[simp] theorem infix_nil : l <:+: [] ↔ l = [] := ⟨(sublist_nil.1 ·.sublist), (· ▸ infix_refl _)⟩
|
||||
|
||||
@[simp] theorem prefix_nil : l <+: [] ↔ l = [] := ⟨(sublist_nil.1 ·.sublist), (· ▸ prefix_refl _)⟩
|
||||
|
||||
@[simp] theorem suffix_nil : l <:+ [] ↔ l = [] := ⟨(sublist_nil.1 ·.sublist), (· ▸ suffix_refl _)⟩
|
||||
|
||||
theorem infix_iff_prefix_suffix (l₁ l₂ : List α) : l₁ <:+: l₂ ↔ ∃ t, l₁ <+: t ∧ t <:+ l₂ :=
|
||||
⟨fun ⟨_, t, e⟩ => ⟨l₁ ++ t, ⟨_, rfl⟩, e ▸ append_assoc .. ▸ ⟨_, rfl⟩⟩,
|
||||
fun ⟨_, ⟨t, rfl⟩, s, e⟩ => ⟨s, t, append_assoc .. ▸ e⟩⟩
|
||||
|
||||
theorem IsInfix.eq_of_length (h : l₁ <:+: l₂) : l₁.length = l₂.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length
|
||||
|
||||
theorem IsPrefix.eq_of_length (h : l₁ <+: l₂) : l₁.length = l₂.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length
|
||||
|
||||
theorem IsSuffix.eq_of_length (h : l₁ <:+ l₂) : l₁.length = l₂.length → l₁ = l₂ :=
|
||||
h.sublist.eq_of_length
|
||||
|
||||
theorem prefix_of_prefix_length_le :
|
||||
∀ {l₁ l₂ l₃ : List α}, l₁ <+: l₃ → l₂ <+: l₃ → length l₁ ≤ length l₂ → l₁ <+: l₂
|
||||
| [], l₂, _, _, _, _ => nil_prefix _
|
||||
| a :: l₁, b :: l₂, _, ⟨r₁, rfl⟩, ⟨r₂, e⟩, ll => by
|
||||
injection e with _ e'; subst b
|
||||
rcases prefix_of_prefix_length_le ⟨_, rfl⟩ ⟨_, e'⟩ (le_of_succ_le_succ ll) with ⟨r₃, rfl⟩
|
||||
exact ⟨r₃, rfl⟩
|
||||
|
||||
theorem prefix_or_prefix_of_prefix (h₁ : l₁ <+: l₃) (h₂ : l₂ <+: l₃) : l₁ <+: l₂ ∨ l₂ <+: l₁ :=
|
||||
(Nat.le_total (length l₁) (length l₂)).imp (prefix_of_prefix_length_le h₁ h₂)
|
||||
(prefix_of_prefix_length_le h₂ h₁)
|
||||
|
||||
theorem suffix_of_suffix_length_le
|
||||
(h₁ : l₁ <:+ l₃) (h₂ : l₂ <:+ l₃) (ll : length l₁ ≤ length l₂) : l₁ <:+ l₂ :=
|
||||
reverse_prefix.1 <|
|
||||
prefix_of_prefix_length_le (reverse_prefix.2 h₁) (reverse_prefix.2 h₂) (by simp [ll])
|
||||
|
||||
theorem suffix_or_suffix_of_suffix (h₁ : l₁ <:+ l₃) (h₂ : l₂ <:+ l₃) : l₁ <:+ l₂ ∨ l₂ <:+ l₁ :=
|
||||
(prefix_or_prefix_of_prefix (reverse_prefix.2 h₁) (reverse_prefix.2 h₂)).imp reverse_prefix.1
|
||||
reverse_prefix.1
|
||||
|
||||
theorem prefix_cons_iff : l₁ <+: a :: l₂ ↔ l₁ = [] ∨ ∃ t, l₁ = a :: t ∧ t <+: l₂ := by
|
||||
cases l₁ with
|
||||
| nil => simp
|
||||
| cons a' l₁ =>
|
||||
constructor
|
||||
· rintro ⟨t, h⟩
|
||||
simp at h
|
||||
obtain ⟨rfl, rfl⟩ := h
|
||||
exact Or.inr ⟨l₁, rfl, prefix_append l₁ t⟩
|
||||
· rintro (h | ⟨t, w, ⟨s, h'⟩⟩)
|
||||
· simp [h]
|
||||
· simp only [w]
|
||||
refine ⟨s, by simp [h']⟩
|
||||
|
||||
@[simp] theorem cons_prefix_cons : a :: l₁ <+: b :: l₂ ↔ a = b ∧ l₁ <+: l₂ := by
|
||||
simp only [prefix_cons_iff, cons.injEq, false_or]
|
||||
constructor
|
||||
· rintro ⟨t, ⟨rfl, rfl⟩, h⟩
|
||||
exact ⟨rfl, h⟩
|
||||
· rintro ⟨rfl, h⟩
|
||||
exact ⟨l₁, ⟨rfl, rfl⟩, h⟩
|
||||
|
||||
theorem suffix_cons_iff : l₁ <:+ a :: l₂ ↔ l₁ = a :: l₂ ∨ l₁ <:+ l₂ := by
|
||||
constructor
|
||||
· rintro ⟨⟨hd, tl⟩, hl₃⟩
|
||||
· exact Or.inl hl₃
|
||||
· simp only [cons_append] at hl₃
|
||||
injection hl₃ with _ hl₄
|
||||
exact Or.inr ⟨_, hl₄⟩
|
||||
· rintro (rfl | hl₁)
|
||||
· exact (a :: l₂).suffix_refl
|
||||
· exact hl₁.trans (l₂.suffix_cons _)
|
||||
|
||||
theorem infix_cons_iff : l₁ <:+: a :: l₂ ↔ l₁ <+: a :: l₂ ∨ l₁ <:+: l₂ := by
|
||||
constructor
|
||||
· rintro ⟨⟨hd, tl⟩, t, hl₃⟩
|
||||
· exact Or.inl ⟨t, hl₃⟩
|
||||
· simp only [cons_append] at hl₃
|
||||
injection hl₃ with _ hl₄
|
||||
exact Or.inr ⟨_, t, hl₄⟩
|
||||
· rintro (h | hl₁)
|
||||
· exact h.isInfix
|
||||
· exact infix_cons hl₁
|
||||
|
||||
theorem infix_of_mem_join : ∀ {L : List (List α)}, l ∈ L → l <:+: join L
|
||||
| l' :: _, h =>
|
||||
match h with
|
||||
| List.Mem.head .. => infix_append [] _ _
|
||||
| List.Mem.tail _ hlMemL =>
|
||||
IsInfix.trans (infix_of_mem_join hlMemL) <| (suffix_append _ _).isInfix
|
||||
|
||||
theorem prefix_append_right_inj (l) : l ++ l₁ <+: l ++ l₂ ↔ l₁ <+: l₂ :=
|
||||
exists_congr fun r => by rw [append_assoc, append_right_inj]
|
||||
|
||||
@[simp]
|
||||
theorem prefix_cons_inj (a) : a :: l₁ <+: a :: l₂ ↔ l₁ <+: l₂ :=
|
||||
prefix_append_right_inj [a]
|
||||
|
||||
theorem take_prefix (n) (l : List α) : take n l <+: l :=
|
||||
⟨_, take_append_drop _ _⟩
|
||||
|
||||
theorem drop_suffix (n) (l : List α) : drop n l <:+ l :=
|
||||
⟨_, take_append_drop _ _⟩
|
||||
|
||||
theorem take_sublist (n) (l : List α) : take n l <+ l :=
|
||||
(take_prefix n l).sublist
|
||||
|
||||
theorem drop_sublist (n) (l : List α) : drop n l <+ l :=
|
||||
(drop_suffix n l).sublist
|
||||
|
||||
theorem take_subset (n) (l : List α) : take n l ⊆ l :=
|
||||
(take_sublist n l).subset
|
||||
|
||||
theorem drop_subset (n) (l : List α) : drop n l ⊆ l :=
|
||||
(drop_sublist n l).subset
|
||||
|
||||
theorem mem_of_mem_take {l : List α} (h : a ∈ l.take n) : a ∈ l :=
|
||||
take_subset n l h
|
||||
|
||||
theorem mem_of_mem_drop {n} {l : List α} (h : a ∈ l.drop n) : a ∈ l :=
|
||||
drop_subset _ _ h
|
||||
|
||||
theorem IsPrefix.filter (p : α → Bool) ⦃l₁ l₂ : List α⦄ (h : l₁ <+: l₂) :
|
||||
l₁.filter p <+: l₂.filter p := by
|
||||
obtain ⟨xs, rfl⟩ := h
|
||||
rw [filter_append]; apply prefix_append
|
||||
|
||||
theorem IsSuffix.filter (p : α → Bool) ⦃l₁ l₂ : List α⦄ (h : l₁ <:+ l₂) :
|
||||
l₁.filter p <:+ l₂.filter p := by
|
||||
obtain ⟨xs, rfl⟩ := h
|
||||
rw [filter_append]; apply suffix_append
|
||||
|
||||
theorem IsInfix.filter (p : α → Bool) ⦃l₁ l₂ : List α⦄ (h : l₁ <:+: l₂) :
|
||||
l₁.filter p <:+: l₂.filter p := by
|
||||
obtain ⟨xs, ys, rfl⟩ := h
|
||||
rw [filter_append, filter_append]; apply infix_append _
|
||||
|
||||
@[simp] theorem isPrefixOf_iff_prefix [BEq α] [LawfulBEq α] {l₁ l₂ : List α} :
|
||||
l₁.isPrefixOf l₂ ↔ l₁ <+: l₂ := by
|
||||
induction l₁ generalizing l₂ with
|
||||
| nil => simp
|
||||
| cons a l₁ ih =>
|
||||
cases l₂ with
|
||||
| nil => simp
|
||||
| cons a' l₂ => simp [isPrefixOf, ih]
|
||||
|
||||
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <+: l₂) :=
|
||||
decidable_of_iff (l₁.isPrefixOf l₂) isPrefixOf_iff_prefix
|
||||
|
||||
@[simp] theorem isSuffixOf_iff_suffix [BEq α] [LawfulBEq α] {l₁ l₂ : List α} :
|
||||
l₁.isSuffixOf l₂ ↔ l₁ <:+ l₂ := by
|
||||
simp [isSuffixOf]
|
||||
|
||||
instance [DecidableEq α] (l₁ l₂ : List α) : Decidable (l₁ <:+ l₂) :=
|
||||
decidable_of_iff (l₁.isSuffixOf l₂) isSuffixOf_iff_suffix
|
||||
|
||||
end List
|
||||
@@ -5,438 +5,443 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
# Further lemmas about `List.take`, `List.drop`, `List.zip` and `List.zipWith`.
|
||||
|
||||
These are in a separate file from most of the list lemmas
|
||||
as they required importing more lemmas about natural numbers, and use `omega`.
|
||||
# Lemmas about `List.zip`, `List.zipWith`, `List.zipWithAll`, and `List.unzip`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ### take -/
|
||||
/-! ### take and drop
|
||||
|
||||
@[simp] theorem length_take : ∀ (i : Nat) (l : List α), length (take i l) = min i (length l)
|
||||
| 0, l => by simp [Nat.zero_min]
|
||||
| succ n, [] => by simp [Nat.min_zero]
|
||||
| succ n, _ :: l => by simp [Nat.succ_min_succ, length_take]
|
||||
|
||||
theorem length_take_le (n) (l : List α) : length (take n l) ≤ n := by simp [Nat.min_le_left]
|
||||
|
||||
theorem length_take_le' (n) (l : List α) : length (take n l) ≤ l.length :=
|
||||
by simp [Nat.min_le_right]
|
||||
|
||||
theorem length_take_of_le (h : n ≤ length l) : length (take n l) = n := by simp [Nat.min_eq_left h]
|
||||
|
||||
theorem take_take : ∀ (n m) (l : List α), take n (take m l) = take (min n m) l
|
||||
| n, 0, l => by rw [Nat.min_zero, take_zero, take_nil]
|
||||
| 0, m, l => by rw [Nat.zero_min, take_zero, take_zero]
|
||||
| succ n, succ m, nil => by simp only [take_nil]
|
||||
| succ n, succ m, a :: l => by
|
||||
simp only [take, succ_min_succ, take_take n m l]
|
||||
|
||||
@[simp] theorem take_replicate (a : α) : ∀ n m : Nat, take n (replicate m a) = replicate (min n m) a
|
||||
| n, 0 => by simp [Nat.min_zero]
|
||||
| 0, m => by simp [Nat.zero_min]
|
||||
| succ n, succ m => by simp [replicate_succ, succ_min_succ, take_replicate]
|
||||
|
||||
@[simp] theorem drop_replicate (a : α) : ∀ n m : Nat, drop n (replicate m a) = replicate (m - n) a
|
||||
| n, 0 => by simp
|
||||
| 0, m => by simp
|
||||
| succ n, succ m => by simp [replicate_succ, succ_sub_succ, drop_replicate]
|
||||
|
||||
/-- Taking the first `n` elements in `l₁ ++ l₂` is the same as appending the first `n` elements
|
||||
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
|
||||
theorem take_append_eq_append_take {l₁ l₂ : List α} {n : Nat} :
|
||||
take n (l₁ ++ l₂) = take n l₁ ++ take (n - l₁.length) l₂ := by
|
||||
induction l₁ generalizing n
|
||||
· simp
|
||||
· cases n
|
||||
· simp [*]
|
||||
· simp only [cons_append, take_cons_succ, length_cons, succ_eq_add_one, cons.injEq,
|
||||
append_cancel_left_eq, true_and, *]
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem take_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n ≤ l₁.length) :
|
||||
(l₁ ++ l₂).take n = l₁.take n := by
|
||||
simp [take_append_eq_append_take, Nat.sub_eq_zero_of_le h]
|
||||
|
||||
/-- Taking the first `l₁.length + i` elements in `l₁ ++ l₂` is the same as appending the first
|
||||
`i` elements of `l₂` to `l₁`. -/
|
||||
theorem take_append {l₁ l₂ : List α} (i : Nat) :
|
||||
take (l₁.length + i) (l₁ ++ l₂) = l₁ ++ take i l₂ := by
|
||||
rw [take_append_eq_append_take, take_all_of_le (Nat.le_add_right _ _), Nat.add_sub_cancel_left]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the big list to the small list. -/
|
||||
theorem getElem_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
|
||||
L[i] = (L.take j)[i]'(length_take .. ▸ Nat.lt_min.mpr ⟨hj, hi⟩) :=
|
||||
getElem_of_eq (take_append_drop j L).symm _ ▸ getElem_append ..
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
theorem getElem_take' (L : List α) {j i : Nat} {h : i < (L.take j).length} :
|
||||
(L.take j)[i] =
|
||||
L[i]'(Nat.lt_of_lt_of_le h (length_take_le' _ _)) := by
|
||||
rw [length_take, Nat.lt_min] at h; rw [getElem_take L _ h.1]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the big list to the small list. -/
|
||||
@[deprecated getElem_take (since := "2024-06-12")]
|
||||
theorem get_take (L : List α) {i j : Nat} (hi : i < L.length) (hj : i < j) :
|
||||
get L ⟨i, hi⟩ = get (L.take j) ⟨i, length_take .. ▸ Nat.lt_min.mpr ⟨hj, hi⟩⟩ := by
|
||||
simp [getElem_take _ hi hj]
|
||||
|
||||
/-- The `i`-th element of a list coincides with the `i`-th element of any of its prefixes of
|
||||
length `> i`. Version designed to rewrite from the small list to the big list. -/
|
||||
@[deprecated getElem_take (since := "2024-06-12")]
|
||||
theorem get_take' (L : List α) {j i} :
|
||||
get (L.take j) i =
|
||||
get L ⟨i.1, Nat.lt_of_lt_of_le i.2 (length_take_le' _ _)⟩ := by
|
||||
simp [getElem_take']
|
||||
|
||||
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n)[m]? = none :=
|
||||
getElem?_eq_none <| Nat.le_trans (length_take_le _ _) h
|
||||
|
||||
@[deprecated getElem?_take_eq_none (since := "2024-06-12")]
|
||||
theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n ≤ m) :
|
||||
(l.take n).get? m = none := by
|
||||
simp [getElem?_take_eq_none h]
|
||||
|
||||
theorem getElem?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n)[m]? = if m < n then l[m]? else none := by
|
||||
split
|
||||
· next h => exact getElem?_take h
|
||||
· next h => exact getElem?_take_eq_none (Nat.le_of_not_lt h)
|
||||
|
||||
@[deprecated getElem?_take_eq_if (since := "2024-06-12")]
|
||||
theorem get?_take_eq_if {l : List α} {n m : Nat} :
|
||||
(l.take n).get? m = if m < n then l.get? m else none := by
|
||||
simp [getElem?_take_eq_if]
|
||||
Further results on `List.take` and `List.drop`, which rely on stronger automation in `Nat`,
|
||||
are given in `Init.Data.List.TakeDrop`.
|
||||
-/
|
||||
|
||||
@[simp]
|
||||
theorem take_eq_take :
|
||||
∀ {l : List α} {m n : Nat}, l.take m = l.take n ↔ min m l.length = min n l.length
|
||||
| [], m, n => by simp [Nat.min_zero]
|
||||
| _ :: xs, 0, 0 => by simp
|
||||
| x :: xs, m + 1, 0 => by simp [Nat.zero_min, succ_min_succ]
|
||||
| x :: xs, 0, n + 1 => by simp [Nat.zero_min, succ_min_succ]
|
||||
| x :: xs, m + 1, n + 1 => by simp [succ_min_succ, take_eq_take]; omega
|
||||
theorem drop_one : ∀ l : List α, drop 1 l = tail l
|
||||
| [] | _ :: _ => rfl
|
||||
|
||||
theorem take_add (l : List α) (m n : Nat) : l.take (m + n) = l.take m ++ (l.drop m).take n := by
|
||||
suffices take (m + n) (take m l ++ drop m l) = take m l ++ take n (drop m l) by
|
||||
rw [take_append_drop] at this
|
||||
assumption
|
||||
rw [take_append_eq_append_take, take_all_of_le, append_right_inj]
|
||||
· simp only [take_eq_take, length_take, length_drop]
|
||||
omega
|
||||
apply Nat.le_trans (m := m)
|
||||
· apply length_take_le
|
||||
· apply Nat.le_add_right
|
||||
@[simp] theorem take_append_drop : ∀ (n : Nat) (l : List α), take n l ++ drop n l = l
|
||||
| 0, _ => rfl
|
||||
| _+1, [] => rfl
|
||||
| n+1, x :: xs => congrArg (cons x) <| take_append_drop n xs
|
||||
|
||||
theorem dropLast_take {n : Nat} {l : List α} (h : n < l.length) :
|
||||
(l.take n).dropLast = l.take n.pred := by
|
||||
simp only [dropLast_eq_take, length_take, Nat.le_of_lt h, take_take, pred_le, Nat.min_eq_left]
|
||||
@[simp] theorem length_drop : ∀ (i : Nat) (l : List α), length (drop i l) = length l - i
|
||||
| 0, _ => rfl
|
||||
| succ i, [] => Eq.symm (Nat.zero_sub (succ i))
|
||||
| succ i, x :: l => calc
|
||||
length (drop (succ i) (x :: l)) = length l - i := length_drop i l
|
||||
_ = succ (length l) - succ i := (Nat.succ_sub_succ_eq_sub (length l) i).symm
|
||||
|
||||
theorem map_eq_append_split {f : α → β} {l : List α} {s₁ s₂ : List β}
|
||||
(h : map f l = s₁ ++ s₂) : ∃ l₁ l₂, l = l₁ ++ l₂ ∧ map f l₁ = s₁ ∧ map f l₂ = s₂ := by
|
||||
have := h
|
||||
rw [← take_append_drop (length s₁) l] at this ⊢
|
||||
rw [map_append] at this
|
||||
refine ⟨_, _, rfl, append_inj this ?_⟩
|
||||
rw [length_map, length_take, Nat.min_eq_left]
|
||||
rw [← length_map l f, h, length_append]
|
||||
apply Nat.le_add_right
|
||||
theorem drop_of_length_le {l : List α} (h : l.length ≤ i) : drop i l = [] :=
|
||||
length_eq_zero.1 (length_drop .. ▸ Nat.sub_eq_zero_of_le h)
|
||||
|
||||
/-! ### drop -/
|
||||
theorem length_lt_of_drop_ne_nil {l : List α} {n} (h : drop n l ≠ []) : n < l.length :=
|
||||
gt_of_not_le (mt drop_of_length_le h)
|
||||
|
||||
theorem drop_length_cons {l : List α} (h : l ≠ []) (a : α) :
|
||||
(a :: l).drop l.length = [l.getLast h] := by
|
||||
induction l generalizing a with
|
||||
| nil =>
|
||||
cases h rfl
|
||||
| cons y l ih =>
|
||||
simp only [drop, length]
|
||||
by_cases h₁ : l = []
|
||||
· simp [h₁]
|
||||
rw [getLast_cons' _ h₁]
|
||||
exact ih h₁ y
|
||||
theorem take_of_length_le {l : List α} (h : l.length ≤ i) : take i l = l := by
|
||||
have := take_append_drop i l
|
||||
rw [drop_of_length_le h, append_nil] at this; exact this
|
||||
|
||||
/-- Dropping the elements up to `n` in `l₁ ++ l₂` is the same as dropping the elements up to `n`
|
||||
in `l₁`, dropping the elements up to `n - l₁.length` in `l₂`, and appending them. -/
|
||||
theorem drop_append_eq_append_drop {l₁ l₂ : List α} {n : Nat} :
|
||||
drop n (l₁ ++ l₂) = drop n l₁ ++ drop (n - l₁.length) l₂ := by
|
||||
induction l₁ generalizing n
|
||||
· simp
|
||||
· cases n
|
||||
· simp [*]
|
||||
· simp only [cons_append, drop_succ_cons, length_cons, succ_eq_add_one, append_cancel_left_eq, *]
|
||||
congr 1
|
||||
omega
|
||||
theorem lt_length_of_take_ne_self {l : List α} {n} (h : l.take n ≠ l) : n < l.length :=
|
||||
gt_of_not_le (mt take_of_length_le h)
|
||||
|
||||
theorem drop_append_of_le_length {l₁ l₂ : List α} {n : Nat} (h : n ≤ l₁.length) :
|
||||
(l₁ ++ l₂).drop n = l₁.drop n ++ l₂ := by
|
||||
simp [drop_append_eq_append_drop, Nat.sub_eq_zero_of_le h]
|
||||
@[deprecated drop_of_length_le (since := "2024-07-07")] abbrev drop_length_le := @drop_of_length_le
|
||||
@[deprecated take_of_length_le (since := "2024-07-07")] abbrev take_length_le := @take_of_length_le
|
||||
|
||||
@[simp] theorem drop_length (l : List α) : drop l.length l = [] := drop_of_length_le (Nat.le_refl _)
|
||||
|
||||
/-- Dropping the elements up to `l₁.length + i` in `l₁ + l₂` is the same as dropping the elements
|
||||
up to `i` in `l₂`. -/
|
||||
@[simp]
|
||||
theorem drop_append {l₁ l₂ : List α} (i : Nat) : drop (l₁.length + i) (l₁ ++ l₂) = drop i l₂ := by
|
||||
rw [drop_append_eq_append_drop, drop_eq_nil_of_le] <;>
|
||||
simp [Nat.add_sub_cancel_left, Nat.le_add_right]
|
||||
|
||||
theorem lt_length_drop (L : List α) {i j : Nat} (h : i + j < L.length) : j < (L.drop i).length := by
|
||||
have A : i < L.length := Nat.lt_of_le_of_lt (Nat.le.intro rfl) h
|
||||
rw [(take_append_drop i L).symm] at h
|
||||
simpa only [Nat.le_of_lt A, Nat.min_eq_left, Nat.add_lt_add_iff_left, length_take,
|
||||
length_append] using h
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
|
||||
theorem getElem_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
|
||||
L[i + j] = (L.drop i)[j]'(lt_length_drop L h) := by
|
||||
have : i ≤ L.length := Nat.le_trans (Nat.le_add_right _ _) (Nat.le_of_lt h)
|
||||
rw [getElem_of_eq (take_append_drop i L).symm h, getElem_append_right'] <;>
|
||||
simp [Nat.min_eq_left this, Nat.add_sub_cancel_left, Nat.le_add_right]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the big list to the small list. -/
|
||||
@[deprecated getElem_drop (since := "2024-06-12")]
|
||||
theorem get_drop (L : List α) {i j : Nat} (h : i + j < L.length) :
|
||||
get L ⟨i + j, h⟩ = get (L.drop i) ⟨j, lt_length_drop L h⟩ := by
|
||||
simp [getElem_drop]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
|
||||
theorem getElem_drop' (L : List α) {i : Nat} {j : Nat} {h : j < (L.drop i).length} :
|
||||
(L.drop i)[j] = L[i + j]'(by
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub (length_drop i L ▸ h)) := by
|
||||
rw [getElem_drop]
|
||||
|
||||
/-- The `i + j`-th element of a list coincides with the `j`-th element of the list obtained by
|
||||
dropping the first `i` elements. Version designed to rewrite from the small list to the big list. -/
|
||||
@[deprecated getElem_drop' (since := "2024-06-12")]
|
||||
theorem get_drop' (L : List α) {i j} :
|
||||
get (L.drop i) j = get L ⟨i + j, by
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub (length_drop i L ▸ j.2)⟩ := by
|
||||
simp [getElem_drop']
|
||||
@[simp] theorem take_length (l : List α) : take l.length l = l := take_of_length_le (Nat.le_refl _)
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? := by
|
||||
ext
|
||||
simp only [getElem?_eq_some, getElem_drop', Option.mem_def]
|
||||
constructor <;> intro ⟨h, ha⟩
|
||||
· exact ⟨_, ha⟩
|
||||
· refine ⟨?_, ha⟩
|
||||
rw [length_drop]
|
||||
rw [Nat.add_comm] at h
|
||||
apply Nat.lt_sub_of_add_lt h
|
||||
theorem getElem_cons_drop : ∀ (l : List α) (i : Nat) (h : i < l.length),
|
||||
l[i] :: drop (i + 1) l = drop i l
|
||||
| _::_, 0, _ => rfl
|
||||
| _::_, i+1, _ => getElem_cons_drop _ i _
|
||||
|
||||
@[deprecated getElem?_drop (since := "2024-06-12")]
|
||||
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
|
||||
@[deprecated getElem_cons_drop (since := "2024-06-12")]
|
||||
theorem get_cons_drop (l : List α) (i) : get l i :: drop (i + 1) l = drop i l := by
|
||||
simp
|
||||
|
||||
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
|
||||
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
|
||||
split <;> rename_i h
|
||||
· ext1 m
|
||||
by_cases h' : m < n
|
||||
· rw [getElem?_append (by simp [length_take]; omega), getElem?_set_ne (by omega),
|
||||
getElem?_take h']
|
||||
· by_cases h'' : m = n
|
||||
· subst h''
|
||||
rw [getElem?_set_eq (by simp; omega), getElem?_append_right, length_take,
|
||||
Nat.min_eq_left (by omega), Nat.sub_self, getElem?_cons_zero]
|
||||
rw [length_take]
|
||||
exact Nat.min_le_left m l.length
|
||||
· have h''' : n < m := by omega
|
||||
rw [getElem?_set_ne (by omega), getElem?_append_right, length_take,
|
||||
Nat.min_eq_left (by omega)]
|
||||
· obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h'''
|
||||
have p : n + k + 1 - n = k + 1 := by omega
|
||||
rw [p]
|
||||
rw [getElem?_cons_succ, getElem?_drop]
|
||||
congr 1
|
||||
omega
|
||||
· rw [length_take]
|
||||
exact Nat.le_trans (Nat.min_le_left _ _) (by omega)
|
||||
· rw [set_eq_of_length_le]
|
||||
omega
|
||||
theorem drop_eq_getElem_cons {n} {l : List α} (h) : drop n l = l[n] :: drop (n + 1) l :=
|
||||
(getElem_cons_drop _ n h).symm
|
||||
|
||||
theorem exists_of_set {n : Nat} {a' : α} {l : List α} (h : n < l.length) :
|
||||
∃ l₁ l₂, l = l₁ ++ l[n] :: l₂ ∧ l₁.length = n ∧ l.set n a' = l₁ ++ a' :: l₂ := by
|
||||
refine ⟨l.take n, l.drop (n + 1), ⟨by simp, ⟨length_take_of_le (Nat.le_of_lt h), ?_⟩⟩⟩
|
||||
simp [set_eq_take_append_cons_drop, h]
|
||||
@[deprecated drop_eq_getElem_cons (since := "2024-06-12")]
|
||||
theorem drop_eq_get_cons {n} {l : List α} (h) : drop n l = get l ⟨n, h⟩ :: drop (n + 1) l := by
|
||||
simp [drop_eq_getElem_cons]
|
||||
|
||||
theorem drop_set_of_lt (a : α) {n m : Nat} (l : List α)
|
||||
(hnm : n < m) : drop m (l.set n a) = l.drop m :=
|
||||
ext_getElem? fun k => by simpa only [getElem?_drop] using getElem?_set_ne (by omega)
|
||||
@[simp]
|
||||
theorem getElem?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
|
||||
induction n generalizing l m with
|
||||
| zero =>
|
||||
exact absurd h (Nat.not_lt_of_le m.zero_le)
|
||||
| succ _ hn =>
|
||||
cases l with
|
||||
| nil => simp only [take_nil]
|
||||
| cons hd tl =>
|
||||
cases m
|
||||
· simp
|
||||
· simpa using hn (Nat.lt_of_succ_lt_succ h)
|
||||
|
||||
theorem drop_take : ∀ (m n : Nat) (l : List α), drop n (take m l) = take (m - n) (drop n l)
|
||||
@[deprecated getElem?_take (since := "2024-06-12")]
|
||||
theorem get?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n).get? m = l.get? m := by
|
||||
simp [getElem?_take, h]
|
||||
|
||||
@[simp]
|
||||
theorem getElem?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? :=
|
||||
getElem?_take (Nat.lt_succ_self n)
|
||||
|
||||
theorem tail_drop (l : List α) (n : Nat) : (l.drop n).tail = l.drop (n + 1) := by
|
||||
induction l generalizing n with
|
||||
| nil => simp
|
||||
| cons hd tl hl =>
|
||||
cases n
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
@[simp]
|
||||
theorem drop_eq_nil_iff_le {l : List α} {k : Nat} : l.drop k = [] ↔ l.length ≤ k := by
|
||||
refine' ⟨fun h => _, drop_eq_nil_of_le⟩
|
||||
induction k generalizing l with
|
||||
| zero =>
|
||||
simp only [drop] at h
|
||||
simp [h]
|
||||
| succ k hk =>
|
||||
cases l
|
||||
· simp
|
||||
· simp only [drop] at h
|
||||
simpa [Nat.succ_le_succ_iff] using hk h
|
||||
|
||||
@[simp]
|
||||
theorem take_eq_nil_iff {l : List α} {k : Nat} : l.take k = [] ↔ k = 0 ∨ l = [] := by
|
||||
cases l <;> cases k <;> simp [Nat.succ_ne_zero]
|
||||
|
||||
theorem drop_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.drop i = []
|
||||
| _, _, rfl => drop_nil
|
||||
|
||||
theorem ne_nil_of_drop_ne_nil {as : List α} {i : Nat} (h: as.drop i ≠ []) : as ≠ [] :=
|
||||
mt drop_eq_nil_of_eq_nil h
|
||||
|
||||
theorem take_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.take i = []
|
||||
| _, _, rfl => take_nil
|
||||
|
||||
theorem ne_nil_of_take_ne_nil {as : List α} {i : Nat} (h : as.take i ≠ []) : as ≠ [] :=
|
||||
mt take_eq_nil_of_eq_nil h
|
||||
|
||||
theorem set_take {l : List α} {n m : Nat} {a : α} :
|
||||
(l.set m a).take n = (l.take n).set m a := by
|
||||
induction n generalizing l m with
|
||||
| zero => simp
|
||||
| succ _ hn =>
|
||||
cases l with
|
||||
| nil => simp
|
||||
| cons hd tl => cases m <;> simp_all
|
||||
|
||||
theorem drop_set {l : List α} {n m : Nat} {a : α} :
|
||||
(l.set m a).drop n = if m < n then l.drop n else (l.drop n).set (m - n) a := by
|
||||
induction n generalizing l m with
|
||||
| zero => simp
|
||||
| succ _ hn =>
|
||||
cases l with
|
||||
| nil => simp
|
||||
| cons hd tl =>
|
||||
cases m
|
||||
· simp_all
|
||||
· simp only [hn, set_cons_succ, drop_succ_cons, succ_lt_succ_iff]
|
||||
congr 2
|
||||
exact (Nat.add_sub_add_right ..).symm
|
||||
|
||||
theorem set_drop {l : List α} {n m : Nat} {a : α} :
|
||||
(l.drop n).set m a = (l.set (n + m) a).drop n := by
|
||||
rw [drop_set, if_neg, add_sub_self_left n m]
|
||||
exact (Nat.not_lt).2 (le_add_right n m)
|
||||
|
||||
theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
|
||||
(l.take i).concat l[i] = l.take (i+1) :=
|
||||
Eq.symm <| (append_left_inj _).1 <| (take_append_drop (i+1) l).trans <| by
|
||||
rw [concat_eq_append, append_assoc, singleton_append, get_drop_eq_drop, take_append_drop]
|
||||
|
||||
@[deprecated take_succ_cons (since := "2024-07-25")]
|
||||
theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
|
||||
@[deprecated take_of_length_le (since := "2024-07-25")]
|
||||
theorem take_all_of_le {n} {l : List α} (h : length l ≤ n) : take n l = l :=
|
||||
take_of_length_le h
|
||||
|
||||
theorem drop_left : ∀ l₁ l₂ : List α, drop (length l₁) (l₁ ++ l₂) = l₂
|
||||
| [], _ => rfl
|
||||
| _ :: l₁, l₂ => drop_left l₁ l₂
|
||||
|
||||
@[simp]
|
||||
theorem drop_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : drop n (l₁ ++ l₂) = l₂ := by
|
||||
rw [← h]; apply drop_left
|
||||
|
||||
theorem take_left : ∀ l₁ l₂ : List α, take (length l₁) (l₁ ++ l₂) = l₁
|
||||
| [], _ => rfl
|
||||
| a :: l₁, l₂ => congrArg (cons a) (take_left l₁ l₂)
|
||||
|
||||
@[simp]
|
||||
theorem take_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : take n (l₁ ++ l₂) = l₁ := by
|
||||
rw [← h]; apply take_left
|
||||
|
||||
theorem take_succ {l : List α} {n : Nat} : l.take (n + 1) = l.take n ++ l[n]?.toList := by
|
||||
induction l generalizing n with
|
||||
| nil =>
|
||||
simp only [take_nil, Option.toList, getElem?_nil, append_nil]
|
||||
| cons hd tl hl =>
|
||||
cases n
|
||||
· simp only [take, Option.toList, getElem?_cons_zero, nil_append]
|
||||
· simp only [take, hl, getElem?_cons_succ, cons_append]
|
||||
|
||||
@[deprecated (since := "2024-07-25")]
|
||||
theorem drop_sizeOf_le [SizeOf α] (l : List α) (n : Nat) : sizeOf (l.drop n) ≤ sizeOf l := by
|
||||
induction l generalizing n with
|
||||
| nil => rw [drop_nil]; apply Nat.le_refl
|
||||
| cons _ _ lih =>
|
||||
induction n with
|
||||
| zero => apply Nat.le_refl
|
||||
| succ n =>
|
||||
exact Trans.trans (lih _) (Nat.le_add_left _ _)
|
||||
|
||||
theorem dropLast_eq_take (l : List α) : l.dropLast = l.take (l.length - 1) := by
|
||||
cases l with
|
||||
| nil => simp [dropLast]
|
||||
| cons x l =>
|
||||
induction l generalizing x <;> simp_all [dropLast]
|
||||
|
||||
@[simp] theorem map_take (f : α → β) :
|
||||
∀ (L : List α) (i : Nat), (L.take i).map f = (L.map f).take i
|
||||
| [], i => by simp
|
||||
| _, 0 => by simp
|
||||
| h :: t, n + 1 => by dsimp; rw [map_take f t n]
|
||||
|
||||
@[simp] theorem map_drop (f : α → β) :
|
||||
∀ (L : List α) (i : Nat), (L.drop i).map f = (L.map f).drop i
|
||||
| [], i => by simp
|
||||
| L, 0 => by simp
|
||||
| h :: t, n + 1 => by
|
||||
dsimp
|
||||
rw [map_drop f t]
|
||||
|
||||
@[simp] theorem drop_drop (n : Nat) : ∀ (m) (l : List α), drop n (drop m l) = drop (n + m) l
|
||||
| m, [] => by simp
|
||||
| 0, l => by simp
|
||||
| m + 1, a :: l =>
|
||||
calc
|
||||
drop n (drop (m + 1) (a :: l)) = drop n (drop m l) := rfl
|
||||
_ = drop (n + m) l := drop_drop n m l
|
||||
_ = drop (n + (m + 1)) (a :: l) := rfl
|
||||
|
||||
theorem take_drop : ∀ (m n : Nat) (l : List α), take n (drop m l) = drop m (take (m + n) l)
|
||||
| 0, _, _ => by simp
|
||||
| _, 0, _ => by simp
|
||||
| _, _, [] => by simp
|
||||
| m+1, n+1, h :: t => by
|
||||
simp [take_succ_cons, drop_succ_cons, drop_take m n t]
|
||||
congr 1
|
||||
omega
|
||||
| _+1, _, _ :: _ => by simpa [Nat.succ_add, take_succ_cons, drop_succ_cons] using take_drop ..
|
||||
|
||||
theorem take_reverse {α} {xs : List α} (n : Nat) (h : n ≤ xs.length) :
|
||||
xs.reverse.take n = (xs.drop (xs.length - n)).reverse := by
|
||||
induction xs generalizing n <;>
|
||||
simp only [reverse_cons, drop, reverse_nil, Nat.zero_sub, length, take_nil]
|
||||
next xs_hd xs_tl xs_ih =>
|
||||
cases Nat.lt_or_eq_of_le h with
|
||||
| inl h' =>
|
||||
have h' := Nat.le_of_succ_le_succ h'
|
||||
rw [take_append_of_le_length, xs_ih _ h']
|
||||
rw [show xs_tl.length + 1 - n = succ (xs_tl.length - n) from _, drop]
|
||||
· rwa [succ_eq_add_one, Nat.sub_add_comm]
|
||||
· rwa [length_reverse]
|
||||
| inr h' =>
|
||||
subst h'
|
||||
rw [length, Nat.sub_self, drop]
|
||||
suffices xs_tl.length + 1 = (xs_tl.reverse ++ [xs_hd]).length by
|
||||
rw [this, take_length, reverse_cons]
|
||||
rw [length_append, length_reverse]
|
||||
rfl
|
||||
@[deprecated drop_drop (since := "2024-06-15")]
|
||||
theorem drop_add (m n) (l : List α) : drop (m + n) l = drop m (drop n l) := by
|
||||
simp [drop_drop]
|
||||
|
||||
@[deprecated (since := "2024-06-15")] abbrev reverse_take := @take_reverse
|
||||
/-! ### takeWhile and dropWhile -/
|
||||
|
||||
theorem takeWhile_cons (p : α → Bool) (a : α) (l : List α) :
|
||||
(a :: l).takeWhile p = if p a then a :: l.takeWhile p else [] := by
|
||||
simp only [takeWhile]
|
||||
by_cases h: p a <;> simp [h]
|
||||
|
||||
@[simp] theorem takeWhile_cons_of_pos {p : α → Bool} {a : α} {l : List α} (h : p a) :
|
||||
(a :: l).takeWhile p = a :: l.takeWhile p := by
|
||||
simp [takeWhile_cons, h]
|
||||
|
||||
@[simp] theorem takeWhile_cons_of_neg {p : α → Bool} {a : α} {l : List α} (h : ¬ p a) :
|
||||
(a :: l).takeWhile p = [] := by
|
||||
simp [takeWhile_cons, h]
|
||||
|
||||
theorem dropWhile_cons :
|
||||
(x :: xs : List α).dropWhile p = if p x then xs.dropWhile p else x :: xs := by
|
||||
split <;> simp_all [dropWhile]
|
||||
|
||||
@[simp] theorem dropWhile_cons_of_pos {a : α} {l : List α} (h : p a) :
|
||||
(a :: l).dropWhile p = l.dropWhile p := by
|
||||
simp [dropWhile_cons, h]
|
||||
|
||||
@[simp] theorem dropWhile_cons_of_neg {a : α} {l : List α} (h : ¬ p a) :
|
||||
(a :: l).dropWhile p = a :: l := by
|
||||
simp [dropWhile_cons, h]
|
||||
|
||||
theorem head?_takeWhile (p : α → Bool) (l : List α) : (l.takeWhile p).head? = l.head?.filter p := by
|
||||
cases l with
|
||||
| nil => rfl
|
||||
| cons x xs =>
|
||||
simp only [takeWhile_cons, head?_cons, Option.filter_some]
|
||||
split <;> simp
|
||||
|
||||
theorem head_takeWhile (p : α → Bool) (l : List α) (w) :
|
||||
(l.takeWhile p).head w = l.head (by rintro rfl; simp_all) := by
|
||||
cases l with
|
||||
| nil => rfl
|
||||
| cons x xs =>
|
||||
simp only [takeWhile_cons, head_cons]
|
||||
simp only [takeWhile_cons] at w
|
||||
split <;> simp_all
|
||||
|
||||
theorem head?_dropWhile_not (p : α → Bool) (l : List α) :
|
||||
match (l.dropWhile p).head? with | some x => p x = false | none => True := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [dropWhile_cons]
|
||||
split <;> rename_i h <;> split at h <;> simp_all
|
||||
|
||||
theorem head_dropWhile_not (p : α → Bool) (l : List α) (w) :
|
||||
p ((l.dropWhile p).head w) = false := by
|
||||
simpa [head?_eq_head, w] using head?_dropWhile_not p l
|
||||
|
||||
theorem takeWhile_map (f : α → β) (p : β → Bool) (l : List α) :
|
||||
(l.map f).takeWhile p = (l.takeWhile (p ∘ f)).map f := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [map_cons, takeWhile_cons]
|
||||
split <;> simp_all
|
||||
|
||||
theorem dropWhile_map (f : α → β) (p : β → Bool) (l : List α) :
|
||||
(l.map f).dropWhile p = (l.dropWhile (p ∘ f)).map f := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [map_cons, dropWhile_cons]
|
||||
split <;> simp_all
|
||||
|
||||
theorem takeWhile_filterMap (f : α → Option β) (p : β → Bool) (l : List α) :
|
||||
(l.filterMap f).takeWhile p = (l.takeWhile fun a => (f a).all p).filterMap f := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [filterMap_cons]
|
||||
split <;> rename_i h
|
||||
· simp only [takeWhile_cons, h]
|
||||
split <;> simp_all
|
||||
· simp [takeWhile_cons, h, ih]
|
||||
split <;> simp_all [filterMap_cons]
|
||||
|
||||
theorem dropWhile_filterMap (f : α → Option β) (p : β → Bool) (l : List α) :
|
||||
(l.filterMap f).dropWhile p = (l.dropWhile fun a => (f a).all p).filterMap f := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
simp only [filterMap_cons]
|
||||
split <;> rename_i h
|
||||
· simp only [dropWhile_cons, h]
|
||||
split <;> simp_all
|
||||
· simp [dropWhile_cons, h, ih]
|
||||
split <;> simp_all [filterMap_cons]
|
||||
|
||||
theorem takeWhile_filter (p q : α → Bool) (l : List α) :
|
||||
(l.filter p).takeWhile q = (l.takeWhile fun a => !p a || q a).filter p := by
|
||||
simp [← filterMap_eq_filter, takeWhile_filterMap]
|
||||
|
||||
theorem dropWhile_filter (p q : α → Bool) (l : List α) :
|
||||
(l.filter p).dropWhile q = (l.dropWhile fun a => !p a || q a).filter p := by
|
||||
simp [← filterMap_eq_filter, dropWhile_filterMap]
|
||||
|
||||
@[simp] theorem takeWhile_append_dropWhile (p : α → Bool) :
|
||||
∀ (l : List α), takeWhile p l ++ dropWhile p l = l
|
||||
| [] => rfl
|
||||
| x :: xs => by simp [takeWhile, dropWhile]; cases p x <;> simp [takeWhile_append_dropWhile p xs]
|
||||
|
||||
theorem takeWhile_append {xs ys : List α} :
|
||||
(xs ++ ys).takeWhile p =
|
||||
if (xs.takeWhile p).length = xs.length then xs ++ ys.takeWhile p else xs.takeWhile p := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [cons_append, takeWhile_cons]
|
||||
split
|
||||
· simp_all only [length_cons, add_one_inj]
|
||||
split <;> rfl
|
||||
· simp_all
|
||||
|
||||
@[simp] theorem takeWhile_append_of_pos {p : α → Bool} {l₁ l₂ : List α} (h : ∀ a ∈ l₁, p a) :
|
||||
(l₁ ++ l₂).takeWhile p = l₁ ++ l₂.takeWhile p := by
|
||||
induction l₁ with
|
||||
| nil => simp
|
||||
| cons x xs ih => simp_all [takeWhile_cons]
|
||||
|
||||
theorem dropWhile_append {xs ys : List α} :
|
||||
(xs ++ ys).dropWhile p =
|
||||
if (xs.dropWhile p).isEmpty then ys.dropWhile p else xs.dropWhile p ++ ys := by
|
||||
induction xs with
|
||||
| nil => simp
|
||||
| cons h t ih =>
|
||||
simp only [cons_append, dropWhile_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem dropWhile_append_of_pos {p : α → Bool} {l₁ l₂ : List α} (h : ∀ a ∈ l₁, p a) :
|
||||
(l₁ ++ l₂).dropWhile p = l₂.dropWhile p := by
|
||||
induction l₁ with
|
||||
| nil => simp
|
||||
| cons x xs ih => simp_all [dropWhile_cons]
|
||||
|
||||
@[simp] theorem takeWhile_replicate_eq_filter (p : α → Bool) :
|
||||
(replicate n a).takeWhile p = (replicate n a).filter p := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate_succ, takeWhile_cons]
|
||||
split <;> simp_all
|
||||
|
||||
theorem takeWhile_replicate (p : α → Bool) :
|
||||
(replicate n a).takeWhile p = if p a then replicate n a else [] := by
|
||||
rw [takeWhile_replicate_eq_filter, filter_replicate]
|
||||
|
||||
@[simp] theorem dropWhile_replicate_eq_filter_not (p : α → Bool) :
|
||||
(replicate n a).dropWhile p = (replicate n a).filter (fun a => !p a) := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih =>
|
||||
simp only [replicate_succ, dropWhile_cons]
|
||||
split <;> simp_all
|
||||
|
||||
theorem dropWhile_replicate (p : α → Bool) :
|
||||
(replicate n a).dropWhile p = if p a then [] else replicate n a := by
|
||||
simp only [dropWhile_replicate_eq_filter_not, filter_replicate]
|
||||
split <;> simp_all
|
||||
|
||||
theorem take_takeWhile {l : List α} (p : α → Bool) n :
|
||||
(l.takeWhile p).take n = (l.takeWhile p).take n := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons x xs ih =>
|
||||
by_cases h : p x <;> simp [takeWhile_cons, h, ih]
|
||||
|
||||
@[simp] theorem all_takeWhile {l : List α} : (l.takeWhile p).all p = true := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons h t ih => by_cases p h <;> simp_all
|
||||
|
||||
@[simp] theorem any_dropWhile {l : List α} :
|
||||
(l.dropWhile p).any (fun x => !p x) = !l.all p := by
|
||||
induction l with
|
||||
| nil => rfl
|
||||
| cons h t ih => by_cases p h <;> simp_all
|
||||
|
||||
/-! ### rotateLeft -/
|
||||
|
||||
@[simp] theorem rotateLeft_replicate (n) (a : α) : rotateLeft (replicate m a) n = replicate m a := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
suffices 1 < m → m - (n + 1) % m + min ((n + 1) % m) m = m by
|
||||
simpa [rotateLeft]
|
||||
intro h
|
||||
rw [Nat.min_eq_left (Nat.le_of_lt (Nat.mod_lt _ (by omega)))]
|
||||
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
|
||||
omega
|
||||
@[simp] theorem rotateLeft_zero (l : List α) : rotateLeft l 0 = l := by
|
||||
simp [rotateLeft]
|
||||
|
||||
-- TODO Batteries defines its own `getElem?_rotate`, which we need to adapt.
|
||||
-- TODO Prove `map_rotateLeft`, using `ext` and `getElem?_rotateLeft`.
|
||||
|
||||
/-! ### rotateRight -/
|
||||
|
||||
@[simp] theorem rotateRight_replicate (n) (a : α) : rotateRight (replicate m a) n = replicate m a := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
suffices 1 < m → m - (m - (n + 1) % m) + min (m - (n + 1) % m) m = m by
|
||||
simpa [rotateRight]
|
||||
intro h
|
||||
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
|
||||
rw [Nat.min_eq_left (by omega)]
|
||||
omega
|
||||
@[simp] theorem rotateRight_zero (l : List α) : rotateRight l 0 = l := by
|
||||
simp [rotateRight]
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
@[simp] theorem length_zipWith (f : α → β → γ) (l₁ l₂) :
|
||||
length (zipWith f l₁ l₂) = min (length l₁) (length l₂) := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;>
|
||||
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
|
||||
|
||||
theorem zipWith_eq_zipWith_take_min : ∀ (l₁ : List α) (l₂ : List β),
|
||||
zipWith f l₁ l₂ = zipWith f (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
|
||||
| [], _ => by simp
|
||||
| _, [] => by simp
|
||||
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zipWith_eq_zipWith_take_min l₁ l₂]
|
||||
|
||||
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
|
||||
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
|
||||
rw [zipWith_eq_zipWith_take_min]
|
||||
simp
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
@[simp] theorem length_zip (l₁ : List α) (l₂ : List β) :
|
||||
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
|
||||
simp [zip]
|
||||
|
||||
theorem zip_eq_zip_take_min : ∀ (l₁ : List α) (l₂ : List β),
|
||||
zip l₁ l₂ = zip (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
|
||||
| [], _ => by simp
|
||||
| _, [] => by simp
|
||||
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zip_eq_zip_take_min l₁ l₂]
|
||||
|
||||
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
|
||||
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
|
||||
rw [zip_eq_zip_take_min]
|
||||
simp
|
||||
|
||||
/-! ### minimum? -/
|
||||
|
||||
-- A specialization of `minimum?_eq_some_iff` to Nat.
|
||||
theorem minimum?_eq_some_iff' {xs : List Nat} :
|
||||
xs.minimum? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, a ≤ b) :=
|
||||
minimum?_eq_some_iff
|
||||
(le_refl := Nat.le_refl)
|
||||
(min_eq_or := fun _ _ => by omega)
|
||||
(le_min_iff := fun _ _ _ => by omega)
|
||||
|
||||
-- This could be generalized,
|
||||
-- but will first require further work on order typeclasses in the core repository.
|
||||
theorem minimum?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).minimum? = some (match l.minimum? with
|
||||
| none => a
|
||||
| some m => min a m) := by
|
||||
rw [minimum?_eq_some_iff']
|
||||
split <;> rename_i h m
|
||||
· simp_all
|
||||
· rw [minimum?_eq_some_iff'] at m
|
||||
obtain ⟨m, le⟩ := m
|
||||
rw [Nat.min_def]
|
||||
constructor
|
||||
· split
|
||||
· exact mem_cons_self a l
|
||||
· exact mem_cons_of_mem a m
|
||||
· intro b m
|
||||
cases List.mem_cons.1 m with
|
||||
| inl => split <;> omega
|
||||
| inr h =>
|
||||
specialize le b h
|
||||
split <;> omega
|
||||
|
||||
/-! ### maximum? -/
|
||||
|
||||
-- A specialization of `maximum?_eq_some_iff` to Nat.
|
||||
theorem maximum?_eq_some_iff' {xs : List Nat} :
|
||||
xs.maximum? = some a ↔ (a ∈ xs ∧ ∀ b ∈ xs, b ≤ a) :=
|
||||
maximum?_eq_some_iff
|
||||
(le_refl := Nat.le_refl)
|
||||
(max_eq_or := fun _ _ => by omega)
|
||||
(max_le_iff := fun _ _ _ => by omega)
|
||||
|
||||
-- This could be generalized,
|
||||
-- but will first require further work on order typeclasses in the core repository.
|
||||
theorem maximum?_cons' {a : Nat} {l : List Nat} :
|
||||
(a :: l).maximum? = some (match l.maximum? with
|
||||
| none => a
|
||||
| some m => max a m) := by
|
||||
rw [maximum?_eq_some_iff']
|
||||
split <;> rename_i h m
|
||||
· simp_all
|
||||
· rw [maximum?_eq_some_iff'] at m
|
||||
obtain ⟨m, le⟩ := m
|
||||
rw [Nat.max_def]
|
||||
constructor
|
||||
· split
|
||||
· exact mem_cons_of_mem a m
|
||||
· exact mem_cons_self a l
|
||||
· intro b m
|
||||
cases List.mem_cons.1 m with
|
||||
| inl => split <;> omega
|
||||
| inr h =>
|
||||
specialize le b h
|
||||
split <;> omega
|
||||
-- TODO Batteries defines its own `getElem?_rotate`, which we need to adapt.
|
||||
-- TODO Prove `map_rotateRight`, using `ext` and `getElem?_rotateRight`.
|
||||
|
||||
end List
|
||||
|
||||
363
src/Init/Data/List/Zip.lean
Normal file
363
src/Init/Data/List/Zip.lean
Normal file
@@ -0,0 +1,363 @@
|
||||
/-
|
||||
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.TakeDrop
|
||||
|
||||
/-!
|
||||
# Lemmas about `List.zip`, `List.zipWith`, `List.zipWithAll`, and `List.unzip`.
|
||||
-/
|
||||
|
||||
namespace List
|
||||
|
||||
open Nat
|
||||
|
||||
/-! ## Zippers -/
|
||||
|
||||
/-! ### zip -/
|
||||
|
||||
theorem zip_map (f : α → γ) (g : β → δ) :
|
||||
∀ (l₁ : List α) (l₂ : List β), zip (l₁.map f) (l₂.map g) = (zip l₁ l₂).map (Prod.map f g)
|
||||
| [], l₂ => rfl
|
||||
| l₁, [] => by simp only [map, zip_nil_right]
|
||||
| a :: l₁, b :: l₂ => by
|
||||
simp only [map, zip_cons_cons, zip_map, Prod.map]; constructor
|
||||
|
||||
theorem zip_map_left (f : α → γ) (l₁ : List α) (l₂ : List β) :
|
||||
zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id) := by rw [← zip_map, map_id]
|
||||
|
||||
theorem zip_map_right (f : β → γ) (l₁ : List α) (l₂ : List β) :
|
||||
zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f) := by rw [← zip_map, map_id]
|
||||
|
||||
theorem zip_append :
|
||||
∀ {l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
|
||||
zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
|
||||
| [], r₁, l₂, r₂, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
|
||||
| l₁, r₁, [], r₂, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
|
||||
| a :: l₁, r₁, b :: l₂, r₂, h => by
|
||||
simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
|
||||
|
||||
theorem zip_map' (f : α → β) (g : α → γ) :
|
||||
∀ l : List α, zip (l.map f) (l.map g) = l.map fun a => (f a, g a)
|
||||
| [] => rfl
|
||||
| a :: l => by simp only [map, zip_cons_cons, zip_map']
|
||||
|
||||
theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip l₁ l₂ → a ∈ l₁ ∧ b ∈ l₂
|
||||
| _ :: l₁, _ :: l₂, h => by
|
||||
cases h
|
||||
case head => simp
|
||||
case tail h =>
|
||||
· have := of_mem_zip h
|
||||
exact ⟨Mem.tail _ this.1, Mem.tail _ this.2⟩
|
||||
|
||||
@[deprecated of_mem_zip (since := "2024-07-28")] abbrev mem_zip := @of_mem_zip
|
||||
|
||||
theorem map_fst_zip :
|
||||
∀ (l₁ : List α) (l₂ : List β), l₁.length ≤ l₂.length → map Prod.fst (zip l₁ l₂) = l₁
|
||||
| [], bs, _ => rfl
|
||||
| _ :: as, _ :: bs, h => by
|
||||
simp [Nat.succ_le_succ_iff] at h
|
||||
show _ :: map Prod.fst (zip as bs) = _ :: as
|
||||
rw [map_fst_zip as bs h]
|
||||
| a :: as, [], h => by simp at h
|
||||
|
||||
theorem map_snd_zip :
|
||||
∀ (l₁ : List α) (l₂ : List β), l₂.length ≤ l₁.length → map Prod.snd (zip l₁ l₂) = l₂
|
||||
| _, [], _ => by
|
||||
rw [zip_nil_right]
|
||||
rfl
|
||||
| [], b :: bs, h => by simp at h
|
||||
| a :: as, b :: bs, h => by
|
||||
simp [Nat.succ_le_succ_iff] at h
|
||||
show _ :: map Prod.snd (zip as bs) = _ :: bs
|
||||
rw [map_snd_zip as bs h]
|
||||
|
||||
theorem map_prod_left_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (x, f x)) = l.zip (l.map f) := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
exact map_id _
|
||||
|
||||
theorem map_prod_right_eq_zip {l : List α} (f : α → β) :
|
||||
(l.map fun x => (f x, x)) = (l.map f).zip l := by
|
||||
rw [← zip_map']
|
||||
congr
|
||||
exact map_id _
|
||||
|
||||
/-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
|
||||
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :
|
||||
zip (replicate n a) (replicate n b) = replicate n (a, b) := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => simp [replicate_succ, ih]
|
||||
|
||||
/-! ### zipWith -/
|
||||
|
||||
theorem zipWith_comm (f : α → β → γ) :
|
||||
∀ (la : List α) (lb : List β), zipWith f la lb = zipWith (fun b a => f a b) lb la
|
||||
| [], _ => List.zipWith_nil_right.symm
|
||||
| _ :: _, [] => rfl
|
||||
| _ :: as, _ :: bs => congrArg _ (zipWith_comm f as bs)
|
||||
|
||||
theorem zipWith_comm_of_comm (f : α → α → β) (comm : ∀ x y : α, f x y = f y x) (l l' : List α) :
|
||||
zipWith f l l' = zipWith f l' l := by
|
||||
rw [zipWith_comm]
|
||||
simp only [comm]
|
||||
|
||||
@[simp]
|
||||
theorem zipWith_same (f : α → α → δ) : ∀ l : List α, zipWith f l l = l.map fun a => f a a
|
||||
| [] => rfl
|
||||
| _ :: xs => congrArg _ (zipWith_same f xs)
|
||||
|
||||
/--
|
||||
See also `getElem?_zipWith'` for a variant
|
||||
using `Option.map` and `Option.bind` rather than a `match`.
|
||||
-/
|
||||
theorem getElem?_zipWith {f : α → β → γ} {i : Nat} :
|
||||
(List.zipWith f as bs)[i]? = match as[i]?, bs[i]? with
|
||||
| some a, some b => some (f a b) | _, _ => none := by
|
||||
induction as generalizing bs i with
|
||||
| nil => cases bs with
|
||||
| nil => simp
|
||||
| cons b bs => simp
|
||||
| cons a as aih => cases bs with
|
||||
| nil => simp
|
||||
| cons b bs => cases i <;> simp_all
|
||||
|
||||
/-- Variant of `getElem?_zipWith` using `Option.map` and `Option.bind` rather than a `match`. -/
|
||||
theorem getElem?_zipWith' {f : α → β → γ} {i : Nat} :
|
||||
(zipWith f l₁ l₂)[i]? = (l₁[i]?.map f).bind fun g => l₂[i]?.map g := by
|
||||
induction l₁ generalizing l₂ i with
|
||||
| nil => rw [zipWith] <;> simp
|
||||
| cons head tail =>
|
||||
cases l₂
|
||||
· simp
|
||||
· cases i <;> simp_all
|
||||
|
||||
theorem getElem?_zipWith_eq_some (f : α → β → γ) (l₁ : List α) (l₂ : List β) (z : γ) (i : Nat) :
|
||||
(zipWith f l₁ l₂)[i]? = some z ↔
|
||||
∃ x y, l₁[i]? = some x ∧ l₂[i]? = some y ∧ f x y = z := by
|
||||
induction l₁ generalizing l₂ i
|
||||
· simp
|
||||
· cases l₂ <;> cases i <;> simp_all
|
||||
|
||||
theorem getElem?_zip_eq_some (l₁ : List α) (l₂ : List β) (z : α × β) (i : Nat) :
|
||||
(zip l₁ l₂)[i]? = some z ↔ l₁[i]? = some z.1 ∧ l₂[i]? = some z.2 := by
|
||||
cases z
|
||||
rw [zip, getElem?_zipWith_eq_some]; constructor
|
||||
· rintro ⟨x, y, h₀, h₁, h₂⟩
|
||||
simpa [h₀, h₁] using h₂
|
||||
· rintro ⟨h₀, h₁⟩
|
||||
exact ⟨_, _, h₀, h₁, rfl⟩
|
||||
|
||||
@[deprecated getElem?_zipWith (since := "2024-06-12")]
|
||||
theorem get?_zipWith {f : α → β → γ} :
|
||||
(List.zipWith f as bs).get? i = match as.get? i, bs.get? i with
|
||||
| some a, some b => some (f a b) | _, _ => none := by
|
||||
simp [getElem?_zipWith]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem?_zipWith (since := "2024-06-07")] abbrev zipWith_get? := @get?_zipWith
|
||||
|
||||
theorem head?_zipWith {f : α → β → γ} :
|
||||
(List.zipWith f as bs).head? = match as.head?, bs.head? with
|
||||
| some a, some b => some (f a b) | _, _ => none := by
|
||||
simp [head?_eq_getElem?, getElem?_zipWith]
|
||||
|
||||
theorem head_zipWith {f : α → β → γ} (h):
|
||||
(List.zipWith f as bs).head h = f (as.head (by rintro rfl; simp_all)) (bs.head (by rintro rfl; simp_all)) := by
|
||||
apply Option.some.inj
|
||||
rw [← head?_eq_head, head?_zipWith, head?_eq_head, head?_eq_head]
|
||||
|
||||
@[simp]
|
||||
theorem zipWith_map {μ} (f : γ → δ → μ) (g : α → γ) (h : β → δ) (l₁ : List α) (l₂ : List β) :
|
||||
zipWith f (l₁.map g) (l₂.map h) = zipWith (fun a b => f (g a) (h b)) l₁ l₂ := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
theorem zipWith_map_left (l₁ : List α) (l₂ : List β) (f : α → α') (g : α' → β → γ) :
|
||||
zipWith g (l₁.map f) l₂ = zipWith (fun a b => g (f a) b) l₁ l₂ := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
theorem zipWith_map_right (l₁ : List α) (l₂ : List β) (f : β → β') (g : α → β' → γ) :
|
||||
zipWith g l₁ (l₂.map f) = zipWith (fun a b => g a (f b)) l₁ l₂ := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
theorem zipWith_foldr_eq_zip_foldr {f : α → β → γ} (i : δ):
|
||||
(zipWith f l₁ l₂).foldr g i = (zip l₁ l₂).foldr (fun p r => g (f p.1 p.2) r) i := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} (i : δ):
|
||||
(zipWith f l₁ l₂).foldl g i = (zip l₁ l₂).foldl (fun r p => g r (f p.1 p.2)) i := by
|
||||
induction l₁ generalizing i l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem zipWith_eq_nil_iff {f : α → β → γ} {l l'} : zipWith f l l' = [] ↔ l = [] ∨ l' = [] := by
|
||||
cases l <;> cases l' <;> simp
|
||||
|
||||
theorem map_zipWith {δ : Type _} (f : α → β) (g : γ → δ → α) (l : List γ) (l' : List δ) :
|
||||
map f (zipWith g l l') = zipWith (fun x y => f (g x y)) l l' := by
|
||||
induction l generalizing l' with
|
||||
| nil => simp
|
||||
| cons hd tl hl =>
|
||||
· cases l'
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
theorem take_zipWith : (zipWith f l l').take n = zipWith f (l.take n) (l'.take n) := by
|
||||
induction l generalizing l' n with
|
||||
| nil => simp
|
||||
| cons hd tl hl =>
|
||||
cases l'
|
||||
· simp
|
||||
· cases n
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
@[deprecated take_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_take := @take_zipWith
|
||||
|
||||
theorem drop_zipWith : (zipWith f l l').drop n = zipWith f (l.drop n) (l'.drop n) := by
|
||||
induction l generalizing l' n with
|
||||
| nil => simp
|
||||
| cons hd tl hl =>
|
||||
· cases l'
|
||||
· simp
|
||||
· cases n
|
||||
· simp
|
||||
· simp [hl]
|
||||
|
||||
@[deprecated drop_zipWith (since := "2024-07-26")] abbrev zipWith_distrib_drop := @drop_zipWith
|
||||
|
||||
theorem tail_zipWith : (zipWith f l l').tail = zipWith f l.tail l'.tail := by
|
||||
rw [← drop_one]; simp [drop_zipWith]
|
||||
|
||||
@[deprecated tail_zipWith (since := "2024-07-28")] abbrev zipWith_distrib_tail := @tail_zipWith
|
||||
|
||||
theorem zipWith_append (f : α → β → γ) (l la : List α) (l' lb : List β)
|
||||
(h : l.length = l'.length) :
|
||||
zipWith f (l ++ la) (l' ++ lb) = zipWith f l l' ++ zipWith f la lb := by
|
||||
induction l generalizing l' with
|
||||
| nil =>
|
||||
have : l' = [] := eq_nil_of_length_eq_zero (by simpa using h.symm)
|
||||
simp [this]
|
||||
| cons hl tl ih =>
|
||||
cases l' with
|
||||
| nil => simp at h
|
||||
| cons head tail =>
|
||||
simp only [length_cons, Nat.succ.injEq] at h
|
||||
simp [ih _ h]
|
||||
|
||||
/-- See also `List.zipWith_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
|
||||
@[simp] theorem zipWith_replicate' {a : α} {b : β} {n : Nat} :
|
||||
zipWith f (replicate n a) (replicate n b) = replicate n (f a b) := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => simp [replicate_succ, ih]
|
||||
|
||||
/-! ### zipWithAll -/
|
||||
|
||||
theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
|
||||
(zipWithAll f as bs)[i]? = match as[i]?, bs[i]? with
|
||||
| none, none => .none | a?, b? => some (f a? b?) := by
|
||||
induction as generalizing bs i with
|
||||
| nil => induction bs generalizing i with
|
||||
| nil => simp
|
||||
| cons b bs bih => cases i <;> simp_all
|
||||
| cons a as aih => cases bs with
|
||||
| nil =>
|
||||
specialize @aih []
|
||||
cases i <;> simp_all
|
||||
| cons b bs => cases i <;> simp_all
|
||||
|
||||
@[deprecated getElem?_zipWithAll (since := "2024-06-12")]
|
||||
theorem get?_zipWithAll {f : Option α → Option β → γ} :
|
||||
(zipWithAll f as bs).get? i = match as.get? i, bs.get? i with
|
||||
| none, none => .none | a?, b? => some (f a? b?) := by
|
||||
simp [getElem?_zipWithAll]
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated getElem?_zipWithAll (since := "2024-06-07")] abbrev zipWithAll_get? := @get?_zipWithAll
|
||||
|
||||
theorem head?_zipWithAll {f : Option α → Option β → γ} :
|
||||
(zipWithAll f as bs).head? = match as.head?, bs.head? with
|
||||
| none, none => .none | a?, b? => some (f a? b?) := by
|
||||
simp [head?_eq_getElem?, getElem?_zipWithAll]
|
||||
|
||||
theorem head_zipWithAll {f : Option α → Option β → γ} (h) :
|
||||
(zipWithAll f as bs).head h = f as.head? bs.head? := by
|
||||
apply Option.some.inj
|
||||
rw [← head?_eq_head, head?_zipWithAll]
|
||||
split <;> simp_all
|
||||
|
||||
theorem zipWithAll_map {μ} (f : Option γ → Option δ → μ) (g : α → γ) (h : β → δ) (l₁ : List α) (l₂ : List β) :
|
||||
zipWithAll f (l₁.map g) (l₂.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) l₁ l₂ := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
theorem zipWithAll_map_left (l₁ : List α) (l₂ : List β) (f : α → α') (g : Option α' → Option β → γ) :
|
||||
zipWithAll g (l₁.map f) l₂ = zipWithAll (fun a b => g (f <$> a) b) l₁ l₂ := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
theorem zipWithAll_map_right (l₁ : List α) (l₂ : List β) (f : β → β') (g : Option α → Option β' → γ) :
|
||||
zipWithAll g l₁ (l₂.map f) = zipWithAll (fun a b => g a (f <$> b)) l₁ l₂ := by
|
||||
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
|
||||
|
||||
theorem map_zipWithAll {δ : Type _} (f : α → β) (g : Option γ → Option δ → α) (l : List γ) (l' : List δ) :
|
||||
map f (zipWithAll g l l') = zipWithAll (fun x y => f (g x y)) l l' := by
|
||||
induction l generalizing l' with
|
||||
| nil => simp
|
||||
| cons hd tl hl =>
|
||||
cases l' <;> simp_all
|
||||
|
||||
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
|
||||
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
|
||||
induction n with
|
||||
| zero => rfl
|
||||
| succ n ih => simp [replicate_succ, ih]
|
||||
|
||||
/-! ### unzip -/
|
||||
|
||||
@[simp] theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp] theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[deprecated unzip_fst (since := "2024-07-28")] abbrev unzip_left := @unzip_fst
|
||||
@[deprecated unzip_snd (since := "2024-07-28")] abbrev unzip_right := @unzip_snd
|
||||
|
||||
theorem unzip_eq_map : ∀ l : List (α × β), unzip l = (l.map Prod.fst, l.map Prod.snd)
|
||||
| [] => rfl
|
||||
| (a, b) :: l => by simp only [unzip_cons, map_cons, unzip_eq_map l]
|
||||
|
||||
theorem zip_unzip : ∀ l : List (α × β), zip (unzip l).1 (unzip l).2 = l
|
||||
| [] => rfl
|
||||
| (a, b) :: l => by simp only [unzip_cons, zip_cons_cons, zip_unzip l]
|
||||
|
||||
theorem unzip_zip_left :
|
||||
∀ {l₁ : List α} {l₂ : List β}, length l₁ ≤ length l₂ → (unzip (zip l₁ l₂)).1 = l₁
|
||||
| [], l₂, _ => rfl
|
||||
| l₁, [], h => by rw [eq_nil_of_length_eq_zero (Nat.eq_zero_of_le_zero h)]; rfl
|
||||
| a :: l₁, b :: l₂, h => by
|
||||
simp only [zip_cons_cons, unzip_cons, unzip_zip_left (le_of_succ_le_succ h)]
|
||||
|
||||
theorem unzip_zip_right :
|
||||
∀ {l₁ : List α} {l₂ : List β}, length l₂ ≤ length l₁ → (unzip (zip l₁ l₂)).2 = l₂
|
||||
| [], l₂, _ => by simp_all
|
||||
| l₁, [], _ => by simp
|
||||
| a :: l₁, b :: l₂, h => by
|
||||
simp only [zip_cons_cons, unzip_cons, unzip_zip_right (le_of_succ_le_succ h)]
|
||||
|
||||
theorem unzip_zip {l₁ : List α} {l₂ : List β} (h : length l₁ = length l₂) :
|
||||
unzip (zip l₁ l₂) = (l₁, l₂) := by
|
||||
ext
|
||||
· rw [unzip_zip_left (Nat.le_of_eq h)]
|
||||
· rw [unzip_zip_right (Nat.le_of_eq h.symm)]
|
||||
|
||||
theorem zip_of_prod {l : List α} {l' : List β} {lp : List (α × β)} (hl : lp.map Prod.fst = l)
|
||||
(hr : lp.map Prod.snd = l') : lp = l.zip l' := by
|
||||
rw [← hl, ← hr, ← zip_unzip lp, ← unzip_fst, ← unzip_snd, zip_unzip, zip_unzip]
|
||||
|
||||
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
|
||||
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
|
||||
ext1 <;> simp
|
||||
@@ -102,6 +102,13 @@ def blt (a b : Nat) : Bool :=
|
||||
attribute [simp] Nat.zero_le
|
||||
attribute [simp] Nat.not_lt_zero
|
||||
|
||||
theorem and_forall_add_one {p : Nat → Prop} : p 0 ∧ (∀ n, p (n + 1)) ↔ ∀ n, p n :=
|
||||
⟨fun h n => Nat.casesOn n h.1 h.2, fun h => ⟨h _, fun _ => h _⟩⟩
|
||||
|
||||
theorem or_exists_add_one : p 0 ∨ (Exists fun n => p (n + 1)) ↔ Exists p :=
|
||||
⟨fun h => h.elim (fun h0 => ⟨0, h0⟩) fun ⟨n, hn⟩ => ⟨n + 1, hn⟩,
|
||||
fun ⟨n, h⟩ => match n with | 0 => Or.inl h | n+1 => Or.inr ⟨n, h⟩⟩
|
||||
|
||||
/-! # Helper "packing" theorems -/
|
||||
|
||||
@[simp] theorem zero_eq : Nat.zero = 0 := rfl
|
||||
@@ -388,11 +395,11 @@ theorem le_or_eq_of_le_succ {m n : Nat} (h : m ≤ succ n) : m ≤ n ∨ m = suc
|
||||
theorem le_or_eq_of_le_add_one {m n : Nat} (h : m ≤ n + 1) : m ≤ n ∨ m = n + 1 :=
|
||||
le_or_eq_of_le_succ h
|
||||
|
||||
theorem le_add_right : ∀ (n k : Nat), n ≤ n + k
|
||||
@[simp] theorem le_add_right : ∀ (n k : Nat), n ≤ n + k
|
||||
| n, 0 => Nat.le_refl n
|
||||
| n, k+1 => le_succ_of_le (le_add_right n k)
|
||||
|
||||
theorem le_add_left (n m : Nat): n ≤ m + n :=
|
||||
@[simp] theorem le_add_left (n m : Nat): n ≤ m + n :=
|
||||
Nat.add_comm n m ▸ le_add_right n m
|
||||
|
||||
theorem le_of_add_right_le {n m k : Nat} (h : n + k ≤ m) : n ≤ m :=
|
||||
@@ -528,7 +535,7 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
|
||||
rw [Nat.add_comm _ b, Nat.add_comm _ b]
|
||||
apply Nat.le_of_add_le_add_left
|
||||
|
||||
protected theorem add_le_add_iff_right {n : Nat} : m + n ≤ k + n ↔ m ≤ k :=
|
||||
@[simp] protected theorem add_le_add_iff_right {n : Nat} : m + n ≤ k + n ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_right, fun h => Nat.add_le_add_right h _⟩
|
||||
|
||||
/-! ### le/lt -/
|
||||
@@ -634,6 +641,10 @@ theorem succ_lt_succ_iff : succ a < succ b ↔ a < b := ⟨lt_of_succ_lt_succ, s
|
||||
|
||||
theorem add_one_inj : a + 1 = b + 1 ↔ a = b := succ_inj'
|
||||
|
||||
theorem ne_add_one (n : Nat) : n ≠ n + 1 := fun h => by cases h
|
||||
|
||||
theorem add_one_ne (n : Nat) : n + 1 ≠ n := fun h => by cases h
|
||||
|
||||
theorem add_one_le_add_one_iff : a + 1 ≤ b + 1 ↔ a ≤ b := succ_le_succ_iff
|
||||
|
||||
theorem add_one_lt_add_one_iff : a + 1 < b + 1 ↔ a < b := succ_lt_succ_iff
|
||||
@@ -815,6 +826,9 @@ protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
|
||||
@[simp] protected theorem zero_sub_one : 0 - 1 = 0 := rfl
|
||||
@[simp] protected theorem add_one_sub_one (n : Nat) : n + 1 - 1 = n := rfl
|
||||
|
||||
theorem sub_one_eq_self (n : Nat) : n - 1 = n ↔ n = 0 := by cases n <;> simp [ne_add_one]
|
||||
theorem eq_self_sub_one (n : Nat) : n = n - 1 ↔ n = 0 := by cases n <;> simp [add_one_ne]
|
||||
|
||||
theorem succ_pred {a : Nat} (h : a ≠ 0) : a.pred.succ = a := by
|
||||
induction a with
|
||||
| zero => contradiction
|
||||
|
||||
@@ -265,8 +265,8 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
|
||||
have x_eq : x = y + 2^j := Nat.eq_add_of_sub_eq x_ge_j y_eq
|
||||
simp only [Nat.two_pow_pos, x_eq, Nat.le_add_left, true_and, ite_true]
|
||||
have y_lt_x : y < x := by
|
||||
simp [x_eq]
|
||||
exact Nat.lt_add_of_pos_right (Nat.two_pow_pos j)
|
||||
simp only [x_eq, Nat.lt_add_right_iff_pos]
|
||||
exact Nat.two_pow_pos j
|
||||
simp only [hyp y y_lt_x]
|
||||
if i_lt_j : i < j then
|
||||
rw [Nat.add_comm _ (2^_), testBit_two_pow_add_gt i_lt_j]
|
||||
|
||||
@@ -46,6 +46,9 @@ theorem gcd_succ (x y : Nat) : gcd (succ x) y = gcd (y % succ x) (succ x) := by
|
||||
theorem gcd_add_one (x y : Nat) : gcd (x + 1) y = gcd (y % (x + 1)) (x + 1) := by
|
||||
rw [gcd]; rfl
|
||||
|
||||
theorem gcd_def (x y : Nat) : gcd x y = if x = 0 then y else gcd (y % x) x := by
|
||||
cases x <;> simp [Nat.gcd_add_one]
|
||||
|
||||
@[simp] theorem gcd_one_left (n : Nat) : gcd 1 n = 1 := by
|
||||
rw [gcd_succ, mod_one]
|
||||
rfl
|
||||
|
||||
@@ -19,6 +19,14 @@ and later these lemmas should be organised into other files more systematically.
|
||||
-/
|
||||
|
||||
namespace Nat
|
||||
|
||||
@[deprecated and_forall_add_one (since := "2024-07-30")] abbrev and_forall_succ := @and_forall_add_one
|
||||
@[deprecated or_exists_add_one (since := "2024-07-30")] abbrev or_exists_succ := @or_exists_add_one
|
||||
|
||||
@[simp] theorem exists_ne_zero {P : Nat → Prop} : (∃ n, ¬ n = 0 ∧ P n) ↔ ∃ n, P (n + 1) :=
|
||||
⟨fun ⟨n, h, w⟩ => by cases n with | zero => simp at h | succ n => exact ⟨n, w⟩,
|
||||
fun ⟨n, w⟩ => ⟨n + 1, by simp, w⟩⟩
|
||||
|
||||
/-! ## add -/
|
||||
|
||||
protected theorem add_add_add_comm (a b c d : Nat) : (a + b) + (c + d) = (a + c) + (b + d) := by
|
||||
@@ -36,13 +44,13 @@ protected theorem eq_zero_of_add_eq_zero_right (h : n + m = 0) : n = 0 :=
|
||||
protected theorem add_eq_zero_iff : n + m = 0 ↔ n = 0 ∧ m = 0 :=
|
||||
⟨Nat.eq_zero_of_add_eq_zero, fun ⟨h₁, h₂⟩ => h₂.symm ▸ h₁⟩
|
||||
|
||||
protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k ↔ m = k :=
|
||||
@[simp] protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k ↔ m = k :=
|
||||
⟨Nat.add_left_cancel, fun | rfl => rfl⟩
|
||||
|
||||
protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n ↔ m = k :=
|
||||
@[simp] protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n ↔ m = k :=
|
||||
⟨Nat.add_right_cancel, fun | rfl => rfl⟩
|
||||
|
||||
protected theorem add_le_add_iff_left {n : Nat} : n + m ≤ n + k ↔ m ≤ k :=
|
||||
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m ≤ n + k ↔ m ≤ k :=
|
||||
⟨Nat.le_of_add_le_add_left, fun h => Nat.add_le_add_left h _⟩
|
||||
|
||||
protected theorem lt_of_add_lt_add_right : ∀ {n : Nat}, k + n < m + n → k < m
|
||||
@@ -52,10 +60,10 @@ protected theorem lt_of_add_lt_add_right : ∀ {n : Nat}, k + n < m + n → k <
|
||||
protected theorem lt_of_add_lt_add_left {n : Nat} : n + k < n + m → k < m := by
|
||||
rw [Nat.add_comm n, Nat.add_comm n]; exact Nat.lt_of_add_lt_add_right
|
||||
|
||||
protected theorem add_lt_add_iff_left {k n m : Nat} : k + n < k + m ↔ n < m :=
|
||||
@[simp] protected theorem add_lt_add_iff_left {k n m : Nat} : k + n < k + m ↔ n < m :=
|
||||
⟨Nat.lt_of_add_lt_add_left, fun h => Nat.add_lt_add_left h _⟩
|
||||
|
||||
protected theorem add_lt_add_iff_right {k n m : Nat} : n + k < m + k ↔ n < m :=
|
||||
@[simp] protected theorem add_lt_add_iff_right {k n m : Nat} : n + k < m + k ↔ n < m :=
|
||||
⟨Nat.lt_of_add_lt_add_right, fun h => Nat.add_lt_add_right h _⟩
|
||||
|
||||
protected theorem add_lt_add_of_le_of_lt {a b c d : Nat} (hle : a ≤ b) (hlt : c < d) :
|
||||
@@ -75,10 +83,10 @@ protected theorem pos_of_lt_add_right (h : n < n + k) : 0 < k :=
|
||||
protected theorem pos_of_lt_add_left : n < k + n → 0 < k := by
|
||||
rw [Nat.add_comm]; exact Nat.pos_of_lt_add_right
|
||||
|
||||
protected theorem lt_add_right_iff_pos : n < n + k ↔ 0 < k :=
|
||||
@[simp] protected theorem lt_add_right_iff_pos : n < n + k ↔ 0 < k :=
|
||||
⟨Nat.pos_of_lt_add_right, Nat.lt_add_of_pos_right⟩
|
||||
|
||||
protected theorem lt_add_left_iff_pos : n < k + n ↔ 0 < k :=
|
||||
@[simp] protected theorem lt_add_left_iff_pos : n < k + n ↔ 0 < k :=
|
||||
⟨Nat.pos_of_lt_add_left, Nat.lt_add_of_pos_left⟩
|
||||
|
||||
protected theorem add_pos_left (h : 0 < m) (n) : 0 < m + n :=
|
||||
|
||||
@@ -173,13 +173,13 @@ instance : LawfulBEq PolyCnstr where
|
||||
eq_of_beq {a b} h := by
|
||||
cases a; rename_i eq₁ lhs₁ rhs₁
|
||||
cases b; rename_i eq₂ lhs₂ rhs₂
|
||||
have h : eq₁ == eq₂ && lhs₁ == lhs₂ && rhs₁ == rhs₂ := h
|
||||
have h : eq₁ == eq₂ && (lhs₁ == lhs₂ && rhs₁ == rhs₂) := h
|
||||
simp at h
|
||||
have ⟨⟨h₁, h₂⟩, h₃⟩ := h
|
||||
have ⟨h₁, h₂, h₃⟩ := h
|
||||
rw [h₁, h₂, h₃]
|
||||
rfl {a} := by
|
||||
cases a; rename_i eq lhs rhs
|
||||
show (eq == eq && lhs == lhs && rhs == rhs) = true
|
||||
show (eq == eq && (lhs == lhs && rhs == rhs)) = true
|
||||
simp [LawfulBEq.rfl]
|
||||
|
||||
def PolyCnstr.mul (k : Nat) (c : PolyCnstr) : PolyCnstr :=
|
||||
|
||||
@@ -212,6 +212,9 @@ instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
|
||||
@[simp] theorem all_none : Option.all p none = true := rfl
|
||||
@[simp] theorem all_some : Option.all p (some x) = p x := rfl
|
||||
|
||||
@[simp] theorem any_none : Option.any p none = false := rfl
|
||||
@[simp] theorem any_some : Option.any p (some x) = p x := rfl
|
||||
|
||||
/-- The minimum of two optional values. -/
|
||||
protected def min [Min α] : Option α → Option α → Option α
|
||||
| some x, some y => some (Min.min x y)
|
||||
|
||||
@@ -190,6 +190,19 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
|
||||
|
||||
theorem mem_map_of_mem (g : α → β) (h : a ∈ x) : g a ∈ Option.map g x := h.symm ▸ map_some' ..
|
||||
|
||||
@[simp] theorem filter_none (p : α → Bool) : none.filter p = none := rfl
|
||||
theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
|
||||
|
||||
@[simp] theorem all_guard (p : α → Prop) [DecidablePred p] (a : α) :
|
||||
Option.all q (guard p a) = (!p a || q a) := by
|
||||
simp only [guard]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem any_guard (p : α → Prop) [DecidablePred p] (a : α) :
|
||||
Option.any q (guard p a) = (p a && q a) := by
|
||||
simp only [guard]
|
||||
split <;> simp_all
|
||||
|
||||
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α → β} :
|
||||
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ protected def Int.repr : Int → String
|
||||
| negSucc m => "-" ++ Nat.repr (succ m)
|
||||
|
||||
instance : Repr Int where
|
||||
reprPrec i _ := i.repr
|
||||
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
|
||||
|
||||
def hexDigitRepr (n : Nat) : String :=
|
||||
String.singleton <| Nat.digitChar n
|
||||
|
||||
27
src/Init/Data/Subtype.lean
Normal file
27
src/Init/Data/Subtype.lean
Normal file
@@ -0,0 +1,27 @@
|
||||
/-
|
||||
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Johannes Hölzl
|
||||
-/
|
||||
prelude
|
||||
import Init.Ext
|
||||
|
||||
namespace Subtype
|
||||
|
||||
universe u
|
||||
variable {α : Sort u} {p q : α → Prop}
|
||||
|
||||
@[ext]
|
||||
protected theorem ext : ∀ {a1 a2 : { x // p x }}, (a1 : α) = (a2 : α) → a1 = a2
|
||||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||||
|
||||
@[simp]
|
||||
protected theorem «forall» {q : { a // p a } → Prop} : (∀ x, q x) ↔ ∀ a b, q ⟨a, b⟩ :=
|
||||
⟨fun h a b ↦ h ⟨a, b⟩, fun h ⟨a, b⟩ ↦ h a b⟩
|
||||
|
||||
@[simp]
|
||||
protected theorem «exists» {q : { a // p a } → Prop} :
|
||||
(Exists fun x => q x) ↔ Exists fun a => Exists fun b => q ⟨a, b⟩ :=
|
||||
⟨fun ⟨⟨a, b⟩, h⟩ ↦ ⟨a, b, h⟩, fun ⟨a, b, h⟩ ↦ ⟨⟨a, b⟩, h⟩⟩
|
||||
|
||||
end Subtype
|
||||
@@ -24,15 +24,16 @@ syntax extFlat := atomic("(" &"flat" " := " &"false" ")")
|
||||
/--
|
||||
Registers an extensionality theorem.
|
||||
|
||||
* When `@[ext]` is applied to a structure, it generates `.ext` and `.ext_iff` theorems and registers
|
||||
them for the `ext` tactic.
|
||||
|
||||
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic, and it generates an `ext_iff` theorem.
|
||||
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic, and it generates an "`ext_iff`" theorem.
|
||||
The name of the theorem is from adding the suffix `_iff` to the theorem name.
|
||||
|
||||
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the lemma. Higher-priority lemmas are chosen first, and the default is `1000`.
|
||||
* When `@[ext]` is applied to a structure, it generates an `.ext` theorem and applies the `@[ext]` attribute to it.
|
||||
The result is an `.ext` and an `.ext_iff` theorem with the `.ext` theorem registered for the `ext` tactic.
|
||||
|
||||
* The flag `@[ext (iff := false)]` prevents it from generating an `ext_iff` theorem.
|
||||
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the `ext` lemma.
|
||||
Higher-priority lemmas are chosen first, and the default is `1000`.
|
||||
|
||||
* The flag `@[ext (iff := false)]` disables generating an `ext_iff` theorem.
|
||||
|
||||
* The flag `@[ext (flat := false)]` causes generated structure extensionality theorems to show inherited fields based on their representation,
|
||||
rather than flattening the parents' fields into the lemma's equality hypotheses.
|
||||
|
||||
@@ -399,9 +399,16 @@ def setTailInfo (stx : Syntax) (info : SourceInfo) : Syntax :=
|
||||
| some stx => stx
|
||||
| none => stx
|
||||
|
||||
/--
|
||||
Replaces the trailing whitespace in `stx`, if any, with an empty substring.
|
||||
|
||||
The trailing substring's `startPos` and `str` are preserved in order to ensure that the result could
|
||||
have been produced by the parser, in case any syntax consumers rely on such an assumption.
|
||||
-/
|
||||
def unsetTrailing (stx : Syntax) : Syntax :=
|
||||
match stx.getTailInfo with
|
||||
| SourceInfo.original lead pos _ endPos => stx.setTailInfo (SourceInfo.original lead pos "".toSubstring endPos)
|
||||
| SourceInfo.original lead pos trail endPos =>
|
||||
stx.setTailInfo (SourceInfo.original lead pos { trail with stopPos := trail.startPos } endPos)
|
||||
| _ => stx
|
||||
|
||||
@[specialize] private partial def updateFirst {α} [Inhabited α] (a : Array α) (f : α → Option α) (i : Nat) : Option (Array α) :=
|
||||
|
||||
@@ -219,13 +219,13 @@ structure Config where
|
||||
-/
|
||||
index : Bool := true
|
||||
/--
|
||||
When `true` (default: `false`), `simp` will **not** create a proof for a rewriting rule associated
|
||||
When `true` (default: `true`), `simp` will **not** create a proof for a rewriting rule associated
|
||||
with an `rfl`-theorem.
|
||||
Rewriting rules are provided by users by annotating theorems with the attribute `@[simp]`.
|
||||
If the proof of the theorem is just `rfl` (reflexivity), and `implicitDefEqProofs := true`, `simp`
|
||||
will **not** create a proof term which is an application of the annotated theorem.
|
||||
-/
|
||||
implicitDefEqProofs : Bool := false
|
||||
implicitDefEqProofs : Bool := true
|
||||
deriving Inhabited, BEq
|
||||
|
||||
-- Configuration object for `simp_all`
|
||||
|
||||
@@ -267,6 +267,7 @@ syntax (name := rawNatLit) "nat_lit " num : term
|
||||
|
||||
@[inherit_doc] infixr:90 " ∘ " => Function.comp
|
||||
@[inherit_doc] infixr:35 " × " => Prod
|
||||
@[inherit_doc] infixr:35 " ×' " => PProd
|
||||
|
||||
@[inherit_doc] infix:50 " ∣ " => Dvd.dvd
|
||||
@[inherit_doc] infixl:55 " ||| " => HOr.hOr
|
||||
@@ -703,6 +704,28 @@ syntax (name := checkSimp) "#check_simp " term "~>" term : command
|
||||
-/
|
||||
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
|
||||
|
||||
/--
|
||||
`#discr_tree_key t` prints the discrimination tree keys for a term `t` (or, if it is a single identifier, the type of that constant).
|
||||
It uses the default configuration for generating keys.
|
||||
|
||||
For example,
|
||||
```
|
||||
#discr_tree_key (∀ {a n : Nat}, bar a (OfNat.ofNat n))
|
||||
-- bar _ (@OfNat.ofNat Nat _ _)
|
||||
|
||||
#discr_tree_simp_key Nat.add_assoc
|
||||
-- @HAdd.hAdd Nat Nat Nat _ (@HAdd.hAdd Nat Nat Nat _ _ _) _
|
||||
```
|
||||
|
||||
`#discr_tree_simp_key` is similar to `#discr_tree_key`, but treats the underlying type
|
||||
as one of a simp lemma, i.e. transforms it into an equality and produces the key of the
|
||||
left-hand side.
|
||||
-/
|
||||
syntax (name := discrTreeKeyCmd) "#discr_tree_key " term : command
|
||||
|
||||
@[inherit_doc discrTreeKeyCmd]
|
||||
syntax (name := discrTreeSimpKeyCmd) "#discr_tree_simp_key" term : command
|
||||
|
||||
/--
|
||||
The `seal foo` command ensures that the definition of `foo` is sealed, meaning it is marked as `[irreducible]`.
|
||||
This command is particularly useful in contexts where you want to prevent the reduction of `foo` in proofs.
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Scott Morrison
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.Zip
|
||||
import Init.Data.Int.DivModLemmas
|
||||
import Init.Data.Nat.Gcd
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ Because this is in the `Eq` namespace, if you have a variable `h : a = b`,
|
||||
|
||||
For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality)
|
||||
-/
|
||||
theorem Eq.symm {α : Sort u} {a b : α} (h : Eq a b) : Eq b a :=
|
||||
@[symm] theorem Eq.symm {α : Sort u} {a b : α} (h : Eq a b) : Eq b a :=
|
||||
h ▸ rfl
|
||||
|
||||
/--
|
||||
@@ -488,9 +488,9 @@ attribute [unbox] Prod
|
||||
|
||||
/--
|
||||
Similar to `Prod`, but `α` and `β` can be propositions.
|
||||
You can use `α ×' β` as notation for `PProd α β`.
|
||||
We use this type internally to automatically generate the `brecOn` recursor.
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure PProd (α : Sort u) (β : Sort v) where
|
||||
/-- The first projection out of a pair. if `p : PProd α β` then `p.1 : α`. -/
|
||||
fst : α
|
||||
@@ -2214,12 +2214,17 @@ def Char.utf8Size (c : Char) : Nat :=
|
||||
or `none`. In functional programming languages, this type is used to represent
|
||||
the possibility of failure, or sometimes nullability.
|
||||
|
||||
For example, the function `HashMap.find? : HashMap α β → α → Option β` looks up
|
||||
For example, the function `HashMap.get? : HashMap α β → α → Option β` looks up
|
||||
a specified key `a : α` inside the map. Because we do not know in advance
|
||||
whether the key is actually in the map, the return type is `Option β`, where
|
||||
`none` means the value was not in the map, and `some b` means that the value
|
||||
was found and `b` is the value retrieved.
|
||||
|
||||
The `xs[i]` syntax, which is used to index into collections, has a variant
|
||||
`xs[i]?` that returns an optional value depending on whether the given index
|
||||
is valid. For example, if `m : HashMap α β` and `a : α`, then `m[a]?` is
|
||||
equivalent to `HashMap.get? m a`.
|
||||
|
||||
To extract a value from an `Option α`, we use pattern matching:
|
||||
```
|
||||
def map (f : α → β) (x : Option α) : Option β :=
|
||||
@@ -3172,8 +3177,8 @@ class MonadStateOf (σ : semiOutParam (Type u)) (m : Type u → Type v) where
|
||||
export MonadStateOf (set)
|
||||
|
||||
/--
|
||||
Like `withReader`, but with `ρ` explicit. This is useful if a monad supports
|
||||
`MonadWithReaderOf` for multiple different types `ρ`.
|
||||
Like `get`, but with `σ` explicit. This is useful if a monad supports
|
||||
`MonadStateOf` for multiple different types `σ`.
|
||||
-/
|
||||
abbrev getThe (σ : Type u) {m : Type u → Type v} [MonadStateOf σ m] : m σ :=
|
||||
MonadStateOf.get
|
||||
|
||||
@@ -202,6 +202,17 @@ theorem exists_imp : ((∃ x, p x) → b) ↔ ∀ x, p x → b := forall_exists_
|
||||
@[simp] theorem exists_const (α) [i : Nonempty α] : (∃ _ : α, b) ↔ b :=
|
||||
⟨fun ⟨_, h⟩ => h, i.elim Exists.intro⟩
|
||||
|
||||
@[congr]
|
||||
theorem exists_prop_congr {p p' : Prop} {q q' : p → Prop} (hq : ∀ h, q h ↔ q' h) (hp : p ↔ p') :
|
||||
Exists q ↔ ∃ h : p', q' (hp.2 h) :=
|
||||
⟨fun ⟨_, _⟩ ↦ ⟨hp.1 ‹_›, (hq _).1 ‹_›⟩, fun ⟨_, _⟩ ↦ ⟨_, (hq _).2 ‹_›⟩⟩
|
||||
|
||||
theorem exists_prop_of_true {p : Prop} {q : p → Prop} (h : p) : (Exists fun h' : p => q h') ↔ q h :=
|
||||
@exists_const (q h) p ⟨h⟩
|
||||
|
||||
@[simp] theorem exists_true_left (p : True → Prop) : Exists p ↔ p True.intro :=
|
||||
exists_prop_of_true _
|
||||
|
||||
section forall_congr
|
||||
|
||||
theorem forall_congr' (h : ∀ a, p a ↔ q a) : (∀ a, p a) ↔ ∀ a, q a :=
|
||||
@@ -295,6 +306,8 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
|
||||
|
||||
@[simp] theorem exists_eq_left' : (∃ a, a' = a ∧ p a) ↔ p a' := by simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem exists_eq_right' : (∃ a, p a ∧ a' = a) ↔ p a' := by simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem forall_eq_or_imp : (∀ a, a = a' ∨ q a → p a) ↔ p a' ∧ ∀ a, q a → p a := by
|
||||
simp only [or_imp, forall_and, forall_eq]
|
||||
|
||||
@@ -307,6 +320,11 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
|
||||
@[simp] theorem exists_eq_right_right' : (∃ (a : α), p a ∧ q a ∧ a' = a) ↔ p a' ∧ q a' := by
|
||||
simp [@eq_comm _ a']
|
||||
|
||||
@[simp] theorem exists_or_eq_left (y : α) (p : α → Prop) : ∃ x : α, x = y ∨ p x := ⟨y, .inl rfl⟩
|
||||
@[simp] theorem exists_or_eq_right (y : α) (p : α → Prop) : ∃ x : α, p x ∨ x = y := ⟨y, .inr rfl⟩
|
||||
@[simp] theorem exists_or_eq_left' (y : α) (p : α → Prop) : ∃ x : α, y = x ∨ p x := ⟨y, .inl rfl⟩
|
||||
@[simp] theorem exists_or_eq_right' (y : α) (p : α → Prop) : ∃ x : α, p x ∨ y = x := ⟨y, .inr rfl⟩
|
||||
|
||||
@[simp] theorem exists_prop : (∃ _h : a, b) ↔ a ∧ b :=
|
||||
⟨fun ⟨hp, hq⟩ => ⟨hp, hq⟩, fun ⟨hp, hq⟩ => ⟨hp, hq⟩⟩
|
||||
|
||||
|
||||
@@ -102,3 +102,11 @@ instance ShareCommonT.monadShareCommon [Monad m] : MonadShareCommon (ShareCommon
|
||||
|
||||
@[inline] def ShareCommonT.run [Monad m] (x : ShareCommonT σ m α) : m α := x.run' default
|
||||
@[inline] def ShareCommonM.run (x : ShareCommonM σ α) : α := ShareCommonT.run x
|
||||
|
||||
/--
|
||||
A more restrictive but efficient max sharing primitive.
|
||||
|
||||
Remark: it optimizes the number of RC operations, and the strategy for caching results.
|
||||
-/
|
||||
@[extern "lean_sharecommon_quick"]
|
||||
def ShareCommon.shareCommon' (a : α) : α := a
|
||||
|
||||
@@ -45,6 +45,13 @@ def dbgSleep {α : Type u} (ms : UInt32) (f : Unit → α) : α := f ()
|
||||
@[extern "lean_ptr_addr"]
|
||||
unsafe opaque ptrAddrUnsafe {α : Type u} (a : @& α) : USize
|
||||
|
||||
/--
|
||||
Returns `true` if `a` is an exclusive object.
|
||||
We say an object is exclusive if it is single-threaded and its reference counter is 1.
|
||||
-/
|
||||
@[extern "lean_is_exclusive_obj"]
|
||||
unsafe opaque isExclusiveUnsafe {α : Type u} (a : @& α) : Bool
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
@[inline] unsafe def withPtrAddrUnsafe {α : Type u} {β : Type v} (a : α) (k : USize → β) (h : ∀ u₁ u₂, k u₁ = k u₂) : β :=
|
||||
k (ptrAddrUnsafe a)
|
||||
|
||||
@@ -148,22 +148,26 @@ end InvImage
|
||||
wf := InvImage.wf f h.wf
|
||||
|
||||
-- The transitive closure of a well-founded relation is well-founded
|
||||
namespace TC
|
||||
variable {α : Sort u} {r : α → α → Prop}
|
||||
open Relation
|
||||
|
||||
theorem accessible {z : α} (ac : Acc r z) : Acc (TC r) z := by
|
||||
induction ac with
|
||||
| intro x acx ih =>
|
||||
apply Acc.intro x
|
||||
intro y rel
|
||||
induction rel with
|
||||
| base a b rab => exact ih a rab
|
||||
| trans a b c rab _ _ ih₂ => apply Acc.inv (ih₂ acx ih) rab
|
||||
theorem Acc.transGen (h : Acc r a) : Acc (TransGen r) a := by
|
||||
induction h with
|
||||
| intro x _ H =>
|
||||
refine Acc.intro x fun y hy ↦ ?_
|
||||
cases hy with
|
||||
| single hyx =>
|
||||
exact H y hyx
|
||||
| tail hyz hzx =>
|
||||
exact (H _ hzx).inv hyz
|
||||
|
||||
theorem wf (h : WellFounded r) : WellFounded (TC r) :=
|
||||
⟨fun a => accessible (apply h a)⟩
|
||||
end TC
|
||||
theorem acc_transGen_iff : Acc (TransGen r) a ↔ Acc r a :=
|
||||
⟨Subrelation.accessible TransGen.single, Acc.transGen⟩
|
||||
|
||||
theorem WellFounded.transGen (h : WellFounded r) : WellFounded (TransGen r) :=
|
||||
⟨fun a ↦ (h.apply a).transGen⟩
|
||||
|
||||
@[deprecated Acc.transGen (since := "2024-07-16")] abbrev TC.accessible := @Acc.transGen
|
||||
@[deprecated WellFounded.transGen (since := "2024-07-16")] abbrev TC.wf := @WellFounded.transGen
|
||||
namespace Nat
|
||||
|
||||
-- less-than is well-founded
|
||||
|
||||
@@ -37,7 +37,7 @@ def isAuxRecursor (env : Environment) (declName : Name) : Bool :=
|
||||
|
||||
def isAuxRecursorWithSuffix (env : Environment) (declName : Name) (suffix : String) : Bool :=
|
||||
match declName with
|
||||
| .str _ s => s == suffix && isAuxRecursor env declName
|
||||
| .str _ s => (s == suffix || s.startsWith s!"{suffix}_") && isAuxRecursor env declName
|
||||
| _ => false
|
||||
|
||||
def isCasesOnRecursor (env : Environment) (declName : Name) : Bool :=
|
||||
|
||||
@@ -94,7 +94,7 @@ def emitCInitName (n : Name) : M Unit :=
|
||||
def shouldExport (n : Name) : Bool :=
|
||||
-- HACK: exclude symbols very unlikely to be used by the interpreter or other consumers of
|
||||
-- libleanshared to avoid Windows symbol limit
|
||||
!(`Lean.Compiler.LCNF).isPrefixOf n
|
||||
!(`Lean.Compiler.LCNF).isPrefixOf n && !(`Lean.IR).isPrefixOf n && !(`Lean.Server).isPrefixOf n
|
||||
|
||||
def emitFnDeclAux (decl : Decl) (cppBaseName : String) (isExternal : Bool) : M Unit := do
|
||||
let ps := decl.params
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Compiler.Options
|
||||
import Lean.Compiler.ExternAttr
|
||||
import Lean.Compiler.LCNF.PassManager
|
||||
import Lean.Compiler.LCNF.Passes
|
||||
import Lean.Compiler.LCNF.PrettyPrinter
|
||||
|
||||
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.PrettyPrinter
|
||||
import Lean.PrettyPrinter.Delaborator.Options
|
||||
import Lean.Compiler.LCNF.CompilerM
|
||||
import Lean.Compiler.LCNF.Internalize
|
||||
|
||||
|
||||
@@ -80,6 +80,10 @@ protected def max : RBNode α β → Option (Sigma (fun k => β k))
|
||||
def singleton (k : α) (v : β k) : RBNode α β :=
|
||||
node red leaf k v leaf
|
||||
|
||||
def isSingleton : RBNode α β → Bool
|
||||
| node _ leaf _ _ leaf => true
|
||||
| _ => false
|
||||
|
||||
-- the first half of Okasaki's `balance`, concerning red-red sequences in the left child
|
||||
@[inline] def balance1 : RBNode α β → (a : α) → β a → RBNode α β → RBNode α β
|
||||
| node red (node red a kx vx b) ky vy c, kz, vz, d
|
||||
@@ -269,6 +273,9 @@ variable {α : Type u} {β : Type v} {σ : Type w} {cmp : α → α → Ordering
|
||||
def depth (f : Nat → Nat → Nat) (t : RBMap α β cmp) : Nat :=
|
||||
t.val.depth f
|
||||
|
||||
def isSingleton (t : RBMap α β cmp) : Bool :=
|
||||
t.val.isSingleton
|
||||
|
||||
@[inline] def fold (f : σ → α → β → σ) : (init : σ) → RBMap α β cmp → σ
|
||||
| b, ⟨t, _⟩ => t.fold f b
|
||||
|
||||
|
||||
@@ -286,6 +286,7 @@ def mkInductiveValEx (name : Name) (levelParams : List Name) (type : Expr) (numP
|
||||
|
||||
def InductiveVal.numCtors (v : InductiveVal) : Nat := v.ctors.length
|
||||
def InductiveVal.isNested (v : InductiveVal) : Bool := v.numNested > 0
|
||||
def InductiveVal.numTypeFormers (v : InductiveVal) : Nat := v.all.length + v.numNested
|
||||
|
||||
structure ConstructorVal extends ConstantVal where
|
||||
/-- Inductive type this constructor is a member of -/
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Util.FindMVar
|
||||
import Lean.Util.CollectFVars
|
||||
import Lean.Parser.Term
|
||||
import Lean.Meta.KAbstract
|
||||
import Lean.Meta.Tactic.ElimInfo
|
||||
@@ -711,6 +712,12 @@ structure Context where
|
||||
```
|
||||
theorem Eq.subst' {α} {motive : α → Prop} {a b : α} (h : a = b) : motive a → motive b
|
||||
```
|
||||
For another example, the term `isEmptyElim (α := α)` is an underapplied eliminator, and it needs
|
||||
argument `α` to be elaborated eagerly to create a type-correct motive.
|
||||
```
|
||||
def isEmptyElim [IsEmpty α] {p : α → Sort _} (a : α) : p a := ...
|
||||
example {α : Type _} [IsEmpty α] : id (α → False) := isEmptyElim (α := α)
|
||||
```
|
||||
-/
|
||||
extraArgsPos : Array Nat
|
||||
|
||||
@@ -724,8 +731,8 @@ structure State where
|
||||
namedArgs : List NamedArg
|
||||
/-- User-provided arguments that still have to be processed. -/
|
||||
args : List Arg
|
||||
/-- Discriminants processed so far. -/
|
||||
discrs : Array Expr := #[]
|
||||
/-- Discriminants (targets) processed so far. -/
|
||||
discrs : Array (Option Expr)
|
||||
/-- Instance implicit arguments collected so far. -/
|
||||
instMVars : Array MVarId := #[]
|
||||
/-- Position of the next argument to be processed. We use it to decide whether the argument is the motive or a discriminant. -/
|
||||
@@ -736,7 +743,7 @@ structure State where
|
||||
abbrev M := ReaderT Context $ StateRefT State TermElabM
|
||||
|
||||
/-- Infer the `motive` using the expected type by `kabstract`ing the discriminants. -/
|
||||
def mkMotive (discrs : Array Expr) (expectedType : Expr): MetaM Expr := do
|
||||
def mkMotive (discrs : Array Expr) (expectedType : Expr) : MetaM Expr := do
|
||||
discrs.foldrM (init := expectedType) fun discr motive => do
|
||||
let discr ← instantiateMVars discr
|
||||
let motiveBody ← kabstract motive discr
|
||||
@@ -758,7 +765,7 @@ def revertArgs (args : List Arg) (f : Expr) (expectedType : Expr) : TermElabM (E
|
||||
return (mkApp f val, mkForall (← mkFreshBinderName) BinderInfo.default valType expectedTypeBody)
|
||||
|
||||
/--
|
||||
Construct the resulting application after all discriminants have bee elaborated, and we have
|
||||
Construct the resulting application after all discriminants have been elaborated, and we have
|
||||
consumed as many given arguments as possible.
|
||||
-/
|
||||
def finalize : M Expr := do
|
||||
@@ -766,29 +773,50 @@ def finalize : M Expr := do
|
||||
throwError "failed to elaborate eliminator, unused named arguments: {(← get).namedArgs.map (·.name)}"
|
||||
let some motive := (← get).motive?
|
||||
| throwError "failed to elaborate eliminator, insufficient number of arguments"
|
||||
trace[Elab.app.elab_as_elim] "motive: {motive}"
|
||||
forallTelescope (← get).fType fun xs _ => do
|
||||
trace[Elab.app.elab_as_elim] "xs: {xs}"
|
||||
let mut expectedType := (← read).expectedType
|
||||
trace[Elab.app.elab_as_elim] "expectedType:{indentD expectedType}"
|
||||
let throwInsufficient := do
|
||||
throwError "failed to elaborate eliminator, insufficient number of arguments, expected type:{indentExpr expectedType}"
|
||||
let mut f := (← get).f
|
||||
if xs.size > 0 then
|
||||
-- under-application, specialize the expected type using `xs`
|
||||
assert! (← get).args.isEmpty
|
||||
try
|
||||
expectedType ← instantiateForall expectedType xs
|
||||
catch _ =>
|
||||
throwError "failed to elaborate eliminator, insufficient number of arguments, expected type:{indentExpr expectedType}"
|
||||
for x in xs do
|
||||
let .forallE _ t b _ ← whnf expectedType | throwInsufficient
|
||||
unless ← fullApproxDefEq <| isDefEq t (← inferType x) do
|
||||
-- We can't assume that these binding domains were supposed to line up, so report insufficient arguments
|
||||
throwInsufficient
|
||||
expectedType := b.instantiate1 x
|
||||
trace[Elab.app.elab_as_elim] "xs after specialization of expected type: {xs}"
|
||||
else
|
||||
-- over-application, simulate `revert`
|
||||
-- over-application, simulate `revert` while generalizing the values of these arguments in the expected type
|
||||
(f, expectedType) ← revertArgs (← get).args f expectedType
|
||||
unless ← isTypeCorrect expectedType do
|
||||
throwError "failed to elaborate eliminator, after generalizing over-applied arguments, expected type is type incorrect:{indentExpr expectedType}"
|
||||
trace[Elab.app.elab_as_elim] "expectedType after processing:{indentD expectedType}"
|
||||
let result := mkAppN f xs
|
||||
trace[Elab.app.elab_as_elim] "result:{indentD result}"
|
||||
let mut discrs := (← get).discrs
|
||||
let idx := (← get).idx
|
||||
if (← get).discrs.size < (← read).elimInfo.targetsPos.size then
|
||||
if discrs.any Option.isNone then
|
||||
for i in [idx:idx + xs.size], x in xs do
|
||||
if (← read).elimInfo.targetsPos.contains i then
|
||||
discrs := discrs.push x
|
||||
let motiveVal ← mkMotive discrs expectedType
|
||||
if let some tidx := (← read).elimInfo.targetsPos.indexOf? i then
|
||||
discrs := discrs.set! tidx x
|
||||
if let some idx := discrs.findIdx? Option.isNone then
|
||||
-- This should not happen.
|
||||
trace[Elab.app.elab_as_elim] "Internal error, missing target with index {idx}"
|
||||
throwError "failed to elaborate eliminator, insufficient number of arguments"
|
||||
trace[Elab.app.elab_as_elim] "discrs: {discrs.map Option.get!}"
|
||||
let motiveVal ← mkMotive (discrs.map Option.get!) expectedType
|
||||
unless (← isTypeCorrect motiveVal) do
|
||||
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motiveVal}"
|
||||
unless (← isDefEq motive motiveVal) do
|
||||
throwError "failed to elaborate eliminator, invalid motive{indentExpr motiveVal}"
|
||||
synthesizeAppInstMVars (← get).instMVars result
|
||||
trace[Elab.app.elab_as_elim] "completed motive:{indentD motive}"
|
||||
let result ← mkLambdaFVars xs (← instantiateMVars result)
|
||||
return result
|
||||
|
||||
@@ -816,9 +844,9 @@ def getNextArg? (binderName : Name) (binderInfo : BinderInfo) : M (LOption Arg)
|
||||
def setMotive (motive : Expr) : M Unit :=
|
||||
modify fun s => { s with motive? := motive }
|
||||
|
||||
/-- Push the given expression into the `discrs` field in the state. -/
|
||||
def addDiscr (discr : Expr) : M Unit :=
|
||||
modify fun s => { s with discrs := s.discrs.push discr }
|
||||
/-- Push the given expression into the `discrs` field in the state, where `i` is which target it is for. -/
|
||||
def addDiscr (i : Nat) (discr : Expr) : M Unit :=
|
||||
modify fun s => { s with discrs := s.discrs.set! i discr }
|
||||
|
||||
/-- Elaborate the given argument with the given expected type. -/
|
||||
private def elabArg (arg : Arg) (argExpectedType : Expr) : M Expr := do
|
||||
@@ -850,14 +878,23 @@ partial def main : M Expr := do
|
||||
main
|
||||
let idx := (← get).idx
|
||||
if (← read).elimInfo.motivePos == idx then
|
||||
let motive ← mkImplicitArg binderType binderInfo
|
||||
let motive ←
|
||||
match (← getNextArg? binderName binderInfo) with
|
||||
| .some arg =>
|
||||
/- Due to `Lean.Elab.Term.elabAppArgs.elabAsElim?`, this must be a positional argument that is the syntax `_`. -/
|
||||
elabArg arg binderType
|
||||
| .none | .undef =>
|
||||
/- Note: undef occurs when the motive is explicit but missing.
|
||||
In this case, we treat it as if it were an implicit argument
|
||||
to support writing `h.rec` when `h : False`, rather than requiring `h.rec _`. -/
|
||||
mkImplicitArg binderType binderInfo
|
||||
setMotive motive
|
||||
addArgAndContinue motive
|
||||
else if (← read).elimInfo.targetsPos.contains idx then
|
||||
else if let some tidx := (← read).elimInfo.targetsPos.indexOf? idx then
|
||||
match (← getNextArg? binderName binderInfo) with
|
||||
| .some arg => let discr ← elabArg arg binderType; addDiscr discr; addArgAndContinue discr
|
||||
| .some arg => let discr ← elabArg arg binderType; addDiscr tidx discr; addArgAndContinue discr
|
||||
| .undef => finalize
|
||||
| .none => let discr ← mkImplicitArg binderType binderInfo; addDiscr discr; addArgAndContinue discr
|
||||
| .none => let discr ← mkImplicitArg binderType binderInfo; addDiscr tidx discr; addArgAndContinue discr
|
||||
else match (← getNextArg? binderName binderInfo) with
|
||||
| .some (.stx stx) =>
|
||||
if (← read).extraArgsPos.contains idx then
|
||||
@@ -919,10 +956,12 @@ def elabAppArgs (f : Expr) (namedArgs : Array NamedArg) (args : Array Arg)
|
||||
let expectedType ← instantiateMVars expectedType
|
||||
if expectedType.getAppFn.isMVar then throwError "failed to elaborate eliminator, expected type is not available"
|
||||
let extraArgsPos ← getElabAsElimExtraArgsPos elimInfo
|
||||
trace[Elab.app.elab_as_elim] "extraArgsPos: {extraArgsPos}"
|
||||
ElabElim.main.run { elimInfo, expectedType, extraArgsPos } |>.run' {
|
||||
f, fType
|
||||
args := args.toList
|
||||
namedArgs := namedArgs.toList
|
||||
discrs := mkArray elimInfo.targetsPos.size none
|
||||
}
|
||||
else
|
||||
ElabAppArgs.main.run { explicit, ellipsis, resultIsOutParamSupport } |>.run' {
|
||||
@@ -940,31 +979,60 @@ where
|
||||
unless (← shouldElabAsElim declName) do return none
|
||||
let elimInfo ← getElimInfo declName
|
||||
forallTelescopeReducing (← inferType f) fun xs _ => do
|
||||
if h : elimInfo.motivePos < xs.size then
|
||||
let x := xs[elimInfo.motivePos]
|
||||
/- Process arguments similar to `Lean.Elab.Term.ElabElim.main` to see if the motive has been
|
||||
provided, in which case we use the standard app elaborator.
|
||||
If the motive is explicit (like for `False.rec`), then a positional `_` counts as "not provided". -/
|
||||
let mut args := args.toList
|
||||
let mut namedArgs := namedArgs.toList
|
||||
for x in xs[0:elimInfo.motivePos] do
|
||||
let localDecl ← x.fvarId!.getDecl
|
||||
if findBinderName? namedArgs.toList localDecl.userName matches some _ then
|
||||
match findBinderName? namedArgs localDecl.userName with
|
||||
| some _ => namedArgs := eraseNamedArg namedArgs localDecl.userName
|
||||
| none => if localDecl.binderInfo.isExplicit then args := args.tailD []
|
||||
-- Invariant: `elimInfo.motivePos < xs.size` due to construction of `elimInfo`.
|
||||
let some x := xs[elimInfo.motivePos]? | unreachable!
|
||||
let localDecl ← x.fvarId!.getDecl
|
||||
if findBinderName? namedArgs localDecl.userName matches some _ then
|
||||
-- motive has been explicitly provided, so we should use standard app elaborator
|
||||
return none
|
||||
else
|
||||
match localDecl.binderInfo.isExplicit, args with
|
||||
| true, .expr _ :: _ =>
|
||||
-- motive has been explicitly provided, so we should use standard app elaborator
|
||||
return none
|
||||
return some elimInfo
|
||||
else
|
||||
return none
|
||||
| true, .stx arg :: _ =>
|
||||
if arg.isOfKind ``Lean.Parser.Term.hole then
|
||||
return some elimInfo
|
||||
else
|
||||
-- positional motive is not `_`, so we should use standard app elaborator
|
||||
return none
|
||||
| _, _ => return some elimInfo
|
||||
|
||||
/--
|
||||
Collect extra argument positions that must be elaborated eagerly when using `elab_as_elim`.
|
||||
The idea is that the contribute to motive inference. See comment at `ElamElim.Context.extraArgsPos`.
|
||||
The idea is that they contribute to motive inference. See comment at `ElamElim.Context.extraArgsPos`.
|
||||
-/
|
||||
getElabAsElimExtraArgsPos (elimInfo : ElimInfo) : MetaM (Array Nat) := do
|
||||
forallTelescope elimInfo.elimType fun xs type => do
|
||||
let resultArgs := type.getAppArgs
|
||||
let targets := type.getAppArgs
|
||||
/- Compute transitive closure of fvars appearing in the motive and the targets. -/
|
||||
let initMotiveFVars : CollectFVars.State := targets.foldl (init := {}) collectFVars
|
||||
let motiveFVars ← xs.size.foldRevM (init := initMotiveFVars) fun i s => do
|
||||
let x := xs[i]!
|
||||
if elimInfo.motivePos == i || elimInfo.targetsPos.contains i || s.fvarSet.contains x.fvarId! then
|
||||
return collectFVars s (← inferType x)
|
||||
else
|
||||
return s
|
||||
/- Collect the extra argument positions -/
|
||||
let mut extraArgsPos := #[]
|
||||
for i in [:xs.size] do
|
||||
let x := xs[i]!
|
||||
unless elimInfo.targetsPos.contains i do
|
||||
let xType ← inferType x
|
||||
unless elimInfo.motivePos == i || elimInfo.targetsPos.contains i do
|
||||
let xType ← x.fvarId!.getType
|
||||
/- We only consider "first-order" types because we can reliably "extract" information from them. -/
|
||||
if isFirstOrder xType
|
||||
&& Option.isSome (xType.find? fun e => e.isFVar && resultArgs.contains e) then
|
||||
if motiveFVars.fvarSet.contains x.fvarId!
|
||||
|| (isFirstOrder xType
|
||||
&& Option.isSome (xType.find? fun e => e.isFVar && motiveFVars.fvarSet.contains e.fvarId!)) then
|
||||
extraArgsPos := extraArgsPos.push i
|
||||
return extraArgsPos
|
||||
|
||||
@@ -1292,6 +1360,7 @@ private partial def elabAppFnId (fIdent : Syntax) (fExplicitUnivs : List Level)
|
||||
funLVals.foldlM (init := acc) fun acc (f, fIdent, fields) => do
|
||||
let lvals' := toLVals fields (first := true)
|
||||
let s ← observing do
|
||||
checkDeprecated fIdent f
|
||||
let f ← addTermInfo fIdent f expectedType?
|
||||
let e ← elabAppLVals f (lvals' ++ lvals) namedArgs args expectedType? explicit ellipsis
|
||||
if overloaded then ensureHasType expectedType? e else return e
|
||||
@@ -1313,9 +1382,17 @@ private partial def resolveDotName (id : Syntax) (expectedType? : Option Expr) :
|
||||
tryPostponeIfNoneOrMVar expectedType?
|
||||
let some expectedType := expectedType?
|
||||
| throwError "invalid dotted identifier notation, expected type must be known"
|
||||
forallTelescopeReducing expectedType fun _ resultType => do
|
||||
withForallBody expectedType fun resultType => do
|
||||
go resultType expectedType #[]
|
||||
where
|
||||
/-- A weak version of forallTelescopeReducing that only uses whnfCore, to avoid unfolding definitions except by `unfoldDefinition?` below. -/
|
||||
withForallBody {α} (type : Expr) (k : Expr → TermElabM α) : TermElabM α :=
|
||||
forallTelescope type fun _ body => do
|
||||
let body ← whnfCore body
|
||||
if body.isForall then
|
||||
withForallBody body k
|
||||
else
|
||||
k body
|
||||
go (resultType : Expr) (expectedType : Expr) (previousExceptions : Array Exception) : TermElabM Name := do
|
||||
let resultType ← instantiateMVars resultType
|
||||
let resultTypeFn := resultType.cleanupAnnotations.getAppFn
|
||||
@@ -1331,7 +1408,8 @@ where
|
||||
| ex@(.error ..) =>
|
||||
match (← unfoldDefinition? resultType) with
|
||||
| some resultType =>
|
||||
go (← whnfCore resultType) expectedType (previousExceptions.push ex)
|
||||
withForallBody resultType fun resultType => do
|
||||
go resultType expectedType (previousExceptions.push ex)
|
||||
| none =>
|
||||
previousExceptions.forM fun ex => logException ex
|
||||
throw ex
|
||||
@@ -1524,5 +1602,6 @@ builtin_initialize
|
||||
registerTraceClass `Elab.app.args (inherited := true)
|
||||
registerTraceClass `Elab.app.propagateExpectedType (inherited := true)
|
||||
registerTraceClass `Elab.app.finalize (inherited := true)
|
||||
registerTraceClass `Elab.app.elab_as_elim (inherited := true)
|
||||
|
||||
end Lean.Elab.Term
|
||||
|
||||
@@ -5,13 +5,13 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Util.CollectLevelParams
|
||||
import Lean.Util.CollectAxioms
|
||||
import Lean.Meta.Reduce
|
||||
import Lean.Elab.DeclarationRange
|
||||
import Lean.Elab.Eval
|
||||
import Lean.Elab.Command
|
||||
import Lean.Elab.Open
|
||||
import Lean.Elab.SetOption
|
||||
import Lean.PrettyPrinter
|
||||
|
||||
namespace Lean.Elab.Command
|
||||
|
||||
@@ -341,8 +341,7 @@ private def mkRunEval (e : Expr) : MetaM Expr := do
|
||||
let instVal ← mkEvalInstCore ``Lean.Eval e
|
||||
instantiateMVars (mkAppN (mkConst ``Lean.runEval [u]) #[α, instVal, mkSimpleThunk e])
|
||||
|
||||
unsafe def elabEvalUnsafe : CommandElab
|
||||
| `(#eval%$tk $term) => do
|
||||
unsafe def elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax): CommandElabM Unit := do
|
||||
let declName := `_eval
|
||||
let addAndCompile (value : Expr) : TermElabM Unit := do
|
||||
let value ← Term.levelMVarToParam (← instantiateMVars value)
|
||||
@@ -359,6 +358,13 @@ unsafe def elabEvalUnsafe : CommandElab
|
||||
}
|
||||
Term.ensureNoUnassignedMVars decl
|
||||
addAndCompile decl
|
||||
-- Check for sorry axioms
|
||||
let checkSorry (declName : Name) : MetaM Unit := do
|
||||
unless bang do
|
||||
let axioms ← collectAxioms declName
|
||||
if axioms.contains ``sorryAx then
|
||||
throwError ("cannot evaluate expression that depends on the `sorry` axiom.\nUse `#eval!` to " ++
|
||||
"evaluate nevertheless (which may cause lean to crash).")
|
||||
-- Elaborate `term`
|
||||
let elabEvalTerm : TermElabM Expr := do
|
||||
let e ← Term.elabTerm term none
|
||||
@@ -387,6 +393,7 @@ unsafe def elabEvalUnsafe : CommandElab
|
||||
else
|
||||
let e ← mkRunMetaEval e
|
||||
addAndCompile e
|
||||
checkSorry declName
|
||||
let act ← evalConst (Environment → Options → IO (String × Except IO.Error Environment)) declName
|
||||
pure <| Sum.inr act
|
||||
match act with
|
||||
@@ -403,6 +410,7 @@ unsafe def elabEvalUnsafe : CommandElab
|
||||
-- modify e to `runEval e`
|
||||
let e ← mkRunEval (← elabEvalTerm)
|
||||
addAndCompile e
|
||||
checkSorry declName
|
||||
let act ← evalConst (IO (String × Except IO.Error Unit)) declName
|
||||
let (out, res) ← liftM (m := IO) act
|
||||
logInfoAt tk out
|
||||
@@ -413,10 +421,19 @@ unsafe def elabEvalUnsafe : CommandElab
|
||||
elabMetaEval
|
||||
else
|
||||
elabEval
|
||||
|
||||
@[implemented_by elabEvalCoreUnsafe]
|
||||
opaque elabEvalCore (bang : Bool) (tk term : Syntax): CommandElabM Unit
|
||||
|
||||
@[builtin_command_elab «eval»]
|
||||
def elabEval : CommandElab
|
||||
| `(#eval%$tk $term) => elabEvalCore false tk term
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_command_elab «eval», implemented_by elabEvalUnsafe]
|
||||
opaque elabEval : CommandElab
|
||||
@[builtin_command_elab evalBang]
|
||||
def elabEvalBang : CommandElab
|
||||
| `(Parser.Command.evalBang|#eval!%$tk $term) => elabEvalCore true tk term
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
private def checkImportsForRunCmds : CommandElabM Unit := do
|
||||
unless (← getEnv).contains ``CommandElabM do
|
||||
|
||||
@@ -220,6 +220,31 @@ partial def mkPairs (elems : Array Term) : MacroM Term :=
|
||||
pure acc
|
||||
loop (elems.size - 1) elems.back
|
||||
|
||||
/-- Return syntax `PProd.mk elems[0] (PProd.mk elems[1] ... (PProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
|
||||
partial def mkPPairs (elems : Array Term) : MacroM Term :=
|
||||
let rec loop (i : Nat) (acc : Term) := do
|
||||
if i > 0 then
|
||||
let i := i - 1
|
||||
let elem := elems[i]!
|
||||
let acc ← `(PProd.mk $elem $acc)
|
||||
loop i acc
|
||||
else
|
||||
pure acc
|
||||
loop (elems.size - 1) elems.back
|
||||
|
||||
/-- Return syntax `MProd.mk elems[0] (MProd.mk elems[1] ... (MProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
|
||||
partial def mkMPairs (elems : Array Term) : MacroM Term :=
|
||||
let rec loop (i : Nat) (acc : Term) := do
|
||||
if i > 0 then
|
||||
let i := i - 1
|
||||
let elem := elems[i]!
|
||||
let acc ← `(MProd.mk $elem $acc)
|
||||
loop i acc
|
||||
else
|
||||
pure acc
|
||||
loop (elems.size - 1) elems.back
|
||||
|
||||
|
||||
open Parser in
|
||||
partial def hasCDot : Syntax → Bool
|
||||
| Syntax.node _ k args =>
|
||||
|
||||
@@ -316,9 +316,7 @@ private def mkSilentAnnotationIfHole (e : Expr) : TermElabM Expr := do
|
||||
return false
|
||||
return true
|
||||
if canClear then
|
||||
let lctx := (← getLCtx).erase fvarId
|
||||
let localInsts := (← getLocalInstances).filter (·.fvar.fvarId! != fvarId)
|
||||
withLCtx lctx localInsts do elabTerm body expectedType?
|
||||
withErasedFVars #[fvarId] do elabTerm body expectedType?
|
||||
else
|
||||
elabTerm body expectedType?
|
||||
|
||||
@@ -364,4 +362,7 @@ private opaque evalFilePath (stx : Syntax) : TermElabM System.FilePath
|
||||
mkStrLit <$> IO.FS.readFile path
|
||||
| _, _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_term_elab Lean.Parser.Term.namedPattern] def elabNamedPatternErr : TermElab := fun stx _ =>
|
||||
throwError "`<identifier>@<term>` is a named pattern and can only be used in pattern matching contexts{indentD stx}"
|
||||
|
||||
end Lean.Elab.Term
|
||||
|
||||
@@ -12,17 +12,62 @@ import Lean.Language.Basic
|
||||
|
||||
namespace Lean.Elab.Command
|
||||
|
||||
/--
|
||||
A `Scope` records the part of the `CommandElabM` state that respects scoping,
|
||||
such as the data for `universe`, `open`, and `variable` declarations, the current namespace,
|
||||
and currently enabled options.
|
||||
The `CommandElabM` state contains a stack of scopes, and only the top `Scope`
|
||||
on the stack is read from or modified. There is always at least one `Scope` on the stack,
|
||||
even outside any `section` or `namespace`, and each new pushed `Scope`
|
||||
starts as a modified copy of the previous top scope.
|
||||
-/
|
||||
structure Scope where
|
||||
/--
|
||||
The component of the `namespace` or `section` that this scope is associated to.
|
||||
For example, `section a.b.c` and `namespace a.b.c` each create three scopes with headers
|
||||
named `a`, `b`, and `c`.
|
||||
This is used for checking the `end` command. The "base scope" has `""` as its header.
|
||||
-/
|
||||
header : String
|
||||
/--
|
||||
The current state of all set options at this point in the scope. Note that this is the
|
||||
full current set of options and does *not* simply contain the options set
|
||||
while this scope has been active.
|
||||
-/
|
||||
opts : Options := {}
|
||||
/-- The current namespace. The top-level namespace is represented by `Name.anonymous`. -/
|
||||
currNamespace : Name := Name.anonymous
|
||||
/-- All currently `open`ed namespaces and names. -/
|
||||
openDecls : List OpenDecl := []
|
||||
/-- The current list of names for universe level variables to use for new declarations. This is managed by the `universe` command. -/
|
||||
levelNames : List Name := []
|
||||
/-- section variables -/
|
||||
/--
|
||||
The current list of binders to use for new declarations.
|
||||
This is managed by the `variable` command.
|
||||
Each binder is represented in `Syntax` form, and it is re-elaborated
|
||||
within each command that uses this information.
|
||||
|
||||
This is also used by commands, such as `#check`, to create an initial local context,
|
||||
even if they do not work with binders per se.
|
||||
-/
|
||||
varDecls : Array (TSyntax ``Parser.Term.bracketedBinder) := #[]
|
||||
/-- Globally unique internal identifiers for the `varDecls` -/
|
||||
/--
|
||||
Globally unique internal identifiers for the `varDecls`.
|
||||
There is one identifier per variable introduced by the binders
|
||||
(recall that a binder such as `(a b c : Ty)` can produce more than one variable),
|
||||
and each identifier is the user-provided variable name with a macro scope.
|
||||
This is used by `TermElabM` in `Lean.Elab.Term.Context` to help with processing macros
|
||||
that capture these variables.
|
||||
-/
|
||||
varUIds : Array Name := #[]
|
||||
/-- noncomputable sections automatically add the `noncomputable` modifier to any declaration we cannot generate code for. -/
|
||||
/--
|
||||
If true (default: false), all declarations that fail to compile
|
||||
automatically receive the `noncomputable` modifier.
|
||||
A scope with this flag set is created by `noncomputable section`.
|
||||
|
||||
Recall that a new scope inherits all values from its parent scope,
|
||||
so all sections and namespaces nested within a `noncomputable` section also have this flag set.
|
||||
-/
|
||||
isNoncomputable : Bool := false
|
||||
deriving Inhabited
|
||||
|
||||
@@ -230,6 +275,7 @@ private def ioErrorToMessage (ctx : Context) (ref : Syntax) (err : IO.Error) : M
|
||||
instance : MonadLiftT IO CommandElabM where
|
||||
monadLift := liftIO
|
||||
|
||||
/-- Return the current scope. -/
|
||||
def getScope : CommandElabM Scope := do pure (← get).scopes.head!
|
||||
|
||||
instance : MonadResolveName CommandElabM where
|
||||
@@ -479,7 +525,7 @@ def elabCommandTopLevel (stx : Syntax) : CommandElabM Unit := withRef stx do pro
|
||||
-- should be true iff the command supports incrementality
|
||||
if (← IO.hasFinished snap.new.result) then
|
||||
trace[Elab.snapshotTree]
|
||||
Language.ToSnapshotTree.toSnapshotTree snap.new.result.get |>.format
|
||||
(←Language.ToSnapshotTree.toSnapshotTree snap.new.result.get |>.format)
|
||||
modify fun st => { st with
|
||||
messages := initMsgs ++ msgs
|
||||
infoState := { st.infoState with trees := initInfoTrees ++ st.infoState.trees }
|
||||
@@ -612,6 +658,11 @@ Interrupt and abort exceptions are caught but not logged.
|
||||
private def liftAttrM {α} (x : AttrM α) : CommandElabM α := do
|
||||
liftCoreM x
|
||||
|
||||
/--
|
||||
Return the stack of all currently active scopes:
|
||||
the base scope always comes last; new scopes are prepended in the front.
|
||||
In particular, the current scope is always the first element.
|
||||
-/
|
||||
def getScopes : CommandElabM (List Scope) := do
|
||||
pure (← get).scopes
|
||||
|
||||
|
||||
@@ -43,12 +43,10 @@ where
|
||||
let mut ctorArgs1 := #[]
|
||||
let mut ctorArgs2 := #[]
|
||||
let mut rhs ← `(true)
|
||||
-- add `_` for inductive parameters, they are inaccessible
|
||||
for _ in [:indVal.numParams] do
|
||||
ctorArgs1 := ctorArgs1.push (← `(_))
|
||||
ctorArgs2 := ctorArgs2.push (← `(_))
|
||||
let mut rhs_empty := true
|
||||
for i in [:ctorInfo.numFields] do
|
||||
let x := xs[indVal.numParams + i]!
|
||||
let pos := indVal.numParams + ctorInfo.numFields - i - 1
|
||||
let x := xs[pos]!
|
||||
if type.containsFVar x.fvarId! then
|
||||
-- If resulting type depends on this field, we don't need to compare
|
||||
ctorArgs1 := ctorArgs1.push (← `(_))
|
||||
@@ -62,11 +60,32 @@ where
|
||||
if (← isProp xType) then
|
||||
continue
|
||||
if xType.isAppOf indVal.name then
|
||||
rhs ← `($rhs && $(mkIdent auxFunName):ident $a:ident $b:ident)
|
||||
if rhs_empty then
|
||||
rhs ← `($(mkIdent auxFunName):ident $a:ident $b:ident)
|
||||
rhs_empty := false
|
||||
else
|
||||
rhs ← `($(mkIdent auxFunName):ident $a:ident $b:ident && $rhs)
|
||||
/- If `x` appears in the type of another field, use `eq_of_beq` to
|
||||
unify the types of the subsequent variables -/
|
||||
else if ← xs[pos+1:].anyM
|
||||
(fun fvar => (Expr.containsFVar · x.fvarId!) <$> (inferType fvar)) then
|
||||
rhs ← `(if h : $a:ident == $b:ident then by
|
||||
cases (eq_of_beq h)
|
||||
exact $rhs
|
||||
else false)
|
||||
rhs_empty := false
|
||||
else
|
||||
rhs ← `($rhs && $a:ident == $b:ident)
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName):ident $ctorArgs1:term*))
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName):ident $ctorArgs2:term*))
|
||||
if rhs_empty then
|
||||
rhs ← `($a:ident == $b:ident)
|
||||
rhs_empty := false
|
||||
else
|
||||
rhs ← `($a:ident == $b:ident && $rhs)
|
||||
-- add `_` for inductive parameters, they are inaccessible
|
||||
for _ in [:indVal.numParams] do
|
||||
ctorArgs1 := ctorArgs1.push (← `(_))
|
||||
ctorArgs2 := ctorArgs2.push (← `(_))
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName):ident $ctorArgs1.reverse:term*))
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName):ident $ctorArgs2.reverse:term*))
|
||||
`(matchAltExpr| | $[$patterns:term],* => $rhs:term)
|
||||
alts := alts.push alt
|
||||
alts := alts.push (← mkElseAlt)
|
||||
|
||||
@@ -91,8 +91,14 @@ def mkAuxFunction (ctx : Context) (auxFunName : Name) (indVal : InductiveVal): T
|
||||
let header ← mkDecEqHeader indVal
|
||||
let body ← mkMatch ctx header indVal
|
||||
let binders := header.binders
|
||||
let type ← `(Decidable ($(mkIdent header.targetNames[0]!) = $(mkIdent header.targetNames[1]!)))
|
||||
`(private def $(mkIdent auxFunName):ident $binders:bracketedBinder* : $type:term := $body:term)
|
||||
let target₁ := mkIdent header.targetNames[0]!
|
||||
let target₂ := mkIdent header.targetNames[1]!
|
||||
let termSuffix ← if indVal.isRec
|
||||
then `(Parser.Termination.suffix|termination_by structural $target₁)
|
||||
else `(Parser.Termination.suffix|)
|
||||
let type ← `(Decidable ($target₁ = $target₂))
|
||||
`(private def $(mkIdent auxFunName):ident $binders:bracketedBinder* : $type:term := $body:term
|
||||
$termSuffix:suffix)
|
||||
|
||||
def mkAuxFunctions (ctx : Context) : TermElabM (TSyntax `command) := do
|
||||
let mut res : Array (TSyntax `command) := #[]
|
||||
|
||||
@@ -672,8 +672,7 @@ partial def main (patternVarDecls : Array PatternVarDecl) (ps : Array Expr) (mat
|
||||
throwError "invalid patterns, `{mkFVar explicit}` is an explicit pattern variable, but it only occurs in positions that are inaccessible to pattern matching{indentD (MessageData.joinSep (ps.toList.map (MessageData.ofExpr .)) m!"\n\n")}"
|
||||
let packed ← pack patternVars ps matchType
|
||||
trace[Elab.match] "packed: {packed}"
|
||||
let lctx := explicitPatternVars.foldl (init := (← getLCtx)) fun lctx d => lctx.erase d
|
||||
withTheReader Meta.Context (fun ctx => { ctx with lctx := lctx }) do
|
||||
withErasedFVars explicitPatternVars do
|
||||
check packed
|
||||
unpack packed fun patternVars patterns matchType => do
|
||||
let localDecls ← patternVars.mapM fun x => x.fvarId!.getDecl
|
||||
|
||||
@@ -728,12 +728,26 @@ def insertReplacementForLetRecs (r : Replacement) (letRecClosures : List LetRecC
|
||||
letRecClosures.foldl (init := r) fun r c =>
|
||||
r.insert c.toLift.fvarId c.closed
|
||||
|
||||
def isApplicable (r : Replacement) (e : Expr) : Bool :=
|
||||
Option.isSome <| e.findExt? fun e =>
|
||||
if e.hasFVar then
|
||||
match e with
|
||||
| .fvar fvarId => if r.contains fvarId then .found else .done
|
||||
| _ => .visit
|
||||
else
|
||||
.done
|
||||
|
||||
def Replacement.apply (r : Replacement) (e : Expr) : Expr :=
|
||||
e.replace fun e => match e with
|
||||
| .fvar fvarId => match r.find? fvarId with
|
||||
| some c => some c
|
||||
| _ => none
|
||||
| _ => none
|
||||
-- Remark: if `r` is not a singlenton, then declaration is using `mutual` or `let rec`,
|
||||
-- and there is a big chance `isApplicable r e` is true.
|
||||
if r.isSingleton && !isApplicable r e then
|
||||
e
|
||||
else
|
||||
e.replace fun e => match e with
|
||||
| .fvar fvarId => match r.find? fvarId with
|
||||
| some c => some c
|
||||
| _ => none
|
||||
| _ => none
|
||||
|
||||
def pushMain (preDefs : Array PreDefinition) (sectionVars : Array Expr) (mainHeaders : Array DefViewElabHeader) (mainVals : Array Expr)
|
||||
: TermElabM (Array PreDefinition) :=
|
||||
@@ -923,6 +937,7 @@ where
|
||||
trace[Elab.definition] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
let preDefs ← withLevelNames allUserLevelNames <| levelMVarToParamPreDecls preDefs
|
||||
let preDefs ← instantiateMVarsAtPreDecls preDefs
|
||||
let preDefs ← shareCommonPreDefs preDefs
|
||||
let preDefs ← fixLevelParams preDefs scopeLevelNames allUserLevelNames
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition] "after eraseAuxDiscr, {preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.ShareCommon
|
||||
import Lean.Compiler.NoncomputableAttr
|
||||
import Lean.Util.CollectLevelParams
|
||||
import Lean.Meta.AbstractNestedProofs
|
||||
@@ -53,18 +54,20 @@ private def getLevelParamsPreDecls (preDefs : Array PreDefinition) (scopeLevelNa
|
||||
| Except.ok levelParams => pure levelParams
|
||||
|
||||
def fixLevelParams (preDefs : Array PreDefinition) (scopeLevelNames allUserLevelNames : List Name) : TermElabM (Array PreDefinition) := do
|
||||
-- We used to use `shareCommon` here, but is was a bottleneck
|
||||
let levelParams ← getLevelParamsPreDecls preDefs scopeLevelNames allUserLevelNames
|
||||
let us := levelParams.map mkLevelParam
|
||||
let fixExpr (e : Expr) : Expr :=
|
||||
e.replace fun c => match c with
|
||||
| Expr.const declName _ => if preDefs.any fun preDef => preDef.declName == declName then some $ Lean.mkConst declName us else none
|
||||
| _ => none
|
||||
return preDefs.map fun preDef =>
|
||||
{ preDef with
|
||||
type := fixExpr preDef.type,
|
||||
value := fixExpr preDef.value,
|
||||
levelParams := levelParams }
|
||||
profileitM Exception s!"fix level params" (← getOptions) do
|
||||
withTraceNode `Elab.def.fixLevelParams (fun _ => return m!"fix level params") do
|
||||
-- We used to use `shareCommon` here, but is was a bottleneck
|
||||
let levelParams ← getLevelParamsPreDecls preDefs scopeLevelNames allUserLevelNames
|
||||
let us := levelParams.map mkLevelParam
|
||||
let fixExpr (e : Expr) : Expr :=
|
||||
e.replace fun c => match c with
|
||||
| Expr.const declName _ => if preDefs.any fun preDef => preDef.declName == declName then some $ Lean.mkConst declName us else none
|
||||
| _ => none
|
||||
return preDefs.map fun preDef =>
|
||||
{ preDef with
|
||||
type := fixExpr preDef.type,
|
||||
value := fixExpr preDef.value,
|
||||
levelParams := levelParams }
|
||||
|
||||
def applyAttributesOf (preDefs : Array PreDefinition) (applicationTime : AttributeApplicationTime) : TermElabM Unit := do
|
||||
for preDef in preDefs do
|
||||
@@ -210,4 +213,17 @@ def checkCodomainsLevel (preDefs : Array PreDefinition) : MetaM Unit := do
|
||||
m!"for `{preDefs[0]!.declName}` is{indentExpr type₀} : {← inferType type₀}\n" ++
|
||||
m!"and for `{preDefs[i]!.declName}` is{indentExpr typeᵢ} : {← inferType typeᵢ}"
|
||||
|
||||
def shareCommonPreDefs (preDefs : Array PreDefinition) : CoreM (Array PreDefinition) := do
|
||||
profileitM Exception "share common exprs" (← getOptions) do
|
||||
withTraceNode `Elab.def.maxSharing (fun _ => return m!"share common exprs") do
|
||||
let mut es := #[]
|
||||
for preDef in preDefs do
|
||||
es := es.push preDef.type |>.push preDef.value
|
||||
es := ShareCommon.shareCommon' es
|
||||
let mut result := #[]
|
||||
for h : i in [:preDefs.size] do
|
||||
let preDef := preDefs[i]
|
||||
result := result.push { preDef with type := es[2*i]!, value := es[2*i+1]! }
|
||||
return result
|
||||
|
||||
end Lean.Elab
|
||||
|
||||
@@ -333,7 +333,7 @@ def tryContradiction (mvarId : MVarId) : MetaM Bool := do
|
||||
partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
|
||||
let some eqs ← getEqnsFor? declName | throwError "failed to generate equations for '{declName}'"
|
||||
let tryEqns (mvarId : MVarId) : MetaM Bool :=
|
||||
eqs.anyM fun eq => commitWhen do
|
||||
eqs.anyM fun eq => commitWhen do checkpointDefEq (mayPostpone := false) do
|
||||
try
|
||||
let subgoals ← mvarId.apply (← mkConstWithFreshMVarLevels eq)
|
||||
subgoals.allM fun subgoal => do
|
||||
|
||||
@@ -111,10 +111,10 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
|
||||
preDefWith.termination.terminationBy? matches some {structural := true, ..}
|
||||
for preDef in preDefs do
|
||||
if let .some termBy := preDef.termination.terminationBy? then
|
||||
if !preDefsWithout.isEmpty then
|
||||
if !structural && !preDefsWithout.isEmpty then
|
||||
let m := MessageData.andList (preDefsWithout.toList.map (m!"{·.declName}"))
|
||||
let doOrDoes := if preDefsWithout.size = 1 then "does" else "do"
|
||||
logErrorAt termBy.ref (m!"Incomplete set of `termination_by` annotations:\n"++
|
||||
logErrorAt termBy.ref (m!"incomplete set of `termination_by` annotations:\n"++
|
||||
m!"This function is mutually with {m}, which {doOrDoes} not have " ++
|
||||
m!"a `termination_by` clause.\n" ++
|
||||
m!"The present clause is ignored.")
|
||||
@@ -135,13 +135,12 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
|
||||
/--
|
||||
Elaborates the `TerminationHint` in the clique to `TerminationArguments`
|
||||
-/
|
||||
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Option TerminationArguments) := do
|
||||
let tas ← preDefs.mapM fun preDef => do
|
||||
def elabTerminationByHints (preDefs : Array PreDefinition) : TermElabM (Array (Option TerminationArgument)) := do
|
||||
preDefs.mapM fun preDef => do
|
||||
let arity ← lambdaTelescope preDef.value fun xs _ => pure xs.size
|
||||
let hints := preDef.termination
|
||||
hints.terminationBy?.mapM
|
||||
(TerminationArgument.elab preDef.declName preDef.type arity hints.extraParams ·)
|
||||
return tas.sequenceMap id -- only return something if every function has a hint
|
||||
|
||||
def shouldUseStructural (preDefs : Array PreDefinition) : Bool :=
|
||||
preDefs.any fun preDef =>
|
||||
@@ -154,68 +153,70 @@ def shouldUseWF (preDefs : Array PreDefinition) : Bool :=
|
||||
|
||||
|
||||
def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLCtx {} {} do
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition.body] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
let preDefs ← preDefs.mapM ensureNoUnassignedMVarsAtPreDef
|
||||
let preDefs ← betaReduceLetRecApps preDefs
|
||||
let cliques := partitionPreDefs preDefs
|
||||
for preDefs in cliques do
|
||||
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"
|
||||
if preDefs.size == 1 && isNonRecursive preDefs[0]! then
|
||||
/-
|
||||
We must erase `recApp` annotations even when `preDef` is not recursive
|
||||
because it may use another recursive declaration in the same mutual block.
|
||||
See issue #2321
|
||||
-/
|
||||
let preDef ← eraseRecAppSyntax preDefs[0]!
|
||||
ensureEqnReservedNamesAvailable preDef.declName
|
||||
if preDef.modifiers.isNoncomputable then
|
||||
addNonRec preDef
|
||||
else
|
||||
addAndCompileNonRec preDef
|
||||
preDef.termination.ensureNone "not recursive"
|
||||
else if preDefs.any (·.modifiers.isUnsafe) then
|
||||
addAndCompileUnsafe preDefs
|
||||
preDefs.forM (·.termination.ensureNone "unsafe")
|
||||
else if preDefs.any (·.modifiers.isPartial) then
|
||||
profileitM Exception "process pre-definitions" (← getOptions) do
|
||||
withTraceNode `Elab.def.processPreDef (fun _ => return m!"process pre-definitions") do
|
||||
for preDef in preDefs do
|
||||
if preDef.modifiers.isPartial && !(← whnfD preDef.type).isForall then
|
||||
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
|
||||
addAndCompilePartial preDefs
|
||||
preDefs.forM (·.termination.ensureNone "partial")
|
||||
else
|
||||
ensureFunIndReservedNamesAvailable preDefs
|
||||
try
|
||||
checkCodomainsLevel preDefs
|
||||
checkTerminationByHints preDefs
|
||||
let termArgs ← elabTerminationByHints preDefs
|
||||
if shouldUseStructural preDefs then
|
||||
structuralRecursion preDefs termArgs
|
||||
else if shouldUseWF preDefs then
|
||||
wfRecursion preDefs termArgs
|
||||
trace[Elab.definition.body] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
let preDefs ← preDefs.mapM ensureNoUnassignedMVarsAtPreDef
|
||||
let preDefs ← betaReduceLetRecApps preDefs
|
||||
let cliques := partitionPreDefs preDefs
|
||||
for preDefs in cliques do
|
||||
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"
|
||||
if preDefs.size == 1 && isNonRecursive preDefs[0]! then
|
||||
/-
|
||||
We must erase `recApp` annotations even when `preDef` is not recursive
|
||||
because it may use another recursive declaration in the same mutual block.
|
||||
See issue #2321
|
||||
-/
|
||||
let preDef ← eraseRecAppSyntax preDefs[0]!
|
||||
ensureEqnReservedNamesAvailable preDef.declName
|
||||
if preDef.modifiers.isNoncomputable then
|
||||
addNonRec preDef
|
||||
else
|
||||
addAndCompileNonRec preDef
|
||||
preDef.termination.ensureNone "not recursive"
|
||||
else if preDefs.any (·.modifiers.isUnsafe) then
|
||||
addAndCompileUnsafe preDefs
|
||||
preDefs.forM (·.termination.ensureNone "unsafe")
|
||||
else if preDefs.any (·.modifiers.isPartial) then
|
||||
for preDef in preDefs do
|
||||
if preDef.modifiers.isPartial && !(← whnfD preDef.type).isForall then
|
||||
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
|
||||
addAndCompilePartial preDefs
|
||||
preDefs.forM (·.termination.ensureNone "partial")
|
||||
else
|
||||
withRef (preDefs[0]!.ref) <| mapError
|
||||
(orelseMergeErrors
|
||||
(structuralRecursion preDefs termArgs)
|
||||
(wfRecursion preDefs termArgs))
|
||||
(fun msg =>
|
||||
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
|
||||
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
|
||||
catch ex =>
|
||||
logException ex
|
||||
let s ← saveState
|
||||
try
|
||||
if preDefs.all fun preDef => preDef.kind == DefKind.def || preDefs.all fun preDef => preDef.kind == DefKind.abbrev then
|
||||
-- try to add as partial definition
|
||||
ensureFunIndReservedNamesAvailable preDefs
|
||||
try
|
||||
checkCodomainsLevel preDefs
|
||||
checkTerminationByHints preDefs
|
||||
let termArg?s ← elabTerminationByHints preDefs
|
||||
if shouldUseStructural preDefs then
|
||||
structuralRecursion preDefs termArg?s
|
||||
else if shouldUseWF preDefs then
|
||||
wfRecursion preDefs termArg?s
|
||||
else
|
||||
withRef (preDefs[0]!.ref) <| mapError
|
||||
(orelseMergeErrors
|
||||
(structuralRecursion preDefs termArg?s)
|
||||
(wfRecursion preDefs termArg?s))
|
||||
(fun msg =>
|
||||
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
|
||||
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
|
||||
catch ex =>
|
||||
logException ex
|
||||
let s ← saveState
|
||||
try
|
||||
addAndCompilePartial preDefs (useSorry := true)
|
||||
catch _ =>
|
||||
-- Compilation failed try again just as axiom
|
||||
s.restore
|
||||
addAsAxioms preDefs
|
||||
else if preDefs.all fun preDef => preDef.kind == DefKind.theorem then
|
||||
addAsAxioms preDefs
|
||||
catch _ => s.restore
|
||||
if preDefs.all fun preDef => preDef.kind == DefKind.def || preDefs.all fun preDef => preDef.kind == DefKind.abbrev then
|
||||
-- try to add as partial definition
|
||||
try
|
||||
addAndCompilePartial preDefs (useSorry := true)
|
||||
catch _ =>
|
||||
-- Compilation failed try again just as axiom
|
||||
s.restore
|
||||
addAsAxioms preDefs
|
||||
else if preDefs.all fun preDef => preDef.kind == DefKind.theorem then
|
||||
addAsAxioms preDefs
|
||||
catch _ => s.restore
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.body
|
||||
|
||||
@@ -5,11 +5,12 @@ Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Util.HasConstCache
|
||||
import Lean.Meta.PProdN
|
||||
import Lean.Meta.Match.MatcherApp.Transform
|
||||
import Lean.Elab.RecAppSyntax
|
||||
import Lean.Elab.PreDefinition.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.FunPacker
|
||||
import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
@@ -17,51 +18,63 @@ open Meta
|
||||
private def throwToBelowFailed : MetaM α :=
|
||||
throwError "toBelow failed"
|
||||
|
||||
partial def searchPProd (e : Expr) (F : Expr) (k : Expr → Expr → MetaM α) : MetaM α := do
|
||||
match (← whnf e) with
|
||||
| .app (.app (.const `PProd _) d1) d2 =>
|
||||
(do searchPProd d1 (.proj ``PProd 0 F) k)
|
||||
<|> (do searchPProd d2 (.proj ``PProd 1 F) k)
|
||||
| .app (.app (.const `And _) d1) d2 =>
|
||||
(do searchPProd d1 (.proj `And 0 F) k)
|
||||
<|> (do searchPProd d2 (.proj `And 1 F) k)
|
||||
| .const `PUnit _
|
||||
| .const `True _ => throwToBelowFailed
|
||||
| _ => k e F
|
||||
|
||||
/-- See `toBelow` -/
|
||||
private partial def toBelowAux (C : Expr) (belowDict : Expr) (arg : Expr) (F : Expr) : MetaM Expr := do
|
||||
let belowDict ← whnf belowDict
|
||||
trace[Elab.definition.structural] "belowDict: {belowDict}, arg: {arg}"
|
||||
match belowDict with
|
||||
| .app (.app (.const `PProd _) d1) d2 =>
|
||||
(do toBelowAux C d1 arg (← mkAppM `PProd.fst #[F]))
|
||||
<|>
|
||||
(do toBelowAux C d2 arg (← mkAppM `PProd.snd #[F]))
|
||||
| .app (.app (.const `And _) d1) d2 =>
|
||||
(do toBelowAux C d1 arg (← mkAppM `And.left #[F]))
|
||||
<|>
|
||||
(do toBelowAux C d2 arg (← mkAppM `And.right #[F]))
|
||||
| _ => forallTelescopeReducing belowDict fun xs belowDict => do
|
||||
let arg ← zetaReduce arg
|
||||
let argArgs := arg.getAppArgs
|
||||
unless argArgs.size >= xs.size do throwToBelowFailed
|
||||
let n := argArgs.size
|
||||
let argTailArgs := argArgs.extract (n - xs.size) n
|
||||
let belowDict := belowDict.replaceFVars xs argTailArgs
|
||||
match belowDict with
|
||||
| .app belowDictFun belowDictArg =>
|
||||
unless belowDictFun.getAppFn == C do throwToBelowFailed
|
||||
unless ← isDefEq belowDictArg arg do throwToBelowFailed
|
||||
pure (mkAppN F argTailArgs)
|
||||
| _ =>
|
||||
trace[Elab.definition.structural] "belowDict not an app: {belowDict}"
|
||||
throwToBelowFailed
|
||||
trace[Elab.definition.structural] "belowDict start:{indentExpr belowDict}\narg:{indentExpr arg}"
|
||||
-- First search through the PProd packing of the different `brecOn` motives
|
||||
searchPProd belowDict F fun belowDict F => do
|
||||
trace[Elab.definition.structural] "belowDict step 1:{indentExpr belowDict}"
|
||||
-- Then instantiate parameters of a reflexive type, if needed
|
||||
forallTelescopeReducing belowDict fun xs belowDict => do
|
||||
let arg ← zetaReduce arg
|
||||
let argArgs := arg.getAppArgs
|
||||
unless argArgs.size >= xs.size do throwToBelowFailed
|
||||
let n := argArgs.size
|
||||
let argTailArgs := argArgs.extract (n - xs.size) n
|
||||
let belowDict := belowDict.replaceFVars xs argTailArgs
|
||||
-- And again search through the PProd packing due to multiple functions recursing on the
|
||||
-- same inductive data type
|
||||
-- (We could use the funIdx and the `positions` array to replace this search with more
|
||||
-- targeted indexing.)
|
||||
searchPProd belowDict (mkAppN F argTailArgs) fun belowDict F => do
|
||||
trace[Elab.definition.structural] "belowDict step 2:{indentExpr belowDict}"
|
||||
match belowDict with
|
||||
| .app belowDictFun belowDictArg =>
|
||||
unless belowDictFun.getAppFn == C do throwToBelowFailed
|
||||
unless ← isDefEq belowDictArg arg do throwToBelowFailed
|
||||
pure F
|
||||
| _ =>
|
||||
trace[Elab.definition.structural] "belowDict not an app:{indentExpr belowDict}"
|
||||
throwToBelowFailed
|
||||
|
||||
/-- See `toBelow` -/
|
||||
private def withBelowDict [Inhabited α] (below : Expr) (numIndParams : Nat)
|
||||
(positions : Positions) (k : Array Expr → Expr → MetaM α) : MetaM α := do
|
||||
let numIndAll := positions.size
|
||||
let numTypeFormers := positions.size
|
||||
let belowType ← inferType below
|
||||
trace[Elab.definition.structural] "belowType: {belowType}"
|
||||
unless (← isTypeCorrect below) do
|
||||
trace[Elab.definition.structural] "not type correct!"
|
||||
belowType.withApp fun f args => do
|
||||
unless numIndParams + numIndAll < args.size do
|
||||
unless numIndParams + numTypeFormers < args.size do
|
||||
trace[Elab.definition.structural] "unexpected 'below' type{indentExpr belowType}"
|
||||
throwToBelowFailed
|
||||
let params := args[:numIndParams]
|
||||
let finalArgs := args[numIndParams+numIndAll:]
|
||||
let finalArgs := args[numIndParams+numTypeFormers:]
|
||||
let pre := mkAppN f params
|
||||
let motiveTypes ← inferArgumentTypesN numIndAll pre
|
||||
let motiveTypes ← inferArgumentTypesN numTypeFormers pre
|
||||
let numMotives : Nat := positions.numIndices
|
||||
trace[Elab.definition.structural] "numMotives: {numMotives}"
|
||||
let mut CTypes := Array.mkArray numMotives (.sort 37) -- dummy value
|
||||
@@ -72,7 +85,7 @@ private def withBelowDict [Inhabited α] (below : Expr) (numIndParams : Nat)
|
||||
return ((← mkFreshUserName `C), fun _ => pure t)
|
||||
withLocalDeclsD CDecls fun Cs => do
|
||||
-- We have to pack these canary motives like we packed the real motives
|
||||
let packedCs ← positions.mapMwith packMotives motiveTypes Cs
|
||||
let packedCs ← positions.mapMwith PProdN.packLambdas motiveTypes Cs
|
||||
let belowDict := mkAppN pre packedCs
|
||||
let belowDict := mkAppN belowDict finalArgs
|
||||
trace[Elab.definition.structural] "initial belowDict for {Cs}:{indentExpr belowDict}"
|
||||
@@ -133,26 +146,16 @@ private partial def replaceRecApps (recArgInfos : Array RecArgInfo) (positions :
|
||||
e.withApp fun f args => do
|
||||
if let .some fnIdx := recArgInfos.findIdx? (f.isConstOf ·.fnName) then
|
||||
let recArgInfo := recArgInfos[fnIdx]!
|
||||
let numFixed := recArgInfo.numFixed
|
||||
let recArgPos := recArgInfo.recArgPos
|
||||
if recArgPos >= args.size then
|
||||
throwError "insufficient number of parameters at recursive application {indentExpr e}"
|
||||
let recArg := args[recArgPos]!
|
||||
let some recArg := args[recArgInfo.recArgPos]?
|
||||
| throwError "insufficient number of parameters at recursive application {indentExpr e}"
|
||||
-- For reflexive type, we may have nested recursive applications in recArg
|
||||
let recArg ← loop below recArg
|
||||
let f ←
|
||||
try toBelow below recArgInfo.indParams.size positions fnIdx recArg
|
||||
try toBelow below recArgInfo.indGroupInst.params.size positions fnIdx recArg
|
||||
catch _ => throwError "failed to eliminate recursive application{indentExpr e}"
|
||||
-- Recall that the fixed parameters are not in the scope of the `brecOn`. So, we skip them.
|
||||
let argsNonFixed := args.extract numFixed args.size
|
||||
-- The function `f` does not explicitly take `recArg` and its indices as arguments. So, we skip them too.
|
||||
let mut fArgs := #[]
|
||||
for i in [:argsNonFixed.size] do
|
||||
let j := i + numFixed
|
||||
if recArgInfo.recArgPos != j && !recArgInfo.indicesPos.contains j then
|
||||
let arg := argsNonFixed[i]!
|
||||
let arg ← replaceRecApps recArgInfos positions below arg
|
||||
fArgs := fArgs.push arg
|
||||
-- We don't pass the fixed parameters, the indices and the major arg to `f`, only the rest
|
||||
let (_, fArgs) := recArgInfo.pickIndicesMajor args[recArgInfo.numFixed:]
|
||||
let fArgs ← fArgs.mapM (replaceRecApps recArgInfos positions below ·)
|
||||
return mkAppN f fArgs
|
||||
else
|
||||
return mkAppN (← loop below f) (← args.mapM (loop below))
|
||||
@@ -225,36 +228,29 @@ def mkBRecOnF (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
let valueNew ← replaceRecApps recArgInfos positions below value
|
||||
mkLambdaFVars (indexMajorArgs ++ #[below] ++ otherArgs) valueNew
|
||||
|
||||
|
||||
/--
|
||||
Given the `motives`, figures out whether to use `.brecOn` or `.binductionOn`, pass
|
||||
the right universe levels, the parameters, and the motives.
|
||||
It was already checked earlier in `checkCodomainsLevel` that the functions live in the same universe.
|
||||
-/
|
||||
def mkBRecOnConst (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
(motives : Array Expr) : MetaM (Name → Expr) := do
|
||||
-- For now, just look at the first
|
||||
let recArgInfo := recArgInfos[0]!
|
||||
(motives : Array Expr) : MetaM (Nat → Expr) := do
|
||||
let indGroup := recArgInfos[0]!.indGroupInst
|
||||
let motive := motives[0]!
|
||||
let brecOnUniv ← lambdaTelescope motive fun _ type => getLevel type
|
||||
let indInfo ← getConstInfoInduct recArgInfo.indName
|
||||
let indInfo ← getConstInfoInduct indGroup.all[0]!
|
||||
let useBInductionOn := indInfo.isReflexive && brecOnUniv == levelZero
|
||||
let brecOnUniv ←
|
||||
if indInfo.isReflexive && brecOnUniv != levelZero then
|
||||
decLevel brecOnUniv
|
||||
else
|
||||
pure brecOnUniv
|
||||
let brecOnCons := fun n =>
|
||||
let brecOn :=
|
||||
if useBInductionOn then .const (mkBInductionOnName n) recArgInfo.indLevels
|
||||
else .const (mkBRecOnName n) (brecOnUniv :: recArgInfo.indLevels)
|
||||
mkAppN brecOn recArgInfo.indParams
|
||||
|
||||
let brecOnCons := fun idx => indGroup.brecOn useBInductionOn brecOnUniv idx
|
||||
-- Pick one as a prototype
|
||||
let brecOnAux := brecOnCons recArgInfo.indName
|
||||
let brecOnAux := brecOnCons 0
|
||||
-- Infer the type of the packed motive arguments
|
||||
let packedMotiveTypes ← inferArgumentTypesN recArgInfo.indAll.size brecOnAux
|
||||
let packedMotives ← positions.mapMwith packMotives packedMotiveTypes motives
|
||||
let packedMotiveTypes ← inferArgumentTypesN indGroup.numMotives brecOnAux
|
||||
let packedMotives ← positions.mapMwith PProdN.packLambdas packedMotiveTypes motives
|
||||
|
||||
return fun n => mkAppN (brecOnCons n) packedMotives
|
||||
|
||||
@@ -265,17 +261,18 @@ combinators. This assumes that all `.brecOn` functions of a mutual inductive hav
|
||||
It also undoes the permutation and packing done by `packMotives`
|
||||
-/
|
||||
def inferBRecOnFTypes (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
(brecOnConst : Name → Expr) : MetaM (Array Expr) := do
|
||||
(brecOnConst : Nat → Expr) : MetaM (Array Expr) := do
|
||||
let numTypeFormers := positions.size
|
||||
let recArgInfo := recArgInfos[0]! -- pick an arbitrary one
|
||||
let brecOn := brecOnConst recArgInfo.indName
|
||||
let brecOn := brecOnConst 0
|
||||
check brecOn
|
||||
let brecOnType ← inferType brecOn
|
||||
-- Skip the indices and major argument
|
||||
let packedFTypes ← forallBoundedTelescope brecOnType (some (recArgInfo.indicesPos.size + 1)) fun _ brecOnType =>
|
||||
-- And return the types of of the next arguments
|
||||
arrowDomainsN recArgInfo.indAll.size brecOnType
|
||||
arrowDomainsN numTypeFormers brecOnType
|
||||
|
||||
let mut FTypes := Array.mkArray recArgInfos.size (Expr.sort 0)
|
||||
let mut FTypes := Array.mkArray positions.numIndices (Expr.sort 0)
|
||||
for packedFType in packedFTypes, poss in positions do
|
||||
for pos in poss do
|
||||
FTypes := FTypes.set! pos packedFType
|
||||
@@ -285,19 +282,18 @@ def inferBRecOnFTypes (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
Completes the `.brecOn` for the given function.
|
||||
The `value` is the function with (only) the fixed parameters moved into the context.
|
||||
-/
|
||||
def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Name → Expr)
|
||||
def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Nat → Expr)
|
||||
(FArgs : Array Expr) (recArgInfo : RecArgInfo) (value : Expr) : MetaM Expr := do
|
||||
lambdaTelescope value fun ys _value => do
|
||||
let (indexMajorArgs, otherArgs) := recArgInfo.pickIndicesMajor ys
|
||||
let brecOn := brecOnConst recArgInfo.indName
|
||||
let brecOn := brecOnConst recArgInfo.indIdx
|
||||
let brecOn := mkAppN brecOn indexMajorArgs
|
||||
let packedFTypes ← inferArgumentTypesN positions.size brecOn
|
||||
let packedFArgs ← positions.mapMwith packFArgs packedFTypes FArgs
|
||||
let packedFArgs ← positions.mapMwith PProdN.mkLambdas packedFTypes FArgs
|
||||
let brecOn := mkAppN brecOn packedFArgs
|
||||
let some poss := positions.find? (·.contains fnIdx)
|
||||
| throwError "mkBrecOnApp: Could not find {fnIdx} in {positions}"
|
||||
let brecOn ← if poss.size = 1 then pure brecOn else
|
||||
mkPProdProjN (poss.getIdx? fnIdx).get! brecOn
|
||||
let brecOn ← PProdN.proj poss.size (poss.getIdx? fnIdx).get! brecOn
|
||||
mkLambdaFVars ys (mkAppN brecOn otherArgs)
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
@@ -9,46 +9,6 @@ import Lean.Meta.ForEachExpr
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
|
||||
/--
|
||||
Information about the argument of interest of a structurally recursive function.
|
||||
|
||||
The `Expr`s in this data structure expect the `fixedParams` to be in scope, but not the other
|
||||
parameters of the function. This ensures that this data structure makes sense in the other functions
|
||||
of a mutually recursive group.
|
||||
-/
|
||||
structure RecArgInfo where
|
||||
/-- the name of the recursive function -/
|
||||
fnName : Name
|
||||
/-- the fixed prefix of arguments of the function we are trying to justify termination using structural recursion. -/
|
||||
numFixed : Nat
|
||||
/-- position of the argument (counted including fixed prefix) we are recursing on -/
|
||||
recArgPos : Nat
|
||||
/-- position of the indices (counted including fixed prefix) of the inductive datatype indices we are recursing on -/
|
||||
indicesPos : Array Nat
|
||||
/-- inductive datatype name of the argument we are recursing on -/
|
||||
indName : Name
|
||||
/-- inductive datatype universe levels of the argument we are recursing on -/
|
||||
indLevels : List Level
|
||||
/-- inductive datatype parameters of the argument we are recursing on -/
|
||||
indParams : Array Expr
|
||||
/-- The types mutually inductive with indName -/
|
||||
indAll : Array Name
|
||||
deriving Inhabited
|
||||
/--
|
||||
If `xs` are the parameters of the functions (excluding fixed prefix), partitions them
|
||||
into indices and major arguments, and other parameters.
|
||||
-/
|
||||
def RecArgInfo.pickIndicesMajor (info : RecArgInfo) (xs : Array Expr) : (Array Expr × Array Expr) := Id.run do
|
||||
let mut indexMajorArgs := #[]
|
||||
let mut otherArgs := #[]
|
||||
for h : i in [:xs.size] do
|
||||
let j := i + info.numFixed
|
||||
if j = info.recArgPos || info.indicesPos.contains j then
|
||||
indexMajorArgs := indexMajorArgs.push xs[i]
|
||||
else
|
||||
otherArgs := otherArgs.push xs[i]
|
||||
return (indexMajorArgs, otherArgs)
|
||||
|
||||
structure State where
|
||||
/-- As part of the inductive predicates case, we keep adding more and more discriminants from the
|
||||
local context and build up a bigger matcher application until we reach a fixed point.
|
||||
@@ -91,10 +51,11 @@ and for each such type, keep track of the order of the functions.
|
||||
|
||||
We represent these positions as an `Array (Array Nat)`. We have that
|
||||
|
||||
* `positions.size = indInfo.all.length`
|
||||
* `positions.size = indInfo.numTypeFormers`
|
||||
* `positions.flatten` is a permutation of `[0:n]`, so each of the `n` functions has exactly one
|
||||
position, and each position refers to one of the `n` functions.
|
||||
* if `k ∈ positions[i]` then the recursive argument of function `k` is has type `indInfo.all[i]`
|
||||
(or corresponding nested inductive type)
|
||||
|
||||
-/
|
||||
abbrev Positions := Array (Array Nat)
|
||||
@@ -127,3 +88,6 @@ def Positions.mapMwith {α β m} [Monad m] [Inhabited β] (f : α → Array β
|
||||
(Array.zip ys positions).mapM fun ⟨y, poss⟩ => f y (poss.map (xs[·]!))
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
builtin_initialize
|
||||
Lean.registerTraceClass `Elab.definition.structural
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Structural
|
||||
structure EqnInfo extends EqnInfoCore where
|
||||
recArgPos : Nat
|
||||
declNames : Array Name
|
||||
numFixed : Nat
|
||||
deriving Inhabited
|
||||
|
||||
private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
|
||||
@@ -81,9 +82,11 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
|
||||
|
||||
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo ← mkMapDeclarationExtension
|
||||
|
||||
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat) : CoreM Unit := do
|
||||
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat)
|
||||
(numFixed : Nat) : CoreM Unit := do
|
||||
ensureEqnReservedNamesAvailable preDef.declName
|
||||
modifyEnv fun env => eqnInfoExt.insert env preDef.declName { preDef with recArgPos, declNames }
|
||||
modifyEnv fun env => eqnInfoExt.insert env preDef.declName
|
||||
{ preDef with recArgPos, declNames, numFixed }
|
||||
|
||||
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
|
||||
if let some info := eqnInfoExt.find? (← getEnv) declName then
|
||||
|
||||
@@ -4,11 +4,31 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.PreDefinition.TerminationArgument
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
|
||||
def prettyParam (xs : Array Expr) (i : Nat) : MetaM MessageData := do
|
||||
let x := xs[i]!
|
||||
let n ← x.fvarId!.getUserName
|
||||
addMessageContextFull <| if n.hasMacroScopes then m!"#{i+1}" else m!"{x}"
|
||||
|
||||
def prettyRecArg (xs : Array Expr) (value : Expr) (recArgInfo : RecArgInfo) : MetaM MessageData := do
|
||||
lambdaTelescope value fun ys _ => prettyParam (xs ++ ys) recArgInfo.recArgPos
|
||||
|
||||
def prettyParameterSet (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
|
||||
(recArgInfos : Array RecArgInfo) : MetaM MessageData := do
|
||||
if fnNames.size = 1 then
|
||||
return m!"parameter " ++ (← prettyRecArg xs values[0]! recArgInfos[0]!)
|
||||
else
|
||||
let mut l := #[]
|
||||
for fnName in fnNames, value in values, recArgInfo in recArgInfos do
|
||||
l := l.push m!"{(← prettyRecArg xs value recArgInfo)} of {fnName}"
|
||||
return m!"parameters " ++ .andList l.toList
|
||||
|
||||
private def getIndexMinPos (xs : Array Expr) (indices : Array Expr) : Nat := Id.run do
|
||||
let mut minPos := xs.size
|
||||
for index in indices do
|
||||
@@ -48,9 +68,7 @@ def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) :
|
||||
throwError "it is a let-binding"
|
||||
let xType ← whnfD localDecl.type
|
||||
matchConstInduct xType.getAppFn (fun _ => throwError "its type is not an inductive") fun indInfo us => do
|
||||
if !(← hasConst (mkBRecOnName indInfo.name)) then
|
||||
throwError "its type {indInfo.name} does not have a recursor"
|
||||
else if indInfo.isReflexive && !(← hasConst (mkBInductionOnName indInfo.name)) && !(← isInductivePredicate indInfo.name) then
|
||||
if indInfo.isReflexive && !(← hasConst (mkBInductionOnName indInfo.name)) && !(← isInductivePredicate indInfo.name) then
|
||||
throwError "its type {indInfo.name} is a reflexive inductive, but {mkBInductionOnName indInfo.name} does not exist and it is not an inductive predicate"
|
||||
else
|
||||
let indArgs : Array Expr := xType.getAppArgs
|
||||
@@ -72,60 +90,195 @@ def getRecArgInfo (fnName : Name) (numFixed : Nat) (xs : Array Expr) (i : Nat) :
|
||||
| some (indParam, y) =>
|
||||
throwError "its type is an inductive datatype{indentExpr xType}\nand the datatype parameter{indentExpr indParam}\ndepends on the function parameter{indentExpr y}\nwhich does not come before the varying parameters and before the indices of the recursion parameter."
|
||||
| none =>
|
||||
let indAll := indInfo.all.toArray
|
||||
let .some indIdx := indAll.indexOf? indInfo.name | panic! "{indInfo.name} not in {indInfo.all}"
|
||||
let indicesPos := indIndices.map fun index => match xs.indexOf? index with | some i => i.val | none => unreachable!
|
||||
return { fnName := fnName
|
||||
numFixed := numFixed
|
||||
recArgPos := i
|
||||
indicesPos := indicesPos
|
||||
indName := indInfo.name
|
||||
indLevels := us
|
||||
indParams := indParams
|
||||
indAll := indInfo.all.toArray }
|
||||
let indGroupInst := {
|
||||
IndGroupInfo.ofInductiveVal indInfo with
|
||||
levels := us
|
||||
params := indParams }
|
||||
return { fnName := fnName
|
||||
numFixed := numFixed
|
||||
recArgPos := i
|
||||
indicesPos := indicesPos
|
||||
indGroupInst := indGroupInst
|
||||
indIdx := indIdx }
|
||||
else
|
||||
throwError "the index #{i+1} exceeds {xs.size}, the number of parameters"
|
||||
|
||||
/--
|
||||
Runs `k` on all argument indices, until it succeeds.
|
||||
We use this argument to justify termination using the auxiliary `brecOn` construction.
|
||||
Collects the `RecArgInfos` for one function, and returns a report for why the others were not
|
||||
considered.
|
||||
|
||||
We give preference for arguments that are *not* indices of inductive types of other arguments.
|
||||
See issue #837 for an example where we can show termination using the index of an inductive family, but
|
||||
we don't get the desired definitional equalities.
|
||||
The `xs` are the fixed parameters, `value` the body with the fixed prefix instantiated.
|
||||
|
||||
`value` is the function value (including fixed parameters)
|
||||
Takes the optional user annotations into account (`termArg?`). If this is given and the argument
|
||||
is unsuitable, throw an error.
|
||||
-/
|
||||
partial def tryAllArgs (value : Expr) (k : Nat → M α) : M α := do
|
||||
-- It's improtant to keep the call to `k` outside the scope of `lambdaTelescope`:
|
||||
-- The tactics in the IndPred construction search the full local context, so we must not have
|
||||
-- extra FVars there
|
||||
let (indices, nonIndices) ← lambdaTelescope value fun xs _ => do
|
||||
let indicesRef : IO.Ref (Array Nat) ← IO.mkRef {}
|
||||
for x in xs do
|
||||
let xType ← inferType x
|
||||
/- Traverse all sub-expressions in the type of `x` -/
|
||||
forEachExpr xType fun e =>
|
||||
/- If `e` is an inductive family, we store in `indicesRef` all variables in `xs` that occur in "index positions". -/
|
||||
matchConstInduct e.getAppFn (fun _ => pure ()) fun info _ => do
|
||||
if info.numIndices > 0 && info.numParams + info.numIndices == e.getAppNumArgs then
|
||||
for arg in e.getAppArgs[info.numParams:] do
|
||||
forEachExpr arg fun e => do
|
||||
if let .some idx := xs.getIdx? e then
|
||||
indicesRef.modify (·.push idx)
|
||||
let indices ← indicesRef.get
|
||||
let nonIndices := (Array.range xs.size).filter (fun i => !(indices.contains i))
|
||||
return (indices, nonIndices)
|
||||
def getRecArgInfos (fnName : Name) (xs : Array Expr) (value : Expr)
|
||||
(termArg? : Option TerminationArgument) : MetaM (Array RecArgInfo × MessageData) := do
|
||||
lambdaTelescope value fun ys _ => do
|
||||
if let .some termArg := termArg? then
|
||||
-- User explictly asked to use a certain argument, so throw errors eagerly
|
||||
let recArgInfo ← withRef termArg.ref do
|
||||
mapError (f := (m!"cannot use specified parameter for structural recursion:{indentD ·}")) do
|
||||
getRecArgInfo fnName xs.size (xs ++ ys) (← termArg.structuralArg)
|
||||
return (#[recArgInfo], m!"")
|
||||
else
|
||||
let mut recArgInfos := #[]
|
||||
let mut report : MessageData := m!""
|
||||
-- No `termination_by`, so try all, and remember the errors
|
||||
for idx in [:xs.size + ys.size] do
|
||||
try
|
||||
let recArgInfo ← getRecArgInfo fnName xs.size (xs ++ ys) idx
|
||||
recArgInfos := recArgInfos.push recArgInfo
|
||||
catch e =>
|
||||
report := report ++ (m!"Not considering parameter {← prettyParam (xs ++ ys) idx} of {fnName}:" ++
|
||||
indentD e.toMessageData) ++ "\n"
|
||||
trace[Elab.definition.structural] "getRecArgInfos report: {report}"
|
||||
return (recArgInfos, report)
|
||||
|
||||
let mut errors : Array MessageData := Array.mkArray (indices.size + nonIndices.size) m!""
|
||||
let saveState ← get -- backtrack the state for each argument
|
||||
for i in id (nonIndices ++ indices) do
|
||||
trace[Elab.definition.structural] "findRecArg i: {i}"
|
||||
try
|
||||
set saveState
|
||||
return (← k i)
|
||||
catch e => errors := errors.set! i e.toMessageData
|
||||
throwError
|
||||
errors.foldl
|
||||
(init := m!"structural recursion cannot be used:")
|
||||
(f := (· ++ Format.line ++ Format.line ++ .))
|
||||
|
||||
/--
|
||||
Reorders the `RecArgInfos` of one function to put arguments that are indices of other arguments
|
||||
last.
|
||||
See issue #837 for an example where we can show termination using the index of an inductive family, but
|
||||
we don't get the desired definitional equalities.
|
||||
-/
|
||||
def nonIndicesFirst (recArgInfos : Array RecArgInfo) : Array RecArgInfo := Id.run do
|
||||
let mut indicesPos : HashSet Nat := {}
|
||||
for recArgInfo in recArgInfos do
|
||||
for pos in recArgInfo.indicesPos do
|
||||
indicesPos := indicesPos.insert pos
|
||||
let (indices,nonIndices) := recArgInfos.partition (indicesPos.contains ·.recArgPos)
|
||||
return nonIndices ++ indices
|
||||
|
||||
private def dedup [Monad m] (eq : α → α → m Bool) (xs : Array α) : m (Array α) := do
|
||||
let mut ret := #[]
|
||||
for x in xs do
|
||||
unless (← ret.anyM (eq · x)) do
|
||||
ret := ret.push x
|
||||
return ret
|
||||
|
||||
/--
|
||||
Given the `RecArgInfo`s of all the recursive functions, find the inductive groups to consider.
|
||||
-/
|
||||
def inductiveGroups (recArgInfos : Array RecArgInfo) : MetaM (Array IndGroupInst) :=
|
||||
dedup IndGroupInst.isDefEq (recArgInfos.map (·.indGroupInst))
|
||||
|
||||
/--
|
||||
Filters the `recArgInfos` by those that describe an argument that's part of the recursive inductive
|
||||
group `group`.
|
||||
|
||||
Because of nested inductives this function has the ability to change the `recArgInfo`.
|
||||
Consider
|
||||
```
|
||||
inductive Tree where | node : List Tree → Tree
|
||||
```
|
||||
then when we look for arguments whose type is part of the group `Tree`, we want to also consider
|
||||
the argument of type `List Tree`, even though that argument’s `RecArgInfo` refers to initially to
|
||||
`List`.
|
||||
-/
|
||||
def argsInGroup (group : IndGroupInst) (xs : Array Expr) (value : Expr)
|
||||
(recArgInfos : Array RecArgInfo) : MetaM (Array RecArgInfo) := do
|
||||
|
||||
let nestedTypeFormers ← group.nestedTypeFormers
|
||||
|
||||
recArgInfos.filterMapM fun recArgInfo => do
|
||||
-- Is this argument from the same mutual group of inductives?
|
||||
if (← group.isDefEq recArgInfo.indGroupInst) then
|
||||
return (.some recArgInfo)
|
||||
|
||||
-- Can this argument be understood as the auxillary type former of a nested inductive?
|
||||
if nestedTypeFormers.isEmpty then return .none
|
||||
lambdaTelescope value fun ys _ => do
|
||||
let x := (xs++ys)[recArgInfo.recArgPos]!
|
||||
for nestedTypeFormer in nestedTypeFormers, indIdx in [group.all.size : group.numMotives] do
|
||||
let xType ← whnfD (← inferType x)
|
||||
let (indIndices, _, type) ← forallMetaTelescope nestedTypeFormer
|
||||
if (← isDefEqGuarded type xType) then
|
||||
let indIndices ← indIndices.mapM instantiateMVars
|
||||
if !indIndices.all Expr.isFVar then
|
||||
-- throwError "indices are not variables{indentExpr xType}"
|
||||
continue
|
||||
if !indIndices.allDiff then
|
||||
-- throwError "indices are not pairwise distinct{indentExpr xType}"
|
||||
continue
|
||||
-- TODO: Do we have to worry about the indices ending up in the fixed prefix here?
|
||||
if let some (_index, _y) ← hasBadIndexDep? ys indIndices then
|
||||
-- throwError "its type {indInfo.name} is an inductive family{indentExpr xType}\nand index{indentExpr index}\ndepends on the non index{indentExpr y}"
|
||||
continue
|
||||
let indicesPos := indIndices.map fun index => match (xs++ys).indexOf? index with | some i => i.val | none => unreachable!
|
||||
return .some
|
||||
{ fnName := recArgInfo.fnName
|
||||
numFixed := recArgInfo.numFixed
|
||||
recArgPos := recArgInfo.recArgPos
|
||||
indicesPos := indicesPos
|
||||
indGroupInst := group
|
||||
indIdx := indIdx }
|
||||
return .none
|
||||
|
||||
def maxCombinationSize : Nat := 10
|
||||
|
||||
def allCombinations (xss : Array (Array α)) : Option (Array (Array α)) :=
|
||||
if xss.foldl (· * ·.size) 1 > maxCombinationSize then
|
||||
none
|
||||
else
|
||||
let rec go i acc : Array (Array α):=
|
||||
if h : i < xss.size then
|
||||
xss[i].concatMap fun x => go (i + 1) (acc.push x)
|
||||
else
|
||||
#[acc]
|
||||
some (go 0 #[])
|
||||
|
||||
|
||||
def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
|
||||
(termArg?s : Array (Option TerminationArgument)) (k : Array RecArgInfo → M α) : M α := do
|
||||
let mut report := m!""
|
||||
-- Gather information on all possible recursive arguments
|
||||
let mut recArgInfoss := #[]
|
||||
for fnName in fnNames, value in values, termArg? in termArg?s do
|
||||
let (recArgInfos, thisReport) ← getRecArgInfos fnName xs value termArg?
|
||||
report := report ++ thisReport
|
||||
recArgInfoss := recArgInfoss.push recArgInfos
|
||||
-- Put non-indices first
|
||||
recArgInfoss := recArgInfoss.map nonIndicesFirst
|
||||
trace[Elab.definition.structural] "recArgInfoss: {recArgInfoss.map (·.map (·.recArgPos))}"
|
||||
-- Inductive groups to consider
|
||||
let groups ← inductiveGroups recArgInfoss.flatten
|
||||
trace[Elab.definition.structural] "inductive groups: {groups}"
|
||||
if groups.isEmpty then
|
||||
report := report ++ "no parameters suitable for structural recursion"
|
||||
-- Consider each group
|
||||
for group in groups do
|
||||
-- Select those RecArgInfos that are compatible with this inductive group
|
||||
let mut recArgInfoss' := #[]
|
||||
for value in values, recArgInfos in recArgInfoss do
|
||||
recArgInfoss' := recArgInfoss'.push (← argsInGroup group xs value recArgInfos)
|
||||
if let some idx := recArgInfoss'.findIdx? (·.isEmpty) then
|
||||
report := report ++ m!"Skipping arguments of type {group}, as {fnNames[idx]!} has no compatible argument.\n"
|
||||
continue
|
||||
if let some combs := allCombinations recArgInfoss' then
|
||||
for comb in combs do
|
||||
try
|
||||
-- Check that the group actually has a brecOn (we used to check this in getRecArgInfo,
|
||||
-- but in the first phase we do not want to rule-out non-recursive types like `Array`, which
|
||||
-- are ok in a nested group. This logic can maybe simplified)
|
||||
unless (← hasConst (group.brecOnName false 0)) do
|
||||
throwError "the type {group} does not have a `.brecOn` recursor"
|
||||
-- TODO: Here we used to save and restore the state. But should the `try`-`catch`
|
||||
-- not suffice?
|
||||
let r ← k comb
|
||||
trace[Elab.definition.structural] "tryAllArgs report:\n{report}"
|
||||
return r
|
||||
catch e =>
|
||||
let m ← prettyParameterSet fnNames xs values comb
|
||||
report := report ++ m!"Cannot use {m}:{indentD e.toMessageData}\n"
|
||||
else
|
||||
report := report ++ m!"Too many possible combinations of parameters of type {group} (or " ++
|
||||
m!"please indicate the recursive argument explicitly using `termination_by structural`).\n"
|
||||
report := m!"failed to infer structural recursion:\n" ++ report
|
||||
trace[Elab.definition.structural] "tryAllArgs:\n{report}"
|
||||
throwError report
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.InferType
|
||||
|
||||
/-!
|
||||
This module contains the logic that packs the motives and FArgs of multiple functions into one,
|
||||
to allow structural mutual recursion where the number of functions is not exactly the same
|
||||
as the number of inductive data types in the mutual inductive group.
|
||||
|
||||
The private helper functions related to `PProd` here should at some point be moved to their own
|
||||
module, so that they can be used elsewhere (e.g. `FunInd`), and possibly unified with the similar
|
||||
constructions for well-founded recursion (see `ArgsPacker` module).
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
|
||||
private def mkPUnit : Level → Expr
|
||||
| .zero => .const ``True []
|
||||
| lvl => .const ``PUnit [lvl]
|
||||
|
||||
private def mkPProd (e1 e2 : Expr) : MetaM Expr := do
|
||||
let lvl1 ← getLevel e1
|
||||
let lvl2 ← getLevel e2
|
||||
if lvl1 matches .zero && lvl2 matches .zero then
|
||||
return mkApp2 (.const `And []) e1 e2
|
||||
else
|
||||
return mkApp2 (.const ``PProd [lvl1, lvl2]) e1 e2
|
||||
|
||||
private def mkNProd (lvl : Level) (es : Array Expr) : MetaM Expr :=
|
||||
es.foldrM (init := mkPUnit lvl) mkPProd
|
||||
|
||||
private def mkPUnitMk : Level → Expr
|
||||
| .zero => .const ``True.intro []
|
||||
| lvl => .const ``PUnit.unit [lvl]
|
||||
|
||||
private def mkPProdMk (e1 e2 : Expr) : MetaM Expr := do
|
||||
let t1 ← inferType e1
|
||||
let t2 ← inferType e2
|
||||
let lvl1 ← getLevel t1
|
||||
let lvl2 ← getLevel t2
|
||||
if lvl1 matches .zero && lvl2 matches .zero then
|
||||
return mkApp4 (.const ``And.intro []) t1 t2 e1 e2
|
||||
else
|
||||
return mkApp4 (.const ``PProd.mk [lvl1, lvl2]) t1 t2 e1 e2
|
||||
|
||||
private def mkNProdMk (lvl : Level) (es : Array Expr) : MetaM Expr :=
|
||||
es.foldrM (init := mkPUnitMk lvl) mkPProdMk
|
||||
|
||||
/-- `PProd.fst` or `And.left` (as projections) -/
|
||||
private def mkPProdFst (e : Expr) : MetaM Expr := do
|
||||
let t ← whnf (← inferType e)
|
||||
match_expr t with
|
||||
| PProd _ _ => return .proj ``PProd 0 e
|
||||
| And _ _ => return .proj ``And 0 e
|
||||
| _ => throwError "Cannot project .1 out of{indentExpr e}\nof type{indentExpr t}"
|
||||
|
||||
/-- `PProd.snd` or `And.right` (as projections) -/
|
||||
private def mkPProdSnd (e : Expr) : MetaM Expr := do
|
||||
let t ← whnf (← inferType e)
|
||||
match_expr t with
|
||||
| PProd _ _ => return .proj ``PProd 1 e
|
||||
| And _ _ => return .proj ``And 1 e
|
||||
| _ => throwError "Cannot project .2 out of{indentExpr e}\nof type{indentExpr t}"
|
||||
|
||||
/-- Given a proof of `P₁ ∧ … ∧ Pᵢ ∧ … ∧ Pₙ ∧ True`, return the proof of `Pᵢ` -/
|
||||
def mkPProdProjN (i : Nat) (e : Expr) : MetaM Expr := do
|
||||
let mut value := e
|
||||
for _ in [:i] do
|
||||
value ← mkPProdSnd value
|
||||
value ← mkPProdFst value
|
||||
return value
|
||||
|
||||
/--
|
||||
Combines motives from different functions that recurse on the same parameter type into a single
|
||||
function returning a `PProd` type.
|
||||
|
||||
For example
|
||||
```
|
||||
packMotives (Nat → Sort u) #[(fun (n : Nat) => Nat), (fun (n : Nat) => Fin n -> Fin n )]
|
||||
```
|
||||
will return
|
||||
```
|
||||
fun (n : Nat) (PProd Nat (Fin n → Fin n))
|
||||
```
|
||||
|
||||
It is the identity if `motives.size = 1`.
|
||||
|
||||
It returns a dummy motive `(xs : ) → PUnit` or `(xs : … ) → True` if no motive is given.
|
||||
(this is the reason we need the expected type in the `motiveType` parameter).
|
||||
|
||||
-/
|
||||
def packMotives (motiveType : Expr) (motives : Array Expr) : MetaM Expr := do
|
||||
if motives.size = 1 then
|
||||
return motives[0]!
|
||||
trace[Elab.definition.structural] "packing Motives\nexpected: {motiveType}\nmotives: {motives}"
|
||||
forallTelescope motiveType fun xs sort => do
|
||||
unless sort.isSort do
|
||||
throwError "packMotives: Unexpected motiveType {motiveType}"
|
||||
-- NB: Use beta, not instantiateLambda; when constructing the belowDict below
|
||||
-- we pass `C`, a plain FVar, here
|
||||
let motives := motives.map (·.beta xs)
|
||||
let packedMotives ← mkNProd sort.sortLevel! motives
|
||||
mkLambdaFVars xs packedMotives
|
||||
|
||||
/--
|
||||
Combines the F-args from different functions that recurse on the same parameter type into a single
|
||||
function returning a `PProd` value. See `packMotives`
|
||||
|
||||
It is the identity if `motives.size = 1`.
|
||||
-/
|
||||
def packFArgs (FArgType : Expr) (FArgs : Array Expr) : MetaM Expr := do
|
||||
if FArgs.size = 1 then
|
||||
return FArgs[0]!
|
||||
forallTelescope FArgType fun xs body => do
|
||||
let lvl ← getLevel body
|
||||
let FArgs := FArgs.map (·.beta xs)
|
||||
let packedFArgs ← mkNProdMk lvl FArgs
|
||||
mkLambdaFVars xs packedFArgs
|
||||
|
||||
|
||||
end Lean.Elab.Structural
|
||||
110
src/Lean/Elab/PreDefinition/Structural/IndGroupInfo.lean
Normal file
110
src/Lean/Elab/PreDefinition/Structural/IndGroupInfo.lean
Normal file
@@ -0,0 +1,110 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.InferType
|
||||
|
||||
/-!
|
||||
This module contains 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.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Lean Meta
|
||||
|
||||
/--
|
||||
A mutually inductive group, identified by the `all` array of the `InductiveVal` of its
|
||||
constituents.
|
||||
-/
|
||||
structure IndGroupInfo where
|
||||
all : Array Name
|
||||
numNested : Nat
|
||||
deriving BEq, Inhabited
|
||||
|
||||
def IndGroupInfo.ofInductiveVal (indInfo : InductiveVal) : IndGroupInfo where
|
||||
all := indInfo.all.toArray
|
||||
numNested := indInfo.numNested
|
||||
|
||||
def IndGroupInfo.numMotives (group : IndGroupInfo) : Nat :=
|
||||
group.all.size + group.numNested
|
||||
|
||||
/-- Instantiates the right `.brecOn` or `.bInductionOn` for the given type former index,
|
||||
including universe parameters and fixed prefix. -/
|
||||
partial def IndGroupInfo.brecOnName (info : IndGroupInfo) (ind : Bool) (idx : Nat) : Name :=
|
||||
if let .some n := info.all[idx]? then
|
||||
if ind then mkBInductionOnName n
|
||||
else mkBRecOnName n
|
||||
else
|
||||
let j := idx - info.all.size + 1
|
||||
info.brecOnName ind 0 |>.appendIndexAfter j
|
||||
|
||||
/--
|
||||
An instance of an mutually inductive group of inductives, identified by the `all` array
|
||||
and the level and expressions parameters.
|
||||
|
||||
For example this distinguishes between `List α` and `List β` so that we will not even attempt
|
||||
mutual structural recursion on such incompatible types.
|
||||
-/
|
||||
structure IndGroupInst extends IndGroupInfo where
|
||||
levels : List Level
|
||||
params : Array Expr
|
||||
deriving Inhabited
|
||||
|
||||
def IndGroupInst.toMessageData (igi : IndGroupInst) : MessageData :=
|
||||
mkAppN (.const igi.all[0]! igi.levels) igi.params
|
||||
|
||||
instance : ToMessageData IndGroupInst where
|
||||
toMessageData := IndGroupInst.toMessageData
|
||||
|
||||
def IndGroupInst.isDefEq (igi1 igi2 : IndGroupInst) : MetaM Bool := do
|
||||
unless igi1.toIndGroupInfo == igi2.toIndGroupInfo do return false
|
||||
unless igi1.levels.length = igi2.levels.length do return false
|
||||
unless (igi1.levels.zip igi2.levels).all (fun (l₁, l₂) => Level.isEquiv l₁ l₂) do return false
|
||||
unless igi1.params.size = igi2.params.size do return false
|
||||
unless (← (igi1.params.zip igi2.params).allM (fun (e₁, e₂) => Meta.isDefEqGuarded e₁ e₂)) do return false
|
||||
return true
|
||||
|
||||
/-- Instantiates the right `.brecOn` or `.bInductionOn` for the given type former index,
|
||||
including universe parameters and fixed prefix. -/
|
||||
def IndGroupInst.brecOn (group : IndGroupInst) (ind : Bool) (lvl : Level) (idx : Nat) : Expr :=
|
||||
let n := group.brecOnName ind idx
|
||||
let us := if ind then group.levels else lvl :: group.levels
|
||||
mkAppN (.const n us) group.params
|
||||
|
||||
/--
|
||||
Figures out the nested type formers of an inductive group, with parameters instantiated
|
||||
and indices still forall-abstracted.
|
||||
|
||||
For example given a nested inductive
|
||||
```
|
||||
inductive Tree α where | node : α → Vector (Tree α) n → Tree α
|
||||
```
|
||||
(where `n` is an index of `Vector`) and the instantiation `Tree Int` it will return
|
||||
```
|
||||
#[(n : Nat) → Vector (Tree Int) n]
|
||||
```
|
||||
|
||||
-/
|
||||
def IndGroupInst.nestedTypeFormers (igi : IndGroupInst) : MetaM (Array Expr) := do
|
||||
if igi.numNested = 0 then return #[]
|
||||
-- We extract this information from the motives of the recursor
|
||||
let recName := mkRecName igi.all[0]!
|
||||
let recInfo ← getConstInfoRec recName
|
||||
assert! recInfo.numMotives = igi.numMotives
|
||||
let aux := mkAppN (.const recName (0 :: igi.levels)) igi.params
|
||||
let motives ← inferArgumentTypesN recInfo.numMotives aux
|
||||
let auxMotives : Array Expr := motives[igi.all.size:]
|
||||
auxMotives.mapM fun motive =>
|
||||
forallTelescopeReducing motive fun xs _ => do
|
||||
assert! xs.size > 0
|
||||
mkForallFVars xs.pop (← inferType xs.back)
|
||||
|
||||
end Lean.Elab.Structural
|
||||
@@ -7,12 +7,40 @@ prelude
|
||||
import Lean.Meta.IndPredBelow
|
||||
import Lean.Elab.PreDefinition.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.Basic
|
||||
import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
|
||||
private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (motive : Expr) (e : Expr) : M Expr := do
|
||||
let maxDepth := IndPredBelow.maxBackwardChainingDepth.get (← getOptions)
|
||||
private def replaceIndPredRecApp (numFixed : Nat) (funType : Expr) (e : Expr) : M Expr := do
|
||||
withoutProofIrrelevance do
|
||||
withTraceNode `Elab.definition.structural (fun _ => pure m!"eliminating recursive call {e}") do
|
||||
-- We want to replace `e` with an expression of the same type
|
||||
let main ← mkFreshExprSyntheticOpaqueMVar (← inferType e)
|
||||
let args : Array Expr := e.getAppArgs[numFixed:]
|
||||
let lctx ← getLCtx
|
||||
let r ← lctx.anyM fun localDecl => do
|
||||
if localDecl.isAuxDecl then return false
|
||||
let (mvars, _, t) ← forallMetaTelescope localDecl.type -- NB: do not reduce, we want to see the `funType`
|
||||
unless t.getAppFn == funType do return false
|
||||
withTraceNodeBefore `Elab.definition.structural (do pure m!"trying {mkFVar localDecl.fvarId} : {localDecl.type}") do
|
||||
if args.size < t.getAppNumArgs then
|
||||
trace[Elab.definition.structural] "too few arguments. Underapplied recursive call?"
|
||||
return false
|
||||
if (← (t.getAppArgs.zip args).allM (fun (t,s) => isDefEq t s)) then
|
||||
main.mvarId!.assign (mkAppN (mkAppN localDecl.toExpr mvars) args[t.getAppNumArgs:])
|
||||
return ← mvars.allM fun v => do
|
||||
unless (← v.mvarId!.isAssigned) do
|
||||
trace[Elab.definition.structural] "Cannot use {mkFVar localDecl.fvarId}: parameter {v} remains unassigned"
|
||||
return false
|
||||
return true
|
||||
trace[Elab.definition.structural] "Arguments do not match"
|
||||
return false
|
||||
unless r do
|
||||
throwError "Could not eliminate recursive call {e}"
|
||||
instantiateMVars main
|
||||
|
||||
private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (funType : Expr) (motive : Expr) (e : Expr) : M Expr := do
|
||||
let rec loop (e : Expr) : M Expr := do
|
||||
match e with
|
||||
| Expr.lam n d b c =>
|
||||
@@ -34,12 +62,7 @@ private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (motive : Ex
|
||||
let processApp (e : Expr) : M Expr := do
|
||||
e.withApp fun f args => do
|
||||
if f.isConstOf recArgInfo.fnName then
|
||||
let ty ← inferType e
|
||||
let main ← mkFreshExprSyntheticOpaqueMVar ty
|
||||
if (← IndPredBelow.backwardsChaining main.mvarId! maxDepth) then
|
||||
pure main
|
||||
else
|
||||
throwError "could not solve using backwards chaining {MessageData.ofGoal main.mvarId!}"
|
||||
replaceIndPredRecApp recArgInfo.numFixed funType e
|
||||
else
|
||||
return mkAppN (← loop f) (← args.mapM loop)
|
||||
match (← matchMatcherApp? e) with
|
||||
@@ -78,33 +101,36 @@ def mkIndPredBRecOn (recArgInfo : RecArgInfo) (value : Expr) : M Expr := do
|
||||
let type := (← inferType value).headBeta
|
||||
let (indexMajorArgs, otherArgs) := recArgInfo.pickIndicesMajor ys
|
||||
trace[Elab.definition.structural] "numFixed: {recArgInfo.numFixed}, indexMajorArgs: {indexMajorArgs}, otherArgs: {otherArgs}"
|
||||
let motive ← mkForallFVars otherArgs type
|
||||
let motive ← mkLambdaFVars indexMajorArgs motive
|
||||
trace[Elab.definition.structural] "brecOn motive: {motive}"
|
||||
let brecOn := Lean.mkConst (mkBRecOnName recArgInfo.indName) recArgInfo.indLevels
|
||||
let brecOn := mkAppN brecOn recArgInfo.indParams
|
||||
let brecOn := mkApp brecOn motive
|
||||
let brecOn := mkAppN brecOn indexMajorArgs
|
||||
check brecOn
|
||||
let brecOnType ← inferType brecOn
|
||||
trace[Elab.definition.structural] "brecOn {brecOn}"
|
||||
trace[Elab.definition.structural] "brecOnType {brecOnType}"
|
||||
-- we need to close the telescope here, because the local context is used:
|
||||
-- The root cause was, that this copied code puts an ih : FType into the
|
||||
-- local context and later, when we use the local context to build the recursive
|
||||
-- call, it uses this ih. But that ih doesn't exist in the actual brecOn call.
|
||||
-- That's why it must go.
|
||||
let FType ← forallBoundedTelescope brecOnType (some 1) fun F _ => do
|
||||
let F := F[0]!
|
||||
let FType ← inferType F
|
||||
trace[Elab.definition.structural] "FType: {FType}"
|
||||
instantiateForall FType indexMajorArgs
|
||||
forallBoundedTelescope FType (some 1) fun below _ => do
|
||||
let below := below[0]!
|
||||
let valueNew ← replaceIndPredRecApps recArgInfo motive value
|
||||
let Farg ← mkLambdaFVars (indexMajorArgs ++ #[below] ++ otherArgs) valueNew
|
||||
let brecOn := mkApp brecOn Farg
|
||||
let brecOn := mkAppN brecOn otherArgs
|
||||
mkLambdaFVars ys brecOn
|
||||
let funType ← mkLambdaFVars ys type
|
||||
withLetDecl `funType (← inferType funType) funType fun funType => do
|
||||
let motive ← mkForallFVars otherArgs (mkAppN funType ys)
|
||||
let motive ← mkLambdaFVars indexMajorArgs motive
|
||||
trace[Elab.definition.structural] "brecOn motive: {motive}"
|
||||
let brecOn := Lean.mkConst (mkBRecOnName recArgInfo.indName!) recArgInfo.indGroupInst.levels
|
||||
let brecOn := mkAppN brecOn recArgInfo.indGroupInst.params
|
||||
let brecOn := mkApp brecOn motive
|
||||
let brecOn := mkAppN brecOn indexMajorArgs
|
||||
check brecOn
|
||||
let brecOnType ← inferType brecOn
|
||||
trace[Elab.definition.structural] "brecOn {brecOn}"
|
||||
trace[Elab.definition.structural] "brecOnType {brecOnType}"
|
||||
-- we need to close the telescope here, because the local context is used:
|
||||
-- The root cause was, that this copied code puts an ih : FType into the
|
||||
-- local context and later, when we use the local context to build the recursive
|
||||
-- call, it uses this ih. But that ih doesn't exist in the actual brecOn call.
|
||||
-- That's why it must go.
|
||||
let FType ← forallBoundedTelescope brecOnType (some 1) fun F _ => do
|
||||
let F := F[0]!
|
||||
let FType ← inferType F
|
||||
trace[Elab.definition.structural] "FType: {FType}"
|
||||
instantiateForall FType indexMajorArgs
|
||||
forallBoundedTelescope FType (some 1) fun below _ => do
|
||||
let below := below[0]!
|
||||
let valueNew ← replaceIndPredRecApps recArgInfo funType motive value
|
||||
let Farg ← mkLambdaFVars (indexMajorArgs ++ #[below] ++ otherArgs) valueNew
|
||||
let brecOn := mkApp brecOn Farg
|
||||
let brecOn := mkAppN brecOn otherArgs
|
||||
let brecOn ← mkLetFVars #[funType] brecOn
|
||||
mkLambdaFVars ys brecOn
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
@@ -89,87 +89,72 @@ def getMutualFixedPrefix (preDefs : Array PreDefinition) : M Nat :=
|
||||
return true
|
||||
resultRef.get
|
||||
|
||||
/-- Checks that all parameter types are mutually inductive -/
|
||||
private def checkAllFromSameClique (recArgInfos : Array RecArgInfo) : MetaM Unit := do
|
||||
for recArgInfo in recArgInfos do
|
||||
unless recArgInfos[0]!.indAll.contains recArgInfo.indName do
|
||||
throwError m!"Cannot use structural mutual recursion: The recursive argument of " ++
|
||||
m!"{recArgInfos[0]!.fnName} is of type {recArgInfos[0]!.indName}, " ++
|
||||
m!"the recursive argument of {recArgInfo.fnName} is of type " ++
|
||||
m!"{recArgInfo.indName}, and these are not mutually recursive."
|
||||
private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr)
|
||||
(recArgInfos : Array RecArgInfo) : M (Array PreDefinition) := do
|
||||
let values ← preDefs.mapM (instantiateLambda ·.value xs)
|
||||
let indInfo ← getConstInfoInduct recArgInfos[0]!.indGroupInst.all[0]!
|
||||
if ← isInductivePredicate indInfo.name then
|
||||
-- Here we branch off to the IndPred construction, but only for non-mutual functions
|
||||
unless preDefs.size = 1 do
|
||||
throwError "structural mutual recursion over inductive predicates is not supported"
|
||||
trace[Elab.definition.structural] "Using mkIndPred construction"
|
||||
let preDef := preDefs[0]!
|
||||
let recArgInfo := recArgInfos[0]!
|
||||
let value := values[0]!
|
||||
let valueNew ← mkIndPredBRecOn recArgInfo value
|
||||
let valueNew ← mkLambdaFVars xs valueNew
|
||||
trace[Elab.definition.structural] "Nonrecursive value:{indentExpr valueNew}"
|
||||
check valueNew
|
||||
return #[{ preDef with value := valueNew }]
|
||||
|
||||
private def elimMutualRecursion (preDefs : Array PreDefinition) (recArgPoss : Array Nat) : M (Array PreDefinition) := do
|
||||
-- Sort the (indices of the) definitions by their position in indInfo.all
|
||||
let positions : Positions := .groupAndSort (·.indIdx) recArgInfos (Array.range indInfo.numTypeFormers)
|
||||
trace[Elab.definition.structural] "positions: {positions}"
|
||||
|
||||
-- Construct the common `.brecOn` arguments
|
||||
let motives ← (Array.zip recArgInfos values).mapM fun (r, v) => mkBRecOnMotive r v
|
||||
trace[Elab.definition.structural] "motives: {motives}"
|
||||
let brecOnConst ← mkBRecOnConst recArgInfos positions motives
|
||||
let FTypes ← inferBRecOnFTypes recArgInfos positions brecOnConst
|
||||
trace[Elab.definition.structural] "FTypes: {FTypes}"
|
||||
let FArgs ← (recArgInfos.zip (values.zip FTypes)).mapM fun (r, (v, t)) =>
|
||||
mkBRecOnF recArgInfos positions r v t
|
||||
trace[Elab.definition.structural] "FArgs: {FArgs}"
|
||||
-- Assemble the individual `.brecOn` applications
|
||||
let valuesNew ← (Array.zip recArgInfos values).mapIdxM fun i (r, v) =>
|
||||
mkBrecOnApp positions i brecOnConst FArgs r v
|
||||
-- Abstract over the fixed prefixed
|
||||
let valuesNew ← valuesNew.mapM (mkLambdaFVars xs ·)
|
||||
return (Array.zip preDefs valuesNew).map fun ⟨preDef, valueNew⟩ => { preDef with value := valueNew }
|
||||
|
||||
private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) :
|
||||
M (Array Nat × (Array PreDefinition) × Nat) := do
|
||||
withoutModifyingEnv do
|
||||
preDefs.forM (addAsAxiom ·)
|
||||
let names := preDefs.map (·.declName)
|
||||
let fnNames := preDefs.map (·.declName)
|
||||
let preDefs ← preDefs.mapM fun preDef =>
|
||||
return { preDef with value := (← preprocess preDef.value names) }
|
||||
return { preDef with value := (← preprocess preDef.value fnNames) }
|
||||
|
||||
-- The syntactically fixed arguments
|
||||
let maxNumFixed ← getMutualFixedPrefix preDefs
|
||||
|
||||
-- We do two passes to get the RecArgInfo values.
|
||||
-- From the first pass, we only keep the mininum of the `numFixed` reported.
|
||||
let numFixed ← lambdaBoundedTelescope preDefs[0]!.value maxNumFixed fun xs _ => do
|
||||
lambdaBoundedTelescope preDefs[0]!.value maxNumFixed fun xs _ => do
|
||||
assert! xs.size = maxNumFixed
|
||||
let values ← preDefs.mapM (instantiateLambda ·.value xs)
|
||||
|
||||
let recArgInfos ← preDefs.mapIdxM fun i preDef => do
|
||||
let recArgPos := recArgPoss[i]!
|
||||
let value := values[i]!
|
||||
lambdaTelescope value fun ys _value => do
|
||||
getRecArgInfo preDef.declName maxNumFixed (xs ++ ys) recArgPos
|
||||
|
||||
return (recArgInfos.map (·.numFixed)).foldl Nat.min maxNumFixed
|
||||
|
||||
if numFixed < maxNumFixed then
|
||||
trace[Elab.definition.structural] "Reduced numFixed from {maxNumFixed} to {numFixed}"
|
||||
|
||||
-- Now we bring exactly that `numFixed` parameter into scope.
|
||||
lambdaBoundedTelescope preDefs[0]!.value numFixed fun xs _ => do
|
||||
assert! xs.size = numFixed
|
||||
let values ← preDefs.mapM (instantiateLambda ·.value xs)
|
||||
|
||||
let recArgInfos ← preDefs.mapIdxM fun i preDef => do
|
||||
let recArgPos := recArgPoss[i]!
|
||||
let value := values[i]!
|
||||
lambdaTelescope value fun ys _value => do
|
||||
getRecArgInfo preDef.declName numFixed (xs ++ ys) recArgPos
|
||||
|
||||
-- Two passes should suffice
|
||||
assert! recArgInfos.all (·.numFixed = numFixed)
|
||||
|
||||
let indInfo ← getConstInfoInduct recArgInfos[0]!.indName
|
||||
if ← isInductivePredicate indInfo.name then
|
||||
-- Here we branch off to the IndPred construction, but only for non-mutual functions
|
||||
unless preDefs.size = 1 do
|
||||
throwError "structural mutual recursion over inductive predicates is not supported"
|
||||
trace[Elab.definition.structural] "Using mkIndPred construction"
|
||||
let preDef := preDefs[0]!
|
||||
let recArgInfo := recArgInfos[0]!
|
||||
let value := values[0]!
|
||||
let valueNew ← mkIndPredBRecOn recArgInfo value
|
||||
let valueNew ← mkLambdaFVars xs valueNew
|
||||
trace[Elab.definition.structural] "Nonrecursive value:{indentExpr valueNew}"
|
||||
check valueNew
|
||||
return #[{ preDef with value := valueNew }]
|
||||
|
||||
checkAllFromSameClique recArgInfos
|
||||
-- Sort the (indices of the) definitions by their position in indInfo.all
|
||||
let positions : Positions := .groupAndSort (·.indName) recArgInfos indInfo.all.toArray
|
||||
|
||||
-- Construct the common `.brecOn` arguments
|
||||
let motives ← (Array.zip recArgInfos values).mapM fun (r, v) => mkBRecOnMotive r v
|
||||
let brecOnConst ← mkBRecOnConst recArgInfos positions motives
|
||||
let FTypes ← inferBRecOnFTypes recArgInfos positions brecOnConst
|
||||
let FArgs ← (recArgInfos.zip (values.zip FTypes)).mapM fun (r, (v, t)) =>
|
||||
mkBRecOnF recArgInfos positions r v t
|
||||
-- Assemble the individual `.brecOn` applications
|
||||
let valuesNew ← (Array.zip recArgInfos values).mapIdxM fun i (r, v) =>
|
||||
mkBrecOnApp positions i brecOnConst FArgs r v
|
||||
-- Abstract over the fixed prefixed
|
||||
let valuesNew ← valuesNew.mapM (mkLambdaFVars xs ·)
|
||||
return (Array.zip preDefs valuesNew).map fun ⟨preDef, valueNew⟩ => { preDef with value := valueNew }
|
||||
tryAllArgs fnNames xs values termArg?s fun recArgInfos => do
|
||||
let recArgPoss := recArgInfos.map (·.recArgPos)
|
||||
trace[Elab.definition.structural] "Trying argument set {recArgPoss}"
|
||||
let numFixed := recArgInfos.foldl (·.min ·.numFixed) maxNumFixed
|
||||
if numFixed < maxNumFixed then
|
||||
trace[Elab.definition.structural] "Reduced numFixed from {maxNumFixed} to {numFixed}"
|
||||
-- We may have decreased the number of arguments we consider fixed, so update
|
||||
-- the recArgInfos, remove the extra arguments from local environment, and recalculate value
|
||||
let recArgInfos := recArgInfos.map ({· with numFixed := numFixed })
|
||||
withErasedFVars (xs.extract numFixed xs.size |>.map (·.fvarId!)) do
|
||||
let xs := xs[:numFixed]
|
||||
let preDefs' ← elimMutualRecursion preDefs xs recArgInfos
|
||||
return (recArgPoss, preDefs', numFixed)
|
||||
|
||||
def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
if let some ref := preDef.termination.terminationBy?? then
|
||||
@@ -179,34 +164,20 @@ def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
let stx ← termArg.delab arity (extraParams := preDef.termination.extraParams)
|
||||
Tactic.TryThis.addSuggestion ref stx
|
||||
|
||||
private def inferRecArgPos (preDefs : Array PreDefinition)
|
||||
(termArgs? : Option TerminationArguments) : M (Array Nat × Array PreDefinition) := do
|
||||
withoutModifyingEnv do
|
||||
if let some termArgs := termArgs? then
|
||||
let recArgPoss ← termArgs.mapM (·.structuralArg)
|
||||
let preDefsNew ← elimMutualRecursion preDefs recArgPoss
|
||||
return (recArgPoss, preDefsNew)
|
||||
else
|
||||
let #[preDef] := preDefs
|
||||
| throwError "mutual structural recursion requires explicit `termination_by` clauses"
|
||||
-- Use termination_by annotation to find argument to recurse on, or just try all
|
||||
tryAllArgs preDef.value fun i =>
|
||||
mapError (f := fun msg => m!"argument #{i+1} cannot be used for structural recursion{indentD msg}") do
|
||||
let preDefsNew ← elimMutualRecursion #[preDef] #[i]
|
||||
return (#[i], preDefsNew)
|
||||
|
||||
def structuralRecursion (preDefs : Array PreDefinition) (termArgs? : Option TerminationArguments) : TermElabM Unit := do
|
||||
let ((recArgPoss, preDefsNonRec), state) ← run <| inferRecArgPos preDefs termArgs?
|
||||
def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
|
||||
let names := preDefs.map (·.declName)
|
||||
let ((recArgPoss, preDefsNonRec, numFixed), state) ← run <| inferRecArgPos preDefs termArg?s
|
||||
for recArgPos in recArgPoss, preDef in preDefs do
|
||||
reportTermArg preDef recArgPos
|
||||
state.addMatchers.forM liftM
|
||||
preDefsNonRec.forM fun preDefNonRec => do
|
||||
let preDefNonRec ← eraseRecAppSyntax preDefNonRec
|
||||
-- state.addMatchers.forM liftM
|
||||
mapError (addNonRec preDefNonRec (applyAttrAfterCompilation := false)) fun msg =>
|
||||
m!"structural recursion failed, produced type incorrect term{indentD msg}"
|
||||
-- We create the `_unsafe_rec` before we abstract nested proofs.
|
||||
-- Reason: the nested proofs may be referring to the _unsafe_rec.
|
||||
mapError (f := (m!"structural recursion failed, produced type incorrect term{indentD ·}")) do
|
||||
-- We create the `_unsafe_rec` before we abstract nested proofs.
|
||||
-- Reason: the nested proofs may be referring to the _unsafe_rec.
|
||||
addNonRec preDefNonRec (applyAttrAfterCompilation := false) (all := names.toList)
|
||||
let preDefs ← preDefs.mapM (eraseRecAppSyntax ·)
|
||||
addAndCompilePartialRec preDefs
|
||||
for preDef in preDefs, recArgPos in recArgPoss do
|
||||
@@ -219,13 +190,11 @@ def structuralRecursion (preDefs : Array PreDefinition) (termArgs? : Option Term
|
||||
for theorems and definitions that are propositions.
|
||||
See issue #2327
|
||||
-/
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
|
||||
addSmartUnfoldingDef preDef recArgPos
|
||||
markAsRecursive preDef.declName
|
||||
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.structural
|
||||
|
||||
end Structural
|
||||
|
||||
|
||||
60
src/Lean/Elab/PreDefinition/Structural/RecArgInfo.lean
Normal file
60
src/Lean/Elab/PreDefinition/Structural/RecArgInfo.lean
Normal file
@@ -0,0 +1,60 @@
|
||||
/-
|
||||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Joachim Breitner
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Basic
|
||||
import Lean.Meta.ForEachExpr
|
||||
import Lean.Elab.PreDefinition.Structural.IndGroupInfo
|
||||
|
||||
namespace Lean.Elab.Structural
|
||||
|
||||
|
||||
/--
|
||||
Information about the argument of interest of a structurally recursive function.
|
||||
|
||||
The `Expr`s in this data structure expect the `fixedParams` to be in scope, but not the other
|
||||
parameters of the function. This ensures that this data structure makes sense in the other functions
|
||||
of a mutually recursive group.
|
||||
-/
|
||||
structure RecArgInfo where
|
||||
/-- the name of the recursive function -/
|
||||
fnName : Name
|
||||
/-- the fixed prefix of arguments of the function we are trying to justify termination using structural recursion. -/
|
||||
numFixed : Nat
|
||||
/-- position of the argument (counted including fixed prefix) we are recursing on -/
|
||||
recArgPos : Nat
|
||||
/-- position of the indices (counted including fixed prefix) of the inductive datatype indices we are recursing on -/
|
||||
indicesPos : Array Nat
|
||||
/-- The inductive group (with parameters) of the argument's type -/
|
||||
indGroupInst : IndGroupInst
|
||||
/--
|
||||
index of the inductive datatype of the argument we are recursing on.
|
||||
If `< indAll.all`, a normal data type, else an auxillary data type due to nested recursion
|
||||
-/
|
||||
indIdx : Nat
|
||||
deriving Inhabited
|
||||
|
||||
/--
|
||||
If `xs` are the parameters of the functions (excluding fixed prefix), partitions them
|
||||
into indices and major arguments, and other parameters.
|
||||
-/
|
||||
def RecArgInfo.pickIndicesMajor (info : RecArgInfo) (xs : Array Expr) : (Array Expr × Array Expr) := Id.run do
|
||||
let mut indexMajorArgs := #[]
|
||||
let mut otherArgs := #[]
|
||||
for h : i in [:xs.size] do
|
||||
let j := i + info.numFixed
|
||||
if j = info.recArgPos || info.indicesPos.contains j then
|
||||
indexMajorArgs := indexMajorArgs.push xs[i]
|
||||
else
|
||||
otherArgs := otherArgs.push xs[i]
|
||||
return (indexMajorArgs, otherArgs)
|
||||
|
||||
/--
|
||||
Name of the recursive data type. Assumes that it is not one of the auxillary ones.
|
||||
-/
|
||||
def RecArgInfo.indName! (info : RecArgInfo) : Name :=
|
||||
info.indGroupInst.all[info.indIdx]!
|
||||
|
||||
end Lean.Elab.Structural
|
||||
@@ -10,7 +10,7 @@ import Lean.Elab.Term
|
||||
import Lean.Elab.Binders
|
||||
import Lean.Elab.SyntheticMVars
|
||||
import Lean.Elab.PreDefinition.TerminationHint
|
||||
import Lean.PrettyPrinter.Delaborator
|
||||
import Lean.PrettyPrinter.Delaborator.Basic
|
||||
|
||||
/-!
|
||||
This module contains
|
||||
@@ -115,7 +115,7 @@ def TerminationArgument.delab (arity : Nat) (extraParams : Nat) (termArg : Termi
|
||||
-- any variable not mentioned syntatically (it may appear in the `Expr`, so do not just use
|
||||
-- `e.bindingBody!.hasLooseBVar`) should be delaborated as a hole.
|
||||
let vars : TSyntaxArray [`ident, `Lean.Parser.Term.hole] :=
|
||||
Array.map (fun (i : Ident) => if hasIdent i.getId stxBody then i else hole) vars
|
||||
Array.map (fun (i : Ident) => if stxBody.raw.hasIdent i.getId then i else hole) vars
|
||||
-- drop trailing underscores
|
||||
let mut vars := vars
|
||||
while ! vars.isEmpty && vars.back.raw.isOfKind ``hole do vars := vars.pop
|
||||
|
||||
@@ -57,18 +57,18 @@ structure TerminationHints where
|
||||
|
||||
def TerminationHints.none : TerminationHints := ⟨.missing, .none, .none, .none, 0⟩
|
||||
|
||||
/-- Logs warnings when the `TerminationHints` are present. -/
|
||||
/-- Logs warnings when the `TerminationHints` are unexpectedly present. -/
|
||||
def TerminationHints.ensureNone (hints : TerminationHints) (reason : String) : CoreM Unit := do
|
||||
match hints.terminationBy??, hints.terminationBy?, hints.decreasingBy? with
|
||||
| .none, .none, .none => pure ()
|
||||
| .none, .none, .some dec_by =>
|
||||
logErrorAt dec_by.ref m!"unused `decreasing_by`, function is {reason}"
|
||||
logWarningAt dec_by.ref m!"unused `decreasing_by`, function is {reason}"
|
||||
| .some term_by?, .none, .none =>
|
||||
logErrorAt term_by? m!"unused `termination_by?`, function is {reason}"
|
||||
logWarningAt term_by? m!"unused `termination_by?`, function is {reason}"
|
||||
| .none, .some term_by, .none =>
|
||||
logErrorAt term_by.ref m!"unused `termination_by`, function is {reason}"
|
||||
logWarningAt term_by.ref m!"unused `termination_by`, function is {reason}"
|
||||
| _, _, _ =>
|
||||
logErrorAt hints.ref m!"unused termination hints, function is {reason}"
|
||||
logWarningAt hints.ref m!"unused termination hints, function is {reason}"
|
||||
|
||||
/-- True if any form of termination hint is present. -/
|
||||
def TerminationHints.isNotNone (hints : TerminationHints) : Bool :=
|
||||
|
||||
@@ -86,7 +86,8 @@ def varyingVarNames (fixedPrefixSize : Nat) (preDef : PreDefinition) : MetaM (Ar
|
||||
let xs : Array Expr := xs[fixedPrefixSize:]
|
||||
xs.mapM (·.fvarId!.getUserName)
|
||||
|
||||
def wfRecursion (preDefs : Array PreDefinition) (termArgs? : Option TerminationArguments) : TermElabM Unit := do
|
||||
def wfRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Option TerminationArgument)) : TermElabM Unit := do
|
||||
let termArgs? := termArg?s.sequenceMap id -- Either all or none, checked by `elabTerminationByHints`
|
||||
let preDefs ← preDefs.mapM fun preDef =>
|
||||
return { preDef with value := (← preprocess preDef.value) }
|
||||
let (fixedPrefixSize, argsPacker, unaryPreDef) ← withoutModifyingEnv do
|
||||
|
||||
@@ -4,8 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Util.FoldConsts
|
||||
import Lean.Meta.Eqns
|
||||
import Lean.Util.CollectAxioms
|
||||
import Lean.Elab.Command
|
||||
|
||||
namespace Lean.Elab.Command
|
||||
@@ -120,40 +120,12 @@ private def printId (id : Syntax) : CommandElabM Unit := do
|
||||
| `(#print%$tk $s:str) => logInfoAt tk s.getString
|
||||
| _ => throwError "invalid #print command"
|
||||
|
||||
namespace CollectAxioms
|
||||
|
||||
structure State where
|
||||
visited : NameSet := {}
|
||||
axioms : Array Name := #[]
|
||||
|
||||
abbrev M := ReaderT Environment $ StateM State
|
||||
|
||||
partial def collect (c : Name) : M Unit := do
|
||||
let collectExpr (e : Expr) : M Unit := e.getUsedConstants.forM collect
|
||||
let s ← get
|
||||
unless s.visited.contains c do
|
||||
modify fun s => { s with visited := s.visited.insert c }
|
||||
let env ← read
|
||||
match env.find? c with
|
||||
| some (ConstantInfo.axiomInfo _) => modify fun s => { s with axioms := s.axioms.push c }
|
||||
| some (ConstantInfo.defnInfo v) => collectExpr v.type *> collectExpr v.value
|
||||
| some (ConstantInfo.thmInfo v) => collectExpr v.type *> collectExpr v.value
|
||||
| some (ConstantInfo.opaqueInfo v) => collectExpr v.type *> collectExpr v.value
|
||||
| some (ConstantInfo.quotInfo _) => pure ()
|
||||
| some (ConstantInfo.ctorInfo v) => collectExpr v.type
|
||||
| some (ConstantInfo.recInfo v) => collectExpr v.type
|
||||
| some (ConstantInfo.inductInfo v) => collectExpr v.type *> v.ctors.forM collect
|
||||
| none => pure ()
|
||||
|
||||
end CollectAxioms
|
||||
|
||||
private def printAxiomsOf (constName : Name) : CommandElabM Unit := do
|
||||
let env ← getEnv
|
||||
let (_, s) := ((CollectAxioms.collect constName).run env).run {}
|
||||
if s.axioms.isEmpty then
|
||||
let axioms ← collectAxioms constName
|
||||
if axioms.isEmpty then
|
||||
logInfo m!"'{constName}' does not depend on any axioms"
|
||||
else
|
||||
logInfo m!"'{constName}' depends on axioms: {s.axioms.qsort Name.lt |>.toList}"
|
||||
logInfo m!"'{constName}' depends on axioms: {axioms.qsort Name.lt |>.toList}"
|
||||
|
||||
@[builtin_command_elab «printAxioms»] def elabPrintAxioms : CommandElab
|
||||
| `(#print%$tk axioms $id) => withRef tk do
|
||||
|
||||
@@ -40,3 +40,4 @@ import Lean.Elab.Tactic.LibrarySearch
|
||||
import Lean.Elab.Tactic.ShowTerm
|
||||
import Lean.Elab.Tactic.Rfl
|
||||
import Lean.Elab.Tactic.Rewrites
|
||||
import Lean.Elab.Tactic.DiscrTreeKey
|
||||
|
||||
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.Tactic.Util
|
||||
import Lean.Elab.Term
|
||||
|
||||
namespace Lean.Elab
|
||||
@@ -155,7 +156,9 @@ partial def evalTactic (stx : Syntax) : TacticM Unit := do
|
||||
-- Macro writers create a sequence of tactics `t₁ ... tₙ` using `mkNullNode #[t₁, ..., tₙ]`
|
||||
-- We could support incrementality here by allocating `n` new snapshot bundles but the
|
||||
-- practical value is not clear
|
||||
Term.withoutTacticIncrementality true do
|
||||
-- NOTE: `withTacticInfoContext` is used to preserve the invariant of `elabTactic` producing
|
||||
-- exactly one info tree, which is necessary for using `getInfoTreeWithContext`.
|
||||
Term.withoutTacticIncrementality true <| withTacticInfoContext stx do
|
||||
stx.getArgs.forM evalTactic
|
||||
else withTraceNode `Elab.step (fun _ => return stx) (tag := stx.getKind.toString) do
|
||||
let evalFns := tacticElabAttribute.getEntries (← getEnv) stx.getKind
|
||||
@@ -222,14 +225,18 @@ where
|
||||
snap.new.resolve <| .mk {
|
||||
stx := stx'
|
||||
diagnostics := .empty
|
||||
finished := .pure { state? := (← Tactic.saveState) }
|
||||
} #[{ range? := stx'.getRange?, task := promise.result }]
|
||||
finished := .pure {
|
||||
diagnostics := .empty
|
||||
state? := (← Tactic.saveState)
|
||||
}
|
||||
next := #[{ range? := stx'.getRange?, task := promise.result }]
|
||||
}
|
||||
-- Update `tacSnap?` to old unfolding
|
||||
withTheReader Term.Context ({ · with tacSnap? := some {
|
||||
new := promise
|
||||
old? := do
|
||||
let old ← old?
|
||||
return ⟨old.data.stx, (← old.next.get? 0)⟩
|
||||
return ⟨old.data.stx, (← old.data.next.get? 0)⟩
|
||||
} }) do
|
||||
evalTactic stx'
|
||||
return
|
||||
@@ -398,12 +405,19 @@ def ensureHasNoMVars (e : Expr) : TacticM Unit := do
|
||||
if e.hasExprMVar then
|
||||
throwError "tactic failed, resulting expression contains metavariables{indentExpr e}"
|
||||
|
||||
/-- Close main goal using the given expression. If `checkUnassigned == true`, then `val` must not contain unassigned metavariables. -/
|
||||
def closeMainGoal (val : Expr) (checkUnassigned := true): TacticM Unit := do
|
||||
/--
|
||||
Closes main goal using the given expression.
|
||||
If `checkUnassigned == true`, then `val` must not contain unassigned metavariables.
|
||||
Returns `true` if `val` was successfully used to close the goal.
|
||||
-/
|
||||
def closeMainGoal (tacName : Name) (val : Expr) (checkUnassigned := true): TacticM Unit := do
|
||||
if checkUnassigned then
|
||||
ensureHasNoMVars val
|
||||
(← getMainGoal).assign val
|
||||
replaceMainGoal []
|
||||
let mvarId ← getMainGoal
|
||||
if (← mvarId.checkedAssign val) then
|
||||
replaceMainGoal []
|
||||
else
|
||||
throwTacticEx tacName mvarId m!"attempting to close the goal using{indentExpr val}\nthis is often due occurs-check failure"
|
||||
|
||||
@[inline] def liftMetaMAtMain (x : MVarId → MetaM α) : TacticM α := do
|
||||
withMainContext do x (← getMainGoal)
|
||||
|
||||
@@ -60,7 +60,7 @@ where
|
||||
if let some snap := (← readThe Term.Context).tacSnap? then
|
||||
if let some old := snap.old? then
|
||||
let oldParsed := old.val.get
|
||||
oldInner? := oldParsed.next.get? 0 |>.map (⟨oldParsed.data.stx, ·⟩)
|
||||
oldInner? := oldParsed.data.inner? |>.map (⟨oldParsed.data.stx, ·⟩)
|
||||
-- compare `stx[0]` for `finished`/`next` reuse, focus on remainder of script
|
||||
Term.withNarrowedTacticReuse (stx := stx) (fun stx => (stx[0], mkNullNode stx.getArgs[1:])) fun stxs => do
|
||||
let some snap := (← readThe Term.Context).tacSnap?
|
||||
@@ -73,29 +73,47 @@ where
|
||||
if let some state := oldParsed.data.finished.get.state? then
|
||||
reusableResult? := some ((), state)
|
||||
-- only allow `next` reuse in this case
|
||||
oldNext? := oldParsed.next.get? 1 |>.map (⟨old.stx, ·⟩)
|
||||
oldNext? := oldParsed.data.next.get? 0 |>.map (⟨old.stx, ·⟩)
|
||||
|
||||
-- For `tac`'s snapshot task range, disregard synthetic info as otherwise
|
||||
-- `SnapshotTree.findInfoTreeAtPos` might choose the wrong snapshot: for example, when
|
||||
-- hovering over a `show` tactic, we should choose the info tree in `finished` over that in
|
||||
-- `inner`, which points to execution of the synthesized `refine` step and does not contain
|
||||
-- the full info. In most other places, siblings in the snapshot tree have disjoint ranges and
|
||||
-- so this issue does not occur.
|
||||
let mut range? := tac.getRange? (canonicalOnly := true)
|
||||
-- Include trailing whitespace in the range so that `goalsAs?` does not have to wait for more
|
||||
-- snapshots than necessary.
|
||||
if let some range := range? then
|
||||
range? := some { range with stop := ⟨range.stop.byteIdx + tac.getTrailingSize⟩ }
|
||||
withAlwaysResolvedPromise fun next => do
|
||||
withAlwaysResolvedPromise fun finished => do
|
||||
withAlwaysResolvedPromise fun inner => do
|
||||
snap.new.resolve <| .mk {
|
||||
diagnostics := .empty
|
||||
stx := tac
|
||||
diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog
|
||||
(← Core.getAndEmptyMessageLog))
|
||||
finished := finished.result
|
||||
} #[
|
||||
{
|
||||
range? := tac.getRange?
|
||||
task := inner.result },
|
||||
{
|
||||
range? := stxs |>.getRange?
|
||||
task := next.result }]
|
||||
let (_, state) ← withRestoreOrSaveFull reusableResult?
|
||||
-- set up nested reuse; `evalTactic` will check for `isIncrementalElab`
|
||||
(tacSnap? := some { old? := oldInner?, new := inner }) do
|
||||
Term.withReuseContext tac do
|
||||
evalTactic tac
|
||||
finished.resolve { state? := state }
|
||||
inner? := some { range?, task := inner.result }
|
||||
finished := { range?, task := finished.result }
|
||||
next := #[{ range? := stxs.getRange?, task := next.result }]
|
||||
}
|
||||
-- Run `tac` in a fresh info tree state and store resulting state in snapshot for
|
||||
-- incremental reporting, then add back saved trees. Here we rely on `evalTactic`
|
||||
-- producing at most one info tree as otherwise `getInfoTreeWithContext?` would panic.
|
||||
let trees ← getResetInfoTrees
|
||||
try
|
||||
let (_, state) ← withRestoreOrSaveFull reusableResult?
|
||||
-- set up nested reuse; `evalTactic` will check for `isIncrementalElab`
|
||||
(tacSnap? := some { old? := oldInner?, new := inner }) do
|
||||
Term.withReuseContext tac do
|
||||
evalTactic tac
|
||||
finished.resolve {
|
||||
diagnostics := (← Language.Snapshot.Diagnostics.ofMessageLog
|
||||
(← Core.getAndEmptyMessageLog))
|
||||
infoTree? := (← Term.getInfoTreeWithContext?)
|
||||
state? := state
|
||||
}
|
||||
finally
|
||||
modifyInfoState fun s => { s with trees := trees ++ s.trees }
|
||||
|
||||
withTheReader Term.Context ({ · with tacSnap? := some {
|
||||
new := next
|
||||
|
||||
65
src/Lean/Elab/Tactic/DiscrTreeKey.lean
Normal file
65
src/Lean/Elab/Tactic/DiscrTreeKey.lean
Normal file
@@ -0,0 +1,65 @@
|
||||
/-
|
||||
Copyright (c) 2024 Matthew Robert Ballard. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Tomas Skrivan, Matthew Robert Ballard
|
||||
-/
|
||||
prelude
|
||||
import Init.Tactics
|
||||
import Lean.Elab.Command
|
||||
import Lean.Meta.Tactic.Simp.SimpTheorems
|
||||
|
||||
namespace Lean.Elab.Tactic.DiscrTreeKey
|
||||
|
||||
open Lean.Meta DiscrTree
|
||||
open Lean.Elab.Tactic
|
||||
open Lean.Elab.Command
|
||||
|
||||
private def mkKey (e : Expr) (simp : Bool) : MetaM (Array Key) := do
|
||||
let (_, _, type) ← withReducible <| forallMetaTelescopeReducing e
|
||||
let type ← whnfR type
|
||||
if simp then
|
||||
if let some (_, lhs, _) := type.eq? then
|
||||
mkPath lhs simpDtConfig
|
||||
else if let some (lhs, _) := type.iff? then
|
||||
mkPath lhs simpDtConfig
|
||||
else if let some (_, lhs, _) := type.ne? then
|
||||
mkPath lhs simpDtConfig
|
||||
else if let some p := type.not? then
|
||||
match p.eq? with
|
||||
| some (_, lhs, _) =>
|
||||
mkPath lhs simpDtConfig
|
||||
| _ => mkPath p simpDtConfig
|
||||
else
|
||||
mkPath type simpDtConfig
|
||||
else
|
||||
mkPath type {}
|
||||
|
||||
private def getType (t : TSyntax `term) : TermElabM Expr := do
|
||||
if let `($id:ident) := t then
|
||||
if let some ldecl := (← getLCtx).findFromUserName? id.getId then
|
||||
return ldecl.type
|
||||
else
|
||||
let info ← getConstInfo (← realizeGlobalConstNoOverloadWithInfo id)
|
||||
return info.type
|
||||
else
|
||||
Term.elabTerm t none
|
||||
|
||||
@[builtin_command_elab Lean.Parser.discrTreeKeyCmd]
|
||||
def evalDiscrTreeKeyCmd : CommandElab := fun stx => do
|
||||
Command.liftTermElabM <| do
|
||||
match stx with
|
||||
| `(command| #discr_tree_key $t:term) => do
|
||||
let type ← getType t
|
||||
logInfo (← keysAsPattern <| ← mkKey type false)
|
||||
| _ => Elab.throwUnsupportedSyntax
|
||||
|
||||
@[builtin_command_elab Lean.Parser.discrTreeSimpKeyCmd]
|
||||
def evalDiscrTreeSimpKeyCmd : CommandElab := fun stx => do
|
||||
Command.liftTermElabM <| do
|
||||
match stx with
|
||||
| `(command| #discr_tree_simp_key $t:term) => do
|
||||
let type ← getType t
|
||||
logInfo (← keysAsPattern <| ← mkKey type true)
|
||||
| _ => Elab.throwUnsupportedSyntax
|
||||
|
||||
end Lean.Elab.Tactic.DiscrTreeKey
|
||||
@@ -56,9 +56,9 @@ def elabTermEnsuringType (stx : Syntax) (expectedType? : Option Expr) (mayPostpo
|
||||
return e
|
||||
|
||||
/-- Try to close main goal using `x target`, where `target` is the type of the main goal. -/
|
||||
def closeMainGoalUsing (x : Expr → TacticM Expr) (checkUnassigned := true) : TacticM Unit :=
|
||||
def closeMainGoalUsing (tacName : Name) (x : Expr → TacticM Expr) (checkUnassigned := true) : TacticM Unit :=
|
||||
withMainContext do
|
||||
closeMainGoal (checkUnassigned := checkUnassigned) (← x (← getMainTarget))
|
||||
closeMainGoal (tacName := tacName) (checkUnassigned := checkUnassigned) (← x (← getMainTarget))
|
||||
|
||||
def logUnassignedAndAbort (mvarIds : Array MVarId) : TacticM Unit := do
|
||||
if (← Term.logUnassignedUsingErrorInfos mvarIds) then
|
||||
@@ -68,13 +68,14 @@ def filterOldMVars (mvarIds : Array MVarId) (mvarCounterSaved : Nat) : MetaM (Ar
|
||||
let mctx ← getMCtx
|
||||
return mvarIds.filter fun mvarId => (mctx.getDecl mvarId |>.index) >= mvarCounterSaved
|
||||
|
||||
@[builtin_tactic «exact»] def evalExact : Tactic := fun stx =>
|
||||
@[builtin_tactic «exact»] def evalExact : Tactic := fun stx => do
|
||||
match stx with
|
||||
| `(tactic| exact $e) => closeMainGoalUsing (checkUnassigned := false) fun type => do
|
||||
let mvarCounterSaved := (← getMCtx).mvarCounter
|
||||
let r ← elabTermEnsuringType e type
|
||||
logUnassignedAndAbort (← filterOldMVars (← getMVars r) mvarCounterSaved)
|
||||
return r
|
||||
| `(tactic| exact $e) =>
|
||||
closeMainGoalUsing `exact (checkUnassigned := false) fun type => do
|
||||
let mvarCounterSaved := (← getMCtx).mvarCounter
|
||||
let r ← elabTermEnsuringType e type
|
||||
logUnassignedAndAbort (← filterOldMVars (← getMVars r) mvarCounterSaved)
|
||||
return r
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
def sortMVarIdArrayByIndex [MonadMCtx m] [Monad m] (mvarIds : Array MVarId) : m (Array MVarId) := do
|
||||
@@ -359,8 +360,8 @@ def elabAsFVar (stx : Syntax) (userName? : Option Name := none) : TacticM FVarId
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
/--
|
||||
Make sure `expectedType` does not contain free and metavariables.
|
||||
It applies zeta and zetaDelta-reduction to eliminate let-free-vars.
|
||||
Make sure `expectedType` does not contain free and metavariables.
|
||||
It applies zeta and zetaDelta-reduction to eliminate let-free-vars.
|
||||
-/
|
||||
private def preprocessPropToDecide (expectedType : Expr) : TermElabM Expr := do
|
||||
let mut expectedType ← instantiateMVars expectedType
|
||||
@@ -370,31 +371,95 @@ private def preprocessPropToDecide (expectedType : Expr) : TermElabM Expr := do
|
||||
throwError "expected type must not contain free or meta variables{indentExpr expectedType}"
|
||||
return expectedType
|
||||
|
||||
/--
|
||||
Given the decidable instance `inst`, reduces it and returns a decidable instance expression
|
||||
in whnf that can be regarded as the reason for the failure of `inst` to fully reduce.
|
||||
-/
|
||||
private partial def blameDecideReductionFailure (inst : Expr) : MetaM Expr := do
|
||||
let inst ← whnf inst
|
||||
-- If it's the Decidable recursor, then blame the major premise.
|
||||
if inst.isAppOfArity ``Decidable.rec 5 then
|
||||
return ← blameDecideReductionFailure inst.appArg!
|
||||
-- If it is a matcher, look for a discriminant that's a Decidable instance to blame.
|
||||
if let .const c _ := inst.getAppFn then
|
||||
if let some info ← getMatcherInfo? c then
|
||||
if inst.getAppNumArgs == info.arity then
|
||||
let args := inst.getAppArgs
|
||||
for i in [0:info.numDiscrs] do
|
||||
let inst' := args[info.numParams + 1 + i]!
|
||||
if (← Meta.isClass? (← inferType inst')) == ``Decidable then
|
||||
let inst'' ← whnf inst'
|
||||
if !(inst''.isAppOf ``isTrue || inst''.isAppOf ``isFalse) then
|
||||
return ← blameDecideReductionFailure inst''
|
||||
return inst
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.decide] def evalDecide : Tactic := fun _ =>
|
||||
closeMainGoalUsing fun expectedType => do
|
||||
closeMainGoalUsing `decide fun expectedType => do
|
||||
let expectedType ← preprocessPropToDecide expectedType
|
||||
let d ← mkDecide expectedType
|
||||
let d ← instantiateMVars d
|
||||
-- Get instance from `d`
|
||||
let s := d.appArg!
|
||||
-- Reduce the instance rather than `d` itself, since that gives a nicer error message on failure.
|
||||
let r ← withDefault <| whnf s
|
||||
if r.isAppOf ``isFalse then
|
||||
throwError "\
|
||||
tactic 'decide' proved that the proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
is false"
|
||||
unless r.isAppOf ``isTrue do
|
||||
throwError "\
|
||||
tactic 'decide' failed for proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
since its 'Decidable' instance reduced to\
|
||||
{indentExpr r}\n\
|
||||
rather than to the 'isTrue' constructor."
|
||||
-- While we have a proof from reduction, we do not embed it in the proof term,
|
||||
-- but rather we let the kernel recompute it during type checking from a more efficient term.
|
||||
let rflPrf ← mkEqRefl (toExpr true)
|
||||
return mkApp3 (Lean.mkConst ``of_decide_eq_true) expectedType s rflPrf
|
||||
-- Reduce the instance rather than `d` itself for diagnostics purposes.
|
||||
let r ← withAtLeastTransparency .default <| whnf s
|
||||
if r.isAppOf ``isTrue then
|
||||
-- Success!
|
||||
-- While we have a proof from reduction, we do not embed it in the proof term,
|
||||
-- and instead we let the kernel recompute it during type checking from the following more efficient term.
|
||||
let rflPrf ← mkEqRefl (toExpr true)
|
||||
return mkApp3 (Lean.mkConst ``of_decide_eq_true) expectedType s rflPrf
|
||||
else
|
||||
-- Diagnose the failure, lazily so that there is no performance impact if `decide` isn't being used interactively.
|
||||
throwError MessageData.ofLazyM (es := #[expectedType]) do
|
||||
if r.isAppOf ``isFalse then
|
||||
return m!"\
|
||||
tactic 'decide' proved that the proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
is false"
|
||||
-- Re-reduce the instance and collect diagnostics, to get all unfolded Decidable instances
|
||||
let (reason, unfoldedInsts) ← withoutModifyingState <| withOptions (fun opt => diagnostics.set opt true) do
|
||||
modifyDiag (fun _ => {})
|
||||
let reason ← withAtLeastTransparency .default <| blameDecideReductionFailure s
|
||||
let unfolded := (← get).diag.unfoldCounter.foldl (init := #[]) fun cs n _ => cs.push n
|
||||
let unfoldedInsts ← unfolded |>.qsort Name.lt |>.filterMapM fun n => do
|
||||
let e ← mkConstWithLevelParams n
|
||||
if (← Meta.isClass? (← inferType e)) == ``Decidable then
|
||||
return m!"'{MessageData.ofConst e}'"
|
||||
else
|
||||
return none
|
||||
return (reason, unfoldedInsts)
|
||||
let stuckMsg :=
|
||||
if unfoldedInsts.isEmpty then
|
||||
m!"Reduction got stuck at the '{MessageData.ofConstName ``Decidable}' instance{indentExpr reason}"
|
||||
else
|
||||
let instances := if unfoldedInsts.size == 1 then "instance" else "instances"
|
||||
m!"After unfolding the {instances} {MessageData.andList unfoldedInsts.toList}, \
|
||||
reduction got stuck at the '{MessageData.ofConstName ``Decidable}' instance{indentExpr reason}"
|
||||
let hint :=
|
||||
if reason.isAppOf ``Eq.rec then
|
||||
m!"\n\n\
|
||||
Hint: Reduction got stuck on '▸' ({MessageData.ofConstName ``Eq.rec}), \
|
||||
which suggests that one of the '{MessageData.ofConstName ``Decidable}' instances is defined using tactics such as 'rw' or 'simp'. \
|
||||
To avoid tactics, make use of functions such as \
|
||||
'{MessageData.ofConstName ``inferInstanceAs}' or '{MessageData.ofConstName ``decidable_of_decidable_of_iff}' \
|
||||
to alter a proposition."
|
||||
else if reason.isAppOf ``Classical.choice then
|
||||
m!"\n\n\
|
||||
Hint: Reduction got stuck on '{MessageData.ofConstName ``Classical.choice}', \
|
||||
which indicates that a '{MessageData.ofConstName ``Decidable}' instance \
|
||||
is defined using classical reasoning, proving an instance exists rather than giving a concrete construction. \
|
||||
The 'decide' tactic works by evaluating a decision procedure via reduction, and it cannot make progress with such instances. \
|
||||
This can occur due to the 'opened scoped Classical' command, which enables the instance \
|
||||
'{MessageData.ofConstName ``Classical.propDecidable}'."
|
||||
else
|
||||
MessageData.nil
|
||||
return m!"\
|
||||
tactic 'decide' failed for proposition\
|
||||
{indentExpr expectedType}\n\
|
||||
since its '{MessageData.ofConstName ``Decidable}' instance\
|
||||
{indentExpr s}\n\
|
||||
did not reduce to '{MessageData.ofConstName ``isTrue}' or '{MessageData.ofConstName ``isFalse}'.\n\n\
|
||||
{stuckMsg}{hint}"
|
||||
|
||||
private def mkNativeAuxDecl (baseName : Name) (type value : Expr) : TermElabM Name := do
|
||||
let auxName ← Term.mkAuxName baseName
|
||||
@@ -408,7 +473,7 @@ private def mkNativeAuxDecl (baseName : Name) (type value : Expr) : TermElabM Na
|
||||
pure auxName
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.nativeDecide] def evalNativeDecide : Tactic := fun _ =>
|
||||
closeMainGoalUsing fun expectedType => do
|
||||
closeMainGoalUsing `nativeDecide fun expectedType => do
|
||||
let expectedType ← preprocessPropToDecide expectedType
|
||||
let d ← mkDecide expectedType
|
||||
let auxDeclName ← mkNativeAuxDecl `_nativeDecide (Lean.mkConst `Bool) d
|
||||
|
||||
@@ -74,18 +74,23 @@ def mkExtIffType (extThmName : Name) : MetaM Expr := withLCtx {} {} do
|
||||
let some (_, x, y) := ty.eq? | failNotEq
|
||||
let some xIdx := args.findIdx? (· == x) | failNotEq
|
||||
let some yIdx := args.findIdx? (· == y) | failNotEq
|
||||
unless xIdx == yIdx + 1 || xIdx + 1 == yIdx do
|
||||
unless xIdx + 1 == yIdx do
|
||||
throwError "expecting {x} and {y} to be consecutive arguments"
|
||||
let startIdx := max xIdx yIdx + 1
|
||||
let startIdx := yIdx + 1
|
||||
let toRevert := args[startIdx:].toArray
|
||||
let fvars ← toRevert.foldlM (init := {}) (fun st e => return collectFVars st (← inferType e))
|
||||
for fvar in toRevert do
|
||||
unless ← Meta.isProof fvar do
|
||||
throwError "argument {fvar} is not a proof, which is not supported"
|
||||
throwError "argument {fvar} is not a proof, which is not supported for arguments after {x} and {y}"
|
||||
if fvars.fvarSet.contains fvar.fvarId! then
|
||||
throwError "argument {fvar} is depended upon, which is not supported"
|
||||
throwError "argument {fvar} is depended upon, which is not supported for arguments after {x} and {y}"
|
||||
let conj := mkAndN (← toRevert.mapM (inferType ·)).toList
|
||||
withNewBinderInfos (args |>.extract 0 startIdx |>.map (·.fvarId!, .implicit)) do
|
||||
-- Make everything implicit except for inst implicits
|
||||
let mut newBis := #[]
|
||||
for fvar in args[0:startIdx] do
|
||||
if (← fvar.fvarId!.getBinderInfo) matches .default | .strictImplicit then
|
||||
newBis := newBis.push (fvar.fvarId!, .implicit)
|
||||
withNewBinderInfos newBis do
|
||||
mkForallFVars args[:startIdx] <| mkIff ty conj
|
||||
|
||||
/--
|
||||
@@ -99,27 +104,31 @@ def realizeExtTheorem (structName : Name) (flat : Bool) : Elab.Command.CommandEl
|
||||
throwError "'{structName}' is not a structure"
|
||||
let extName := structName.mkStr "ext"
|
||||
unless (← getEnv).contains extName do
|
||||
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extName do
|
||||
let type ← mkExtType structName flat
|
||||
let pf ← withSynthesize do
|
||||
let indVal ← getConstInfoInduct structName
|
||||
let params := Array.mkArray indVal.numParams (← `(_))
|
||||
Elab.Term.elabTermEnsuringType (expectedType? := type) (implicitLambda := false)
|
||||
-- introduce the params, do cases on 'x' and 'y', and then substitute each equation
|
||||
(← `(by intro $params* {..} {..}; intros; subst_eqs; rfl))
|
||||
let pf ← instantiateMVars pf
|
||||
if pf.hasMVar then throwError "(internal error) synthesized ext proof contains metavariables{indentD pf}"
|
||||
let info ← getConstInfo structName
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name := extName
|
||||
type
|
||||
value := pf
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
modifyEnv fun env => addProtected env extName
|
||||
Lean.addDeclarationRanges extName {
|
||||
range := ← getDeclarationRange (← getRef)
|
||||
selectionRange := ← getDeclarationRange (← getRef) }
|
||||
try
|
||||
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extName do
|
||||
let type ← mkExtType structName flat
|
||||
let pf ← withSynthesize do
|
||||
let indVal ← getConstInfoInduct structName
|
||||
let params := Array.mkArray indVal.numParams (← `(_))
|
||||
Elab.Term.elabTermEnsuringType (expectedType? := type) (implicitLambda := false)
|
||||
-- introduce the params, do cases on 'x' and 'y', and then substitute each equation
|
||||
(← `(by intro $params* {..} {..}; intros; subst_eqs; rfl))
|
||||
let pf ← instantiateMVars pf
|
||||
if pf.hasMVar then throwError "(internal error) synthesized ext proof contains metavariables{indentD pf}"
|
||||
let info ← getConstInfo structName
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name := extName
|
||||
type
|
||||
value := pf
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
modifyEnv fun env => addProtected env extName
|
||||
Lean.addDeclarationRanges extName {
|
||||
range := ← getDeclarationRange (← getRef)
|
||||
selectionRange := ← getDeclarationRange (← getRef) }
|
||||
catch e =>
|
||||
throwError m!"\
|
||||
Failed to generate an 'ext' theorem for '{MessageData.ofConstName structName}': {e.toMessageData}"
|
||||
return extName
|
||||
|
||||
/--
|
||||
@@ -133,29 +142,35 @@ def realizeExtIffTheorem (extName : Name) : Elab.Command.CommandElabM Name := do
|
||||
| .str n s => .str n (s ++ "_iff")
|
||||
| _ => .str extName "ext_iff"
|
||||
unless (← getEnv).contains extIffName do
|
||||
let info ← getConstInfo extName
|
||||
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extIffName do
|
||||
let type ← mkExtIffType extName
|
||||
let pf ← withSynthesize do
|
||||
Elab.Term.elabTermEnsuringType (expectedType? := type) <| ← `(by
|
||||
intros
|
||||
refine ⟨?_, ?_⟩
|
||||
· intro h; cases h; and_intros <;> (intros; first | rfl | simp | fail "Failed to prove converse of ext theorem")
|
||||
· intro; (repeat cases ‹_ ∧ _›); apply $(mkCIdent extName) <;> assumption)
|
||||
let pf ← instantiateMVars pf
|
||||
if pf.hasMVar then throwError "(internal error) synthesized ext_iff proof contains metavariables{indentD pf}"
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name := extIffName
|
||||
type
|
||||
value := pf
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
-- Only declarations in a namespace can be protected:
|
||||
unless extIffName.isAtomic do
|
||||
modifyEnv fun env => addProtected env extIffName
|
||||
Lean.addDeclarationRanges extIffName {
|
||||
range := ← getDeclarationRange (← getRef)
|
||||
selectionRange := ← getDeclarationRange (← getRef) }
|
||||
try
|
||||
let info ← getConstInfo extName
|
||||
Elab.Command.liftTermElabM <| withoutErrToSorry <| withDeclName extIffName do
|
||||
let type ← mkExtIffType extName
|
||||
let pf ← withSynthesize do
|
||||
Elab.Term.elabTermEnsuringType (expectedType? := type) <| ← `(by
|
||||
intros
|
||||
refine ⟨?_, ?_⟩
|
||||
· intro h; cases h; and_intros <;> (intros; first | rfl | simp | fail "Failed to prove converse of ext theorem")
|
||||
· intro; (repeat cases ‹_ ∧ _›); apply $(mkCIdent extName) <;> assumption)
|
||||
let pf ← instantiateMVars pf
|
||||
if pf.hasMVar then throwError "(internal error) synthesized ext_iff proof contains metavariables{indentD pf}"
|
||||
addDecl <| Declaration.thmDecl {
|
||||
name := extIffName
|
||||
type
|
||||
value := pf
|
||||
levelParams := info.levelParams
|
||||
}
|
||||
-- Only declarations in a namespace can be protected:
|
||||
unless extIffName.isAtomic do
|
||||
modifyEnv fun env => addProtected env extIffName
|
||||
Lean.addDeclarationRanges extIffName {
|
||||
range := ← getDeclarationRange (← getRef)
|
||||
selectionRange := ← getDeclarationRange (← getRef) }
|
||||
catch e =>
|
||||
throwError m!"\
|
||||
Failed to generate an 'ext_iff' theorem from '{MessageData.ofConstName extName}': {e.toMessageData}\n\
|
||||
\n\
|
||||
Try '@[ext (iff := false)]' to prevent generating an 'ext_iff' theorem."
|
||||
return extIffName
|
||||
|
||||
|
||||
|
||||
@@ -263,9 +263,10 @@ where
|
||||
-- save all relevant syntax here for comparison with next document version
|
||||
stx := mkNullNode altStxs
|
||||
diagnostics := .empty
|
||||
finished := finished.result
|
||||
} (altStxs.zipWith altPromises fun stx prom =>
|
||||
{ range? := stx.getRange?, task := prom.result })
|
||||
finished := { range? := none, task := finished.result }
|
||||
next := altStxs.zipWith altPromises fun stx prom =>
|
||||
{ range? := stx.getRange?, task := prom.result }
|
||||
}
|
||||
goWithIncremental <| altPromises.mapIdx fun i prom => {
|
||||
old? := do
|
||||
let old ← tacSnap.old?
|
||||
@@ -274,10 +275,10 @@ where
|
||||
let old := old.val.get
|
||||
-- use old version of `mkNullNode altsSyntax` as guard, will be compared with new
|
||||
-- version and picked apart in `applyAltStx`
|
||||
return ⟨old.data.stx, (← old.next[i]?)⟩
|
||||
return ⟨old.data.stx, (← old.data.next[i]?)⟩
|
||||
new := prom
|
||||
}
|
||||
finished.resolve { state? := (← saveState) }
|
||||
finished.resolve { diagnostics := .empty, state? := (← saveState) }
|
||||
return
|
||||
|
||||
goWithIncremental #[]
|
||||
@@ -564,7 +565,7 @@ def getInductiveValFromMajor (major : Expr) : TacticM InductiveVal :=
|
||||
|
||||
/--
|
||||
Elaborates the term in the `using` clause. We want to allow parameters to be instantiated
|
||||
(e.g. `using foo (p := …)`), but preserve other paramters, like the motives, as parameters,
|
||||
(e.g. `using foo (p := …)`), but preserve other parameters, like the motives, as parameters,
|
||||
without turning them into MVars. So this uses `abstractMVars` at the end. This is inspired by
|
||||
`Lean.Elab.Tactic.addSimpTheorem`.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Scott Morrison
|
||||
prelude
|
||||
import Init.BinderPredicates
|
||||
import Init.Data.Int.Order
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.List.MinMax
|
||||
import Init.Data.Nat.MinMax
|
||||
import Init.Data.Option.Lemmas
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user