mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-19 03:14:08 +00:00
Compare commits
1 Commits
fix
...
doc_replac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29cc25963f |
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
|
||||
# `:` means do not prefix with msystem
|
||||
pacboy: "make: python: cmake clang ccache gmp git: zip: unzip: diffutils: binutils: tree: zstd tar:"
|
||||
# `: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"
|
||||
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 check-stage3
|
||||
make -C build -j$NPROC stage3
|
||||
if: matrix.test-speedcenter
|
||||
- name: Test Speedcenter Benchmarks
|
||||
run: |
|
||||
@@ -455,24 +455,12 @@ 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:
|
||||
@@ -545,8 +533,3 @@ 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
34
.github/workflows/jira.yml
vendored
@@ -1,34 +0,0 @@
|
||||
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,20 +63,6 @@ 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: 19 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 57 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 23 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 -- -3
|
||||
#eval -10 / 4 -- -2
|
||||
```
|
||||
|
||||
Similar to `Nat`, the internal representation of `Int` is optimized. Small integers are
|
||||
|
||||
@@ -7,17 +7,12 @@ 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` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Docs: Show Setup Guide".
|
||||
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Setup: Show Setup Guide".
|
||||
|
||||

|
||||

|
||||
|
||||
1. Follow the Lean 4 setup guide. It will:
|
||||
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.
|
||||
|
||||
- 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.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
* 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,6 +1,5 @@
|
||||
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()
|
||||
|
||||
@@ -474,8 +474,6 @@ 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 }`. -/
|
||||
@@ -703,7 +701,7 @@ theorem Ne.elim (h : a ≠ b) : a = b → False := h
|
||||
|
||||
theorem Ne.irrefl (h : a ≠ a) : False := h rfl
|
||||
|
||||
@[symm] theorem Ne.symm (h : a ≠ b) : b ≠ a := fun h₁ => h (h₁.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⟩
|
||||
|
||||
@@ -756,7 +754,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₂
|
||||
|
||||
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
|
||||
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' :=
|
||||
@@ -812,15 +810,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
|
||||
|
||||
@[symm] theorem Iff.symm (h : a ↔ b) : b ↔ a := Iff.intro h.mpr h.mp
|
||||
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
|
||||
|
||||
@[symm] theorem And.symm : a ∧ b → b ∧ a := fun ⟨ha, hb⟩ => ⟨hb, ha⟩
|
||||
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
|
||||
|
||||
@[symm] theorem Or.symm : a ∨ b → b ∨ a := .rec .inr .inl
|
||||
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
|
||||
|
||||
@@ -1107,7 +1105,6 @@ inductive Relation.TransGen {α : Sort u} (r : α → α → Prop) : α → α
|
||||
/-! # Subtype -/
|
||||
|
||||
namespace Subtype
|
||||
|
||||
theorem existsOfSubtype {α : Type u} {p : α → Prop} : { x // p x } → Exists (fun x => p x)
|
||||
| ⟨a, h⟩ => ⟨a, h⟩
|
||||
|
||||
@@ -1204,13 +1201,9 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
|
||||
|
||||
/-! # Dependent products -/
|
||||
|
||||
theorem PSigma.exists {α : Type u} {p : α → Prop} : (PSigma (fun x => p x)) → Exists (fun x => p x)
|
||||
theorem ex_of_PSigma {α : Type 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₁
|
||||
@@ -1552,7 +1545,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, elab_as_elim] protected abbrev recOn
|
||||
@[inherit_doc Quot.rec] 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)
|
||||
@@ -1563,7 +1556,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.
|
||||
-/
|
||||
@[elab_as_elim] protected abbrev recOnSubsingleton
|
||||
protected abbrev recOnSubsingleton
|
||||
[h : (a : α) → Subsingleton (motive (Quot.mk r a))]
|
||||
(q : Quot r)
|
||||
(f : (a : α) → motive (Quot.mk r a))
|
||||
|
||||
@@ -36,4 +36,3 @@ import Init.Data.Channel
|
||||
import Init.Data.Cast
|
||||
import Init.Data.Sum
|
||||
import Init.Data.BEq
|
||||
import Init.Data.Subtype
|
||||
|
||||
@@ -50,13 +50,6 @@ 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`. -/
|
||||
@@ -181,7 +174,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 := as.usize
|
||||
let sz := USize.ofNat as.size
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
if i < sz then
|
||||
let a := as.uget i lcProof
|
||||
@@ -287,7 +280,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 := as.usize
|
||||
let sz := USize.ofNat as.size
|
||||
let rec @[specialize] map (i : USize) (r : Array NonScalar) : m (Array PNonScalar.{v}) := do
|
||||
if i < sz then
|
||||
let v := r.uget i lcProof
|
||||
|
||||
@@ -98,37 +98,6 @@ 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))
|
||||
|
||||
@@ -321,7 +290,7 @@ theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow (x : BitVec w
|
||||
simp [hik', hik'']
|
||||
· ext k
|
||||
simp
|
||||
by_cases hi : x.getLsb i <;> simp [hi] <;> omega
|
||||
omega
|
||||
|
||||
/--
|
||||
Recurrence lemma: multiplying `l` with the first `s` bits of `r` is the
|
||||
@@ -345,7 +314,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]
|
||||
simp only [ofNat_eq_ofNat, and_twoPow_eq]
|
||||
by_cases hr : r.getLsb (s' + 1) <;> simp [hr]
|
||||
rw [heq, ← BitVec.mul_add, ← zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow]
|
||||
|
||||
@@ -357,78 +326,4 @@ 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,12 +436,6 @@ 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]
|
||||
@@ -537,11 +531,6 @@ 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) :
|
||||
@@ -637,10 +626,6 @@ 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]
|
||||
@@ -706,22 +691,6 @@ 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) :
|
||||
@@ -1483,18 +1452,12 @@ theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j))
|
||||
simp at hi
|
||||
simp_all
|
||||
|
||||
@[simp]
|
||||
theorem and_twoPow (x : BitVec w) (i : Nat) :
|
||||
theorem and_twoPow_eq (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
|
||||
@@ -1508,14 +1471,6 @@ 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,10 +37,6 @@ 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]
|
||||
@@ -123,7 +119,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 := as.usize
|
||||
let sz := USize.ofNat as.size
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
if i < sz then
|
||||
let a := as.uget i lcProof
|
||||
|
||||
@@ -37,10 +37,6 @@ 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]
|
||||
@@ -94,7 +90,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 := as.usize
|
||||
let sz := USize.ofNat as.size
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
if i < sz then
|
||||
let a := as.uget i lcProof
|
||||
|
||||
@@ -12,5 +12,3 @@ import Init.Data.List.Attach
|
||||
import Init.Data.List.Impl
|
||||
import Init.Data.List.TakeDrop
|
||||
import Init.Data.List.Notation
|
||||
import Init.Data.List.Range
|
||||
import Init.Data.List.Nat
|
||||
|
||||
@@ -5,7 +5,6 @@ Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.Subtype
|
||||
|
||||
namespace List
|
||||
|
||||
@@ -45,155 +44,3 @@ 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₂ _
|
||||
|
||||
@@ -27,32 +27,24 @@ Recall that `length`, `get`, `set`, `foldl`, and `concat` have already been defi
|
||||
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`.
|
||||
* Additional functions defined in terms of these: `leftpad`, `rightPad`, and `reduceOption`.
|
||||
`map`, `filter`, `filterMap`, `foldr`, `append`, `join`, `pure`, `bind`, `replicate`, and `reverse`.
|
||||
* 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`, `eraseP`, `eraseIdx`.
|
||||
* Finding elements: `find?`, `findSome?`, `findIdx`, `indexOf`, `findIdx?`, `indexOf?`,
|
||||
`countP`, `count`, and `lookup`.
|
||||
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`, `rotateLeft` and `rotateRight`.
|
||||
* Manipulating elements: `replace`, `insert`, `erase`, `eraseP`, `eraseIdx`, `find?`, `findSome?`, 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!`, `tail!`.
|
||||
* Head and tail: `head`, `head!`, `head?`, `headD`, `tail!`, `tail?`, and `tailD`.
|
||||
* Other operations on sublists: `partitionMap`, `rotateLeft`, and `rotateRight`.
|
||||
-/
|
||||
|
||||
@@ -323,16 +315,6 @@ 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? -/
|
||||
|
||||
/--
|
||||
@@ -595,28 +577,6 @@ 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`.
|
||||
@@ -759,7 +719,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_succ_cons : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
@[simp] theorem take_cons_succ : (a::as).take (i+1) = a :: as.take i := rfl
|
||||
|
||||
/-! ### drop -/
|
||||
|
||||
@@ -866,6 +826,46 @@ 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,68 +900,6 @@ 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 -/
|
||||
|
||||
/--
|
||||
@@ -1120,8 +1058,6 @@ 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? -/
|
||||
|
||||
/--
|
||||
@@ -1159,50 +1095,6 @@ 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 -/
|
||||
|
||||
/--
|
||||
@@ -1344,14 +1236,6 @@ 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 -/
|
||||
|
||||
/--
|
||||
@@ -1367,14 +1251,6 @@ 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.reverse
|
||||
| [], bs => pure bs
|
||||
| a :: as, bs => do
|
||||
match (← f a) with
|
||||
| none => loop as bs
|
||||
| some b => loop as (b::bs)
|
||||
loop as []
|
||||
loop as.reverse []
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from left to right:
|
||||
@@ -227,8 +227,6 @@ 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
|
||||
|
||||
|
||||
@@ -193,17 +193,6 @@ 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 -/
|
||||
@@ -377,26 +366,6 @@ 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
@@ -1,93 +0,0 @@
|
||||
/-
|
||||
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
|
||||
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)
|
||||
|
||||
/-! ### 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
|
||||
@@ -1,387 +0,0 @@
|
||||
/-
|
||||
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
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
# 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
|
||||
@@ -32,6 +32,46 @@ theorem length_take_le' (n) (l : List α) : length (take n l) ≤ l.length :=
|
||||
|
||||
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) :
|
||||
@@ -117,54 +157,6 @@ theorem getLast_take {l : List α} (h : l.take n ≠ []) :
|
||||
· 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
|
||||
@@ -172,13 +164,13 @@ theorem take_eq_take :
|
||||
| _ :: 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]
|
||||
| x :: xs, m + 1, n + 1 => by simp [succ_min_succ, take_eq_take]; omega
|
||||
|
||||
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]
|
||||
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)
|
||||
@@ -186,8 +178,8 @@ theorem take_add (l : List α) (m n : Nat) : l.take (m + n) = l.take m ++ (l.dro
|
||||
· 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]
|
||||
(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]
|
||||
|
||||
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
|
||||
@@ -201,6 +193,42 @@ theorem map_eq_append_split {f : α → β} {l : List α} {s₁ s₂ : List β}
|
||||
|
||||
/-! ### drop -/
|
||||
|
||||
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 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
|
||||
@@ -279,41 +307,6 @@ theorem getLast_drop {l : List α} (h : l.drop n ≠ []) :
|
||||
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
|
||||
@@ -323,7 +316,7 @@ theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
|
||||
getElem?_take h']
|
||||
· by_cases h'' : m = n
|
||||
· subst h''
|
||||
rw [getElem?_set_eq ‹_›, getElem?_append_right, length_take,
|
||||
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
|
||||
@@ -359,7 +352,7 @@ theorem drop_take : ∀ (m n : Nat) (l : List α), drop n (take m l) = take (m -
|
||||
congr 1
|
||||
omega
|
||||
|
||||
theorem take_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
theorem take_reverse {α} {xs : List α} (n : Nat) (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]
|
||||
@@ -367,7 +360,7 @@ theorem take_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
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 [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]
|
||||
@@ -381,19 +374,6 @@ theorem take_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
|
||||
@[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
|
||||
@@ -427,43 +407,12 @@ theorem drop_reverse {α} {xs : List α} {n : Nat} (h : n ≤ xs.length) :
|
||||
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]
|
||||
@@ -475,20 +424,6 @@ theorem reverse_zipWith (h : l.length = l'.length) :
|
||||
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
|
||||
@@ -500,4 +435,70 @@ theorem zip_eq_zip_take_min : ∀ (l₁ : List α) (l₂ : List β),
|
||||
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
|
||||
|
||||
end List
|
||||
|
||||
@@ -388,11 +388,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
|
||||
|
||||
@[simp] theorem le_add_right : ∀ (n k : Nat), n ≤ n + k
|
||||
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)
|
||||
|
||||
@[simp] theorem le_add_left (n m : Nat): n ≤ m + n :=
|
||||
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 +528,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
|
||||
|
||||
@[simp] protected theorem add_le_add_iff_right {n : Nat} : m + n ≤ k + n ↔ m ≤ k :=
|
||||
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 -/
|
||||
|
||||
@@ -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 only [x_eq, Nat.lt_add_right_iff_pos]
|
||||
exact Nat.two_pow_pos j
|
||||
simp [x_eq]
|
||||
exact Nat.lt_add_of_pos_right (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,9 +46,6 @@ 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,9 +19,6 @@ and later these lemmas should be organised into other files more systematically.
|
||||
-/
|
||||
|
||||
namespace Nat
|
||||
|
||||
attribute [simp] not_lt_zero
|
||||
|
||||
/-! ## add -/
|
||||
|
||||
protected theorem add_add_add_comm (a b c d : Nat) : (a + b) + (c + d) = (a + c) + (b + d) := by
|
||||
@@ -39,13 +36,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₁⟩
|
||||
|
||||
@[simp] protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k ↔ m = k :=
|
||||
protected theorem add_left_cancel_iff {n : Nat} : n + m = n + k ↔ m = k :=
|
||||
⟨Nat.add_left_cancel, fun | rfl => rfl⟩
|
||||
|
||||
@[simp] protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n ↔ m = k :=
|
||||
protected theorem add_right_cancel_iff {n : Nat} : m + n = k + n ↔ m = k :=
|
||||
⟨Nat.add_right_cancel, fun | rfl => rfl⟩
|
||||
|
||||
@[simp] protected theorem add_le_add_iff_left {n : Nat} : n + m ≤ n + k ↔ m ≤ k :=
|
||||
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
|
||||
@@ -55,10 +52,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
|
||||
|
||||
@[simp] protected theorem add_lt_add_iff_left {k n m : Nat} : k + n < k + m ↔ n < m :=
|
||||
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 _⟩
|
||||
|
||||
@[simp] protected theorem add_lt_add_iff_right {k n m : Nat} : n + k < m + k ↔ n < m :=
|
||||
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) :
|
||||
@@ -78,10 +75,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
|
||||
|
||||
@[simp] protected theorem lt_add_right_iff_pos : n < n + k ↔ 0 < k :=
|
||||
protected theorem lt_add_right_iff_pos : n < n + k ↔ 0 < k :=
|
||||
⟨Nat.pos_of_lt_add_right, Nat.lt_add_of_pos_right⟩
|
||||
|
||||
@[simp] protected theorem lt_add_left_iff_pos : n < k + n ↔ 0 < k :=
|
||||
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,9 +212,6 @@ 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)
|
||||
|
||||
@@ -193,16 +193,6 @@ theorem mem_map_of_mem (g : α → β) (h : a ∈ x) : g a ∈ Option.map g x :=
|
||||
@[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
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/-
|
||||
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
|
||||
@@ -399,16 +399,9 @@ 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 trail endPos =>
|
||||
stx.setTailInfo (SourceInfo.original lead pos { trail with stopPos := trail.startPos } endPos)
|
||||
| SourceInfo.original lead pos _ endPos => stx.setTailInfo (SourceInfo.original lead pos "".toSubstring 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: `true`), `simp` will **not** create a proof for a rewriting rule associated
|
||||
When `true` (default: `false`), `simp` will **not** create a proof for a rewriting rule associated
|
||||
with an `rfl`-theorem.
|
||||
Rewriting rules are provided by users by annotating theorems with the attribute `@[simp]`.
|
||||
If the proof of the theorem is just `rfl` (reflexivity), and `implicitDefEqProofs := true`, `simp`
|
||||
will **not** create a proof term which is an application of the annotated theorem.
|
||||
-/
|
||||
implicitDefEqProofs : Bool := true
|
||||
implicitDefEqProofs : Bool := false
|
||||
deriving Inhabited, BEq
|
||||
|
||||
-- Configuration object for `simp_all`
|
||||
|
||||
@@ -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)
|
||||
-/
|
||||
@[symm] theorem Eq.symm {α : Sort u} {a b : α} (h : Eq a b) : Eq b a :=
|
||||
theorem Eq.symm {α : Sort u} {a b : α} (h : Eq a b) : Eq b a :=
|
||||
h ▸ rfl
|
||||
|
||||
/--
|
||||
@@ -2214,17 +2214,12 @@ 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.get? : HashMap α β → α → Option β` looks up
|
||||
For example, the function `HashMap.find? : 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 β :=
|
||||
|
||||
@@ -309,11 +309,6 @@ 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⟩⟩
|
||||
|
||||
|
||||
@@ -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 || s.startsWith s!"{suffix}_") && isAuxRecursor env declName
|
||||
| .str _ s => s == suffix && isAuxRecursor env declName
|
||||
| _ => false
|
||||
|
||||
def isCasesOnRecursor (env : Environment) (declName : Name) : Bool :=
|
||||
|
||||
@@ -5,7 +5,6 @@ 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
|
||||
@@ -712,12 +711,6 @@ 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
|
||||
|
||||
@@ -731,8 +724,8 @@ structure State where
|
||||
namedArgs : List NamedArg
|
||||
/-- User-provided arguments that still have to be processed. -/
|
||||
args : List Arg
|
||||
/-- Discriminants (targets) processed so far. -/
|
||||
discrs : Array (Option Expr)
|
||||
/-- Discriminants processed so far. -/
|
||||
discrs : Array 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. -/
|
||||
@@ -749,7 +742,10 @@ def mkMotive (discrs : Array Expr) (expectedType : Expr): MetaM Expr := do
|
||||
let motiveBody ← kabstract motive discr
|
||||
/- We use `transform (usedLetOnly := true)` to eliminate unnecessary let-expressions. -/
|
||||
let discrType ← transform (usedLetOnly := true) (← instantiateMVars (← inferType discr))
|
||||
return Lean.mkLambda (← mkFreshBinderName) BinderInfo.default discrType motiveBody
|
||||
let motive := Lean.mkLambda (← mkFreshBinderName) BinderInfo.default discrType motiveBody
|
||||
unless (← isTypeCorrect motive) do
|
||||
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motive}"
|
||||
return motive
|
||||
|
||||
/-- If the eliminator is over-applied, we "revert" the extra arguments. -/
|
||||
def revertArgs (args : List Arg) (f : Expr) (expectedType : Expr) : TermElabM (Expr × Expr) :=
|
||||
@@ -765,7 +761,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 been elaborated, and we have
|
||||
Construct the resulting application after all discriminants have bee elaborated, and we have
|
||||
consumed as many given arguments as possible.
|
||||
-/
|
||||
def finalize : M Expr := do
|
||||
@@ -773,50 +769,29 @@ 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
|
||||
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}"
|
||||
try
|
||||
expectedType ← instantiateForall expectedType xs
|
||||
catch _ =>
|
||||
throwError "failed to elaborate eliminator, insufficient number of arguments, expected type:{indentExpr expectedType}"
|
||||
else
|
||||
-- over-application, simulate `revert` while generalizing the values of these arguments in the expected type
|
||||
-- over-application, simulate `revert`
|
||||
(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 discrs.any Option.isNone then
|
||||
if (← get).discrs.size < (← read).elimInfo.targetsPos.size then
|
||||
for i in [idx:idx + xs.size], x in xs do
|
||||
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}"
|
||||
if (← read).elimInfo.targetsPos.contains i then
|
||||
discrs := discrs.push x
|
||||
let motiveVal ← mkMotive discrs expectedType
|
||||
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
|
||||
|
||||
@@ -844,9 +819,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, 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 }
|
||||
/-- 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 }
|
||||
|
||||
/-- Elaborate the given argument with the given expected type. -/
|
||||
private def elabArg (arg : Arg) (argExpectedType : Expr) : M Expr := do
|
||||
@@ -881,11 +856,11 @@ partial def main : M Expr := do
|
||||
let motive ← mkImplicitArg binderType binderInfo
|
||||
setMotive motive
|
||||
addArgAndContinue motive
|
||||
else if let some tidx := (← read).elimInfo.targetsPos.indexOf? idx then
|
||||
else if (← read).elimInfo.targetsPos.contains idx then
|
||||
match (← getNextArg? binderName binderInfo) with
|
||||
| .some arg => let discr ← elabArg arg binderType; addDiscr tidx discr; addArgAndContinue discr
|
||||
| .some arg => let discr ← elabArg arg binderType; addDiscr discr; addArgAndContinue discr
|
||||
| .undef => finalize
|
||||
| .none => let discr ← mkImplicitArg binderType binderInfo; addDiscr tidx discr; addArgAndContinue discr
|
||||
| .none => let discr ← mkImplicitArg binderType binderInfo; addDiscr discr; addArgAndContinue discr
|
||||
else match (← getNextArg? binderName binderInfo) with
|
||||
| .some (.stx stx) =>
|
||||
if (← read).extraArgsPos.contains idx then
|
||||
@@ -947,12 +922,10 @@ 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' {
|
||||
@@ -982,29 +955,19 @@ where
|
||||
|
||||
/--
|
||||
Collect extra argument positions that must be elaborated eagerly when using `elab_as_elim`.
|
||||
The idea is that they contribute to motive inference. See comment at `ElamElim.Context.extraArgsPos`.
|
||||
The idea is that the 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 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 resultArgs := type.getAppArgs
|
||||
let mut extraArgsPos := #[]
|
||||
for i in [:xs.size] do
|
||||
let x := xs[i]!
|
||||
unless elimInfo.motivePos == i || elimInfo.targetsPos.contains i do
|
||||
let xType ← x.fvarId!.getType
|
||||
unless elimInfo.targetsPos.contains i do
|
||||
let xType ← inferType x
|
||||
/- We only consider "first-order" types because we can reliably "extract" information from them. -/
|
||||
if motiveFVars.fvarSet.contains x.fvarId!
|
||||
|| (isFirstOrder xType
|
||||
&& Option.isSome (xType.find? fun e => e.isFVar && motiveFVars.fvarSet.contains e.fvarId!)) then
|
||||
if isFirstOrder xType
|
||||
&& Option.isSome (xType.find? fun e => e.isFVar && resultArgs.contains e) then
|
||||
extraArgsPos := extraArgsPos.push i
|
||||
return extraArgsPos
|
||||
|
||||
@@ -1354,17 +1317,9 @@ 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"
|
||||
withForallBody expectedType fun resultType => do
|
||||
forallTelescopeReducing 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
|
||||
@@ -1380,8 +1335,7 @@ where
|
||||
| ex@(.error ..) =>
|
||||
match (← unfoldDefinition? resultType) with
|
||||
| some resultType =>
|
||||
withForallBody resultType fun resultType => do
|
||||
go resultType expectedType (previousExceptions.push ex)
|
||||
go (← whnfCore resultType) expectedType (previousExceptions.push ex)
|
||||
| none =>
|
||||
previousExceptions.forM fun ex => logException ex
|
||||
throw ex
|
||||
@@ -1574,6 +1528,5 @@ 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,7 +5,6 @@ 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
|
||||
@@ -341,7 +340,8 @@ 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 elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax): CommandElabM Unit := do
|
||||
unsafe def elabEvalUnsafe : CommandElab
|
||||
| `(#eval%$tk $term) => do
|
||||
let declName := `_eval
|
||||
let addAndCompile (value : Expr) : TermElabM Unit := do
|
||||
let value ← Term.levelMVarToParam (← instantiateMVars value)
|
||||
@@ -358,13 +358,6 @@ unsafe def elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax): CommandElabM Uni
|
||||
}
|
||||
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
|
||||
@@ -393,7 +386,6 @@ unsafe def elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax): CommandElabM Uni
|
||||
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
|
||||
@@ -410,7 +402,6 @@ unsafe def elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax): CommandElabM Uni
|
||||
-- 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
|
||||
@@ -421,19 +412,10 @@ unsafe def elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax): CommandElabM Uni
|
||||
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 evalBang]
|
||||
def elabEvalBang : CommandElab
|
||||
| `(Parser.Command.evalBang|#eval!%$tk $term) => elabEvalCore true tk term
|
||||
| _ => throwUnsupportedSyntax
|
||||
@[builtin_command_elab «eval», implemented_by elabEvalUnsafe]
|
||||
opaque elabEval : CommandElab
|
||||
|
||||
private def checkImportsForRunCmds : CommandElabM Unit := do
|
||||
unless (← getEnv).contains ``CommandElabM do
|
||||
|
||||
@@ -362,7 +362,4 @@ 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,62 +12,17 @@ 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 := []
|
||||
/--
|
||||
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.
|
||||
-/
|
||||
/-- section variables -/
|
||||
varDecls : Array (TSyntax ``Parser.Term.bracketedBinder) := #[]
|
||||
/--
|
||||
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.
|
||||
-/
|
||||
/-- Globally unique internal identifiers for the `varDecls` -/
|
||||
varUIds : Array Name := #[]
|
||||
/--
|
||||
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.
|
||||
-/
|
||||
/-- noncomputable sections automatically add the `noncomputable` modifier to any declaration we cannot generate code for. -/
|
||||
isNoncomputable : Bool := false
|
||||
deriving Inhabited
|
||||
|
||||
@@ -275,7 +230,6 @@ 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
|
||||
@@ -525,7 +479,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 }
|
||||
@@ -658,11 +612,6 @@ 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,10 +43,12 @@ where
|
||||
let mut ctorArgs1 := #[]
|
||||
let mut ctorArgs2 := #[]
|
||||
let mut rhs ← `(true)
|
||||
let mut rhs_empty := true
|
||||
-- add `_` for inductive parameters, they are inaccessible
|
||||
for _ in [:indVal.numParams] do
|
||||
ctorArgs1 := ctorArgs1.push (← `(_))
|
||||
ctorArgs2 := ctorArgs2.push (← `(_))
|
||||
for i in [:ctorInfo.numFields] do
|
||||
let pos := indVal.numParams + ctorInfo.numFields - i - 1
|
||||
let x := xs[pos]!
|
||||
let x := xs[indVal.numParams + i]!
|
||||
if type.containsFVar x.fvarId! then
|
||||
-- If resulting type depends on this field, we don't need to compare
|
||||
ctorArgs1 := ctorArgs1.push (← `(_))
|
||||
@@ -60,32 +62,11 @@ where
|
||||
if (← isProp xType) then
|
||||
continue
|
||||
if xType.isAppOf indVal.name then
|
||||
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
|
||||
rhs ← `($rhs && $(mkIdent auxFunName):ident $a:ident $b:ident)
|
||||
else
|
||||
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*))
|
||||
rhs ← `($rhs && $a:ident == $b:ident)
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName):ident $ctorArgs1:term*))
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName):ident $ctorArgs2:term*))
|
||||
`(matchAltExpr| | $[$patterns:term],* => $rhs:term)
|
||||
alts := alts.push alt
|
||||
alts := alts.push (← mkElseAlt)
|
||||
|
||||
@@ -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 checkpointDefEq (mayPostpone := false) do
|
||||
eqs.anyM fun eq => commitWhen do
|
||||
try
|
||||
let subgoals ← mvarId.apply (← mkConstWithFreshMVarLevels eq)
|
||||
subgoals.allM fun subgoal => do
|
||||
|
||||
@@ -114,7 +114,7 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
|
||||
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.")
|
||||
|
||||
@@ -5,11 +5,11 @@ 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
|
||||
@@ -21,11 +21,11 @@ private def throwToBelowFailed : MetaM α :=
|
||||
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)
|
||||
(do searchPProd d1 (← mkAppM ``PProd.fst #[F]) k)
|
||||
<|> (do searchPProd d2 (← mkAppM `PProd.snd #[F]) k)
|
||||
| .app (.app (.const `And _) d1) d2 =>
|
||||
(do searchPProd d1 (.proj `And 0 F) k)
|
||||
<|> (do searchPProd d2 (.proj `And 1 F) k)
|
||||
(do searchPProd d1 (← mkAppM `And.left #[F]) k)
|
||||
<|> (do searchPProd d2 (← mkAppM `And.right #[F]) k)
|
||||
| .const `PUnit _
|
||||
| .const `True _ => throwToBelowFailed
|
||||
| _ => k e F
|
||||
@@ -85,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 PProdN.packLambdas motiveTypes Cs
|
||||
let packedCs ← positions.mapMwith packMotives motiveTypes Cs
|
||||
let belowDict := mkAppN pre packedCs
|
||||
let belowDict := mkAppN belowDict finalArgs
|
||||
trace[Elab.definition.structural] "initial belowDict for {Cs}:{indentExpr belowDict}"
|
||||
@@ -245,12 +245,23 @@ def mkBRecOnConst (recArgInfos : Array RecArgInfo) (positions : Positions)
|
||||
decLevel brecOnUniv
|
||||
else
|
||||
pure brecOnUniv
|
||||
let brecOnCons := fun idx => indGroup.brecOn useBInductionOn brecOnUniv idx
|
||||
let brecOnCons := fun idx =>
|
||||
let brecOn :=
|
||||
if let .some n := indGroup.all[idx]? then
|
||||
if useBInductionOn then .const (mkBInductionOnName n) indGroup.levels
|
||||
else .const (mkBRecOnName n) (brecOnUniv :: indGroup.levels)
|
||||
else
|
||||
let n := indGroup.all[0]!
|
||||
let j := idx - indGroup.all.size + 1
|
||||
if useBInductionOn then .const (mkBInductionOnName n |>.appendIndexAfter j) indGroup.levels
|
||||
else .const (mkBRecOnName n |>.appendIndexAfter j) (brecOnUniv :: indGroup.levels)
|
||||
mkAppN brecOn indGroup.params
|
||||
|
||||
-- Pick one as a prototype
|
||||
let brecOnAux := brecOnCons 0
|
||||
-- Infer the type of the packed motive arguments
|
||||
let packedMotiveTypes ← inferArgumentTypesN indGroup.numMotives brecOnAux
|
||||
let packedMotives ← positions.mapMwith PProdN.packLambdas packedMotiveTypes motives
|
||||
let packedMotives ← positions.mapMwith packMotives packedMotiveTypes motives
|
||||
|
||||
return fun n => mkAppN (brecOnCons n) packedMotives
|
||||
|
||||
@@ -289,11 +300,12 @@ def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Nat → Exp
|
||||
let brecOn := brecOnConst recArgInfo.indIdx
|
||||
let brecOn := mkAppN brecOn indexMajorArgs
|
||||
let packedFTypes ← inferArgumentTypesN positions.size brecOn
|
||||
let packedFArgs ← positions.mapMwith PProdN.mkLambdas packedFTypes FArgs
|
||||
let packedFArgs ← positions.mapMwith packFArgs packedFTypes FArgs
|
||||
let brecOn := mkAppN brecOn packedFArgs
|
||||
let some poss := positions.find? (·.contains fnIdx)
|
||||
| throwError "mkBrecOnApp: Could not find {fnIdx} in {positions}"
|
||||
let brecOn ← PProdN.proj poss.size (poss.getIdx? fnIdx).get! brecOn
|
||||
let brecOn ← if poss.size = 1 then pure brecOn else
|
||||
mkPProdProjN (poss.getIdx? fnIdx).get! brecOn
|
||||
mkLambdaFVars ys (mkAppN brecOn otherArgs)
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
@@ -21,7 +21,6 @@ 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
|
||||
@@ -82,11 +81,9 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
|
||||
|
||||
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo ← mkMapDeclarationExtension
|
||||
|
||||
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat)
|
||||
(numFixed : Nat) : CoreM Unit := do
|
||||
def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (recArgPos : Nat) : CoreM Unit := do
|
||||
ensureEqnReservedNamesAvailable preDef.declName
|
||||
modifyEnv fun env => eqnInfoExt.insert env preDef.declName
|
||||
{ preDef with recArgPos, declNames, numFixed }
|
||||
modifyEnv fun env => eqnInfoExt.insert env preDef.declName { preDef with recArgPos, declNames }
|
||||
|
||||
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
|
||||
if let some info := eqnInfoExt.find? (← getEnv) declName then
|
||||
|
||||
@@ -68,7 +68,9 @@ 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 indInfo.isReflexive && !(← hasConst (mkBInductionOnName indInfo.name)) && !(← isInductivePredicate indInfo.name) then
|
||||
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
|
||||
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
|
||||
@@ -261,11 +263,6 @@ def tryAllArgs (fnNames : Array Name) (xs : Array Expr) (values : Array Expr)
|
||||
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
|
||||
|
||||
126
src/Lean/Elab/PreDefinition/Structural/FunPacker.lean
Normal file
126
src/Lean/Elab/PreDefinition/Structural/FunPacker.lean
Normal file
@@ -0,0 +1,126 @@
|
||||
/-
|
||||
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
|
||||
@@ -36,16 +36,6 @@ def IndGroupInfo.ofInductiveVal (indInfo : InductiveVal) : IndGroupInfo where
|
||||
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.
|
||||
@@ -72,13 +62,6 @@ def IndGroupInst.isDefEq (igi1 igi2 : IndGroupInst) : MetaM Bool := do
|
||||
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.
|
||||
|
||||
@@ -12,35 +12,8 @@ import Lean.Elab.PreDefinition.Structural.RecArgInfo
|
||||
namespace Lean.Elab.Structural
|
||||
open Meta
|
||||
|
||||
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
|
||||
private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (motive : Expr) (e : Expr) : M Expr := do
|
||||
let maxDepth := IndPredBelow.maxBackwardChainingDepth.get (← getOptions)
|
||||
let rec loop (e : Expr) : M Expr := do
|
||||
match e with
|
||||
| Expr.lam n d b c =>
|
||||
@@ -62,7 +35,12 @@ private partial def replaceIndPredRecApps (recArgInfo : RecArgInfo) (funType : E
|
||||
let processApp (e : Expr) : M Expr := do
|
||||
e.withApp fun f args => do
|
||||
if f.isConstOf recArgInfo.fnName then
|
||||
replaceIndPredRecApp recArgInfo.numFixed funType e
|
||||
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!}"
|
||||
else
|
||||
return mkAppN (← loop f) (← args.mapM loop)
|
||||
match (← matchMatcherApp? e) with
|
||||
@@ -101,36 +79,33 @@ 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 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
|
||||
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.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 motive value
|
||||
let Farg ← mkLambdaFVars (indexMajorArgs ++ #[below] ++ otherArgs) valueNew
|
||||
let brecOn := mkApp brecOn Farg
|
||||
let brecOn := mkAppN brecOn otherArgs
|
||||
mkLambdaFVars ys brecOn
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
@@ -128,7 +128,7 @@ private def elimMutualRecursion (preDefs : Array PreDefinition) (xs : Array Expr
|
||||
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
|
||||
M (Array Nat × Array PreDefinition) := do
|
||||
withoutModifyingEnv do
|
||||
preDefs.forM (addAsAxiom ·)
|
||||
let fnNames := preDefs.map (·.declName)
|
||||
@@ -154,7 +154,7 @@ private def inferRecArgPos (preDefs : Array PreDefinition) (termArg?s : Array (O
|
||||
withErasedFVars (xs.extract numFixed xs.size |>.map (·.fvarId!)) do
|
||||
let xs := xs[:numFixed]
|
||||
let preDefs' ← elimMutualRecursion preDefs xs recArgInfos
|
||||
return (recArgPoss, preDefs', numFixed)
|
||||
return (recArgPoss, preDefs')
|
||||
|
||||
def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
if let some ref := preDef.termination.terminationBy?? then
|
||||
@@ -167,7 +167,7 @@ def reportTermArg (preDef : PreDefinition) (recArgPos : Nat) : MetaM Unit := do
|
||||
|
||||
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
|
||||
let ((recArgPoss, preDefsNonRec), state) ← run <| inferRecArgPos preDefs termArg?s
|
||||
for recArgPos in recArgPoss, preDef in preDefs do
|
||||
reportTermArg preDef recArgPos
|
||||
state.addMatchers.forM liftM
|
||||
@@ -190,7 +190,7 @@ def structuralRecursion (preDefs : Array PreDefinition) (termArg?s : Array (Opti
|
||||
for theorems and definitions that are propositions.
|
||||
See issue #2327
|
||||
-/
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos numFixed
|
||||
registerEqnsInfo preDef (preDefs.map (·.declName)) recArgPos
|
||||
addSmartUnfoldingDef preDef recArgPos
|
||||
markAsRecursive preDef.declName
|
||||
applyAttributesOf preDefsNonRec AttributeApplicationTime.afterCompilation
|
||||
|
||||
@@ -57,18 +57,18 @@ structure TerminationHints where
|
||||
|
||||
def TerminationHints.none : TerminationHints := ⟨.missing, .none, .none, .none, 0⟩
|
||||
|
||||
/-- Logs warnings when the `TerminationHints` are unexpectedly present. -/
|
||||
/-- Logs warnings when the `TerminationHints` are 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 =>
|
||||
logWarningAt dec_by.ref m!"unused `decreasing_by`, function is {reason}"
|
||||
logErrorAt dec_by.ref m!"unused `decreasing_by`, function is {reason}"
|
||||
| .some term_by?, .none, .none =>
|
||||
logWarningAt term_by? m!"unused `termination_by?`, function is {reason}"
|
||||
logErrorAt term_by? m!"unused `termination_by?`, function is {reason}"
|
||||
| .none, .some term_by, .none =>
|
||||
logWarningAt term_by.ref m!"unused `termination_by`, function is {reason}"
|
||||
logErrorAt term_by.ref m!"unused `termination_by`, function is {reason}"
|
||||
| _, _, _ =>
|
||||
logWarningAt hints.ref m!"unused termination hints, function is {reason}"
|
||||
logErrorAt hints.ref m!"unused termination hints, function is {reason}"
|
||||
|
||||
/-- True if any form of termination hint is present. -/
|
||||
def TerminationHints.isNotNone (hints : TerminationHints) : Bool :=
|
||||
|
||||
@@ -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,12 +120,40 @@ 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 axioms ← collectAxioms constName
|
||||
if axioms.isEmpty then
|
||||
let env ← getEnv
|
||||
let (_, s) := ((CollectAxioms.collect constName).run env).run {}
|
||||
if s.axioms.isEmpty then
|
||||
logInfo m!"'{constName}' does not depend on any axioms"
|
||||
else
|
||||
logInfo m!"'{constName}' depends on axioms: {axioms.qsort Name.lt |>.toList}"
|
||||
logInfo m!"'{constName}' depends on axioms: {s.axioms.qsort Name.lt |>.toList}"
|
||||
|
||||
@[builtin_command_elab «printAxioms»] def elabPrintAxioms : CommandElab
|
||||
| `(#print%$tk axioms $id) => withRef tk do
|
||||
|
||||
@@ -156,9 +156,7 @@ 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
|
||||
-- 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
|
||||
Term.withoutTacticIncrementality true do
|
||||
stx.getArgs.forM evalTactic
|
||||
else withTraceNode `Elab.step (fun _ => return stx) (tag := stx.getKind.toString) do
|
||||
let evalFns := tacticElabAttribute.getEntries (← getEnv) stx.getKind
|
||||
@@ -225,18 +223,14 @@ where
|
||||
snap.new.resolve <| .mk {
|
||||
stx := stx'
|
||||
diagnostics := .empty
|
||||
finished := .pure {
|
||||
diagnostics := .empty
|
||||
state? := (← Tactic.saveState)
|
||||
}
|
||||
next := #[{ range? := stx'.getRange?, task := promise.result }]
|
||||
}
|
||||
finished := .pure { state? := (← Tactic.saveState) }
|
||||
} #[{ 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.data.next.get? 0)⟩
|
||||
return ⟨old.data.stx, (← old.next.get? 0)⟩
|
||||
} }) do
|
||||
evalTactic stx'
|
||||
return
|
||||
|
||||
@@ -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.data.inner? |>.map (⟨oldParsed.data.stx, ·⟩)
|
||||
oldInner? := oldParsed.next.get? 0 |>.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,47 +73,29 @@ where
|
||||
if let some state := oldParsed.data.finished.get.state? then
|
||||
reusableResult? := some ((), state)
|
||||
-- only allow `next` reuse in this case
|
||||
oldNext? := oldParsed.data.next.get? 0 |>.map (⟨old.stx, ·⟩)
|
||||
oldNext? := oldParsed.next.get? 1 |>.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
|
||||
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 }
|
||||
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 }
|
||||
|
||||
withTheReader Term.Context ({ · with tacSnap? := some {
|
||||
new := next
|
||||
|
||||
@@ -263,10 +263,9 @@ where
|
||||
-- save all relevant syntax here for comparison with next document version
|
||||
stx := mkNullNode altStxs
|
||||
diagnostics := .empty
|
||||
finished := { range? := none, task := finished.result }
|
||||
next := altStxs.zipWith altPromises fun stx prom =>
|
||||
{ range? := stx.getRange?, task := prom.result }
|
||||
}
|
||||
finished := finished.result
|
||||
} (altStxs.zipWith altPromises fun stx prom =>
|
||||
{ range? := stx.getRange?, task := prom.result })
|
||||
goWithIncremental <| altPromises.mapIdx fun i prom => {
|
||||
old? := do
|
||||
let old ← tacSnap.old?
|
||||
@@ -275,10 +274,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.data.next[i]?)⟩
|
||||
return ⟨old.data.stx, (← old.next[i]?)⟩
|
||||
new := prom
|
||||
}
|
||||
finished.resolve { diagnostics := .empty, state? := (← saveState) }
|
||||
finished.resolve { state? := (← saveState) }
|
||||
return
|
||||
|
||||
goWithIncremental #[]
|
||||
|
||||
@@ -190,38 +190,33 @@ structure SavedState where
|
||||
term : Term.SavedState
|
||||
tactic : State
|
||||
|
||||
/-- Snapshot after finishing execution of a tactic. -/
|
||||
structure TacticFinishedSnapshot extends Language.Snapshot where
|
||||
/-- State saved for reuse, if no fatal exception occurred. -/
|
||||
/-- State after finishing execution of a tactic. -/
|
||||
structure TacticFinished where
|
||||
/-- Reusable state, if no fatal exception occurred. -/
|
||||
state? : Option SavedState
|
||||
deriving Inhabited
|
||||
instance : ToSnapshotTree TacticFinishedSnapshot where
|
||||
toSnapshotTree s := ⟨s.toSnapshot, #[]⟩
|
||||
|
||||
/-- Snapshot just before execution of a tactic. -/
|
||||
structure TacticParsedSnapshotData (TacticParsedSnapshot : Type) extends Language.Snapshot where
|
||||
structure TacticParsedSnapshotData extends Language.Snapshot where
|
||||
/-- Syntax tree of the tactic, stored and compared for incremental reuse. -/
|
||||
stx : Syntax
|
||||
/-- Task for nested incrementality, if enabled for tactic. -/
|
||||
inner? : Option (SnapshotTask TacticParsedSnapshot) := none
|
||||
/-- Task for state after tactic execution. -/
|
||||
finished : SnapshotTask TacticFinishedSnapshot
|
||||
/-- Tasks for subsequent, potentially parallel, tactic steps. -/
|
||||
next : Array (SnapshotTask TacticParsedSnapshot) := #[]
|
||||
finished : Task TacticFinished
|
||||
deriving Inhabited
|
||||
|
||||
/-- State after execution of a single synchronous tactic step. -/
|
||||
inductive TacticParsedSnapshot where
|
||||
| mk (data : TacticParsedSnapshotData TacticParsedSnapshot)
|
||||
| mk (data : TacticParsedSnapshotData) (next : Array (SnapshotTask TacticParsedSnapshot))
|
||||
deriving Inhabited
|
||||
abbrev TacticParsedSnapshot.data : TacticParsedSnapshot → TacticParsedSnapshotData TacticParsedSnapshot
|
||||
| .mk data => data
|
||||
abbrev TacticParsedSnapshot.data : TacticParsedSnapshot → TacticParsedSnapshotData
|
||||
| .mk data _ => data
|
||||
/-- Potential, potentially parallel, follow-up tactic executions. -/
|
||||
-- In the first, non-parallel version, each task will depend on its predecessor
|
||||
abbrev TacticParsedSnapshot.next : TacticParsedSnapshot → Array (SnapshotTask TacticParsedSnapshot)
|
||||
| .mk _ next => next
|
||||
partial instance : ToSnapshotTree TacticParsedSnapshot where
|
||||
toSnapshotTree := go where
|
||||
go := fun ⟨s⟩ => ⟨s.toSnapshot,
|
||||
s.inner?.toArray.map (·.map (sync := true) go) ++
|
||||
#[s.finished.map (sync := true) toSnapshotTree] ++
|
||||
s.next.map (·.map (sync := true) go)⟩
|
||||
go := fun ⟨s, next⟩ => ⟨s.toSnapshot, next.map (·.map (sync := true) go)⟩
|
||||
|
||||
end Snapshot
|
||||
end Tactic
|
||||
@@ -635,32 +630,6 @@ private def withoutModifyingStateWithInfoAndMessagesImpl (x : TermElabM α) : Te
|
||||
let saved := { saved with meta.core.infoState := (← getInfoState), meta.core.messages := (← getThe Core.State).messages }
|
||||
restoreState saved
|
||||
|
||||
/--
|
||||
Wraps the trees returned from `getInfoTrees`, if any, in an `InfoTree.context` node based on the
|
||||
current monadic context and state. This is mainly used to report info trees early via
|
||||
`Snapshot.infoTree?`. The trees are not removed from the `getInfoTrees` state as the final info tree
|
||||
of the elaborated command should be complete and not depend on whether parts have been reported
|
||||
early.
|
||||
|
||||
As `InfoTree.context` can have only one child, this function panics if `trees` contains more than 1
|
||||
tree. Also, `PartialContextInfo.parentDeclCtx` is not currently generated as that information is not
|
||||
available in the monadic context and only needed for the final info tree.
|
||||
-/
|
||||
def getInfoTreeWithContext? : TermElabM (Option InfoTree) := do
|
||||
let st ← getInfoState
|
||||
if st.trees.size > 1 then
|
||||
return panic! "getInfoTreeWithContext: overfull tree"
|
||||
let some t := st.trees[0]? |
|
||||
return none
|
||||
let t := t.substitute st.assignment
|
||||
let ctx ← readThe Core.Context
|
||||
let s ← getThe Core.State
|
||||
let ctx := PartialContextInfo.commandCtx {
|
||||
env := s.env, fileMap := ctx.fileMap, mctx := {}, currNamespace := ctx.currNamespace,
|
||||
openDecls := ctx.openDecls, options := ctx.options, ngen := s.ngen
|
||||
}
|
||||
return InfoTree.context ctx t
|
||||
|
||||
/-- For testing `TermElabM` methods. The #eval command will sign the error. -/
|
||||
def throwErrorIfErrors : TermElabM Unit := do
|
||||
if (← MonadLog.hasErrors) then
|
||||
|
||||
@@ -12,7 +12,6 @@ prelude
|
||||
import Init.System.Promise
|
||||
import Lean.Message
|
||||
import Lean.Parser.Types
|
||||
import Lean.Elab.InfoTree
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
@@ -47,8 +46,6 @@ def Snapshot.Diagnostics.empty : Snapshot.Diagnostics where
|
||||
The base class of all snapshots: all the generic information the language server needs about a
|
||||
snapshot. -/
|
||||
structure Snapshot where
|
||||
/-- Debug description shown by `trace.Elab.snapshotTree`, defaults to the caller's decl name. -/
|
||||
desc : String := by exact decl_name%.toString
|
||||
/--
|
||||
The messages produced by this step. The union of message logs of all finished snapshots is
|
||||
reported to the user. -/
|
||||
@@ -74,7 +71,7 @@ structure SnapshotTask (α : Type) where
|
||||
range? : Option String.Range
|
||||
/-- Underlying task producing the snapshot. -/
|
||||
task : Task α
|
||||
deriving Nonempty, Inhabited
|
||||
deriving Nonempty
|
||||
|
||||
/-- Creates a snapshot task from a reporting range and a `BaseIO` action. -/
|
||||
def SnapshotTask.ofIO (range? : Option String.Range) (act : BaseIO α) : BaseIO (SnapshotTask α) := do
|
||||
@@ -206,19 +203,15 @@ abbrev SnapshotTree.children : SnapshotTree → Array (SnapshotTask SnapshotTree
|
||||
| mk _ children => children
|
||||
|
||||
/-- Produces debug tree format of given snapshot tree, synchronously waiting on all children. -/
|
||||
partial def SnapshotTree.format [Monad m] [MonadFileMap m] [MonadLiftT IO m] :
|
||||
SnapshotTree → m Format :=
|
||||
go none
|
||||
where go range? s := do
|
||||
let file ← getFileMap
|
||||
let mut desc := f!"• {s.element.desc}"
|
||||
if let some range := range? then
|
||||
desc := desc ++ f!"{file.toPosition range.start}-{file.toPosition range.stop} "
|
||||
desc := desc ++ .prefixJoin "\n• " (← s.element.diagnostics.msgLog.toList.mapM (·.toString))
|
||||
if let some t := s.element.infoTree? then
|
||||
desc := desc ++ f!"\n{← t.format}"
|
||||
desc := desc ++ .prefixJoin "\n" (← s.children.toList.mapM fun c => go c.range? c.get)
|
||||
return .nestD desc
|
||||
partial def SnapshotTree.format : SnapshotTree → Format := go none
|
||||
where go range? s :=
|
||||
let range := match range? with
|
||||
| some range => f!"{range.start}..{range.stop} "
|
||||
| none => ""
|
||||
let element := f!"{s.element.diagnostics.msgLog.unreported.size} diagnostics"
|
||||
let children := Std.Format.prefixJoin .line <|
|
||||
s.children.toList.map fun c => go c.range? c.get
|
||||
.nestD f!"• {range}{element}{children}"
|
||||
|
||||
/--
|
||||
Helper class for projecting a heterogeneous hierarchy of snapshot classes to a homogeneous
|
||||
|
||||
@@ -362,16 +362,16 @@ where
|
||||
parseHeader (old? : Option HeaderParsedSnapshot) : LeanProcessingM HeaderParsedSnapshot := do
|
||||
let ctx ← read
|
||||
let ictx := ctx.toInputContext
|
||||
let unchanged old newStx newParserState :=
|
||||
let unchanged old newParserState :=
|
||||
-- when header syntax is unchanged, reuse import processing task as is and continue with
|
||||
-- parsing the first command, synchronously if possible
|
||||
-- NOTE: even if the syntax tree is functionally unchanged, its concrete structure and the new
|
||||
-- parser state may still have changed because of trailing whitespace and comments etc., so
|
||||
-- they are passed separately from `old`
|
||||
-- NOTE: even if the syntax tree is functionally unchanged, the new parser state may still
|
||||
-- have changed because of trailing whitespace and comments etc., so it is passed separately
|
||||
-- from `old`
|
||||
if let some oldSuccess := old.result? then
|
||||
return {
|
||||
ictx
|
||||
stx := newStx
|
||||
stx := old.stx
|
||||
diagnostics := old.diagnostics
|
||||
cancelTk? := ctx.newCancelTk
|
||||
result? := some { oldSuccess with
|
||||
@@ -394,7 +394,7 @@ where
|
||||
if let some nextCom ← processed.firstCmdSnap.get? then
|
||||
if (← isBeforeEditPos nextCom.data.parserState.pos) then
|
||||
-- ...go immediately to next snapshot
|
||||
return (← unchanged old old.stx oldSuccess.parserState)
|
||||
return (← unchanged old oldSuccess.parserState)
|
||||
|
||||
withHeaderExceptions ({ · with
|
||||
ictx, stx := .missing, result? := none, cancelTk? := none }) do
|
||||
@@ -408,19 +408,16 @@ where
|
||||
cancelTk? := none
|
||||
}
|
||||
|
||||
let trimmedStx := stx.unsetTrailing
|
||||
-- semi-fast path: go to next snapshot if syntax tree is unchanged
|
||||
-- NOTE: We compare modulo `unsetTrailing` in order to ensure that changes in trailing
|
||||
-- whitespace do not invalidate the header. This is safe because we only pass the trimmed
|
||||
-- syntax tree to `processHeader` below, so there cannot be any references to the trailing
|
||||
-- whitespace in its result. We still store the untrimmed syntax tree in the snapshot in order
|
||||
-- to uphold the invariant that concatenating all top-level snapshots' syntax trees results in
|
||||
-- the original file.
|
||||
-- semi-fast path: go to next snapshot if syntax tree is unchanged AND we're still in front
|
||||
-- of the edit location
|
||||
-- TODO: dropping the second condition would require adjusting positions in the state
|
||||
-- NOTE: as `parserState.pos` includes trailing whitespace, this forces reprocessing even if
|
||||
-- only that whitespace changes, which is wasteful but still necessary because it may
|
||||
-- influence the range of error messages such as from a trailing `exact`
|
||||
if let some old := old? then
|
||||
if trimmedStx.eqWithInfo old.stx.unsetTrailing then
|
||||
-- Here we must make sure to pass the *new* syntax and parser state; see NOTE in
|
||||
-- `unchanged`
|
||||
return (← unchanged old stx parserState)
|
||||
if (← isBeforeEditPos parserState.pos) && old.stx == stx then
|
||||
-- Here we must make sure to pass the *new* parser state; see NOTE in `unchanged`
|
||||
return (← unchanged old parserState)
|
||||
-- on first change, make sure to cancel old invocation
|
||||
if let some tk := ctx.oldCancelTk? then
|
||||
tk.set
|
||||
@@ -429,7 +426,7 @@ where
|
||||
diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
|
||||
result? := some {
|
||||
parserState
|
||||
processedSnap := (← processHeader trimmedStx parserState)
|
||||
processedSnap := (← processHeader stx parserState)
|
||||
}
|
||||
cancelTk? := ctx.newCancelTk
|
||||
}
|
||||
@@ -526,10 +523,7 @@ where
|
||||
|
||||
-- semi-fast path
|
||||
if let some old := old? then
|
||||
-- NOTE: as `parserState.pos` includes trailing whitespace, this forces reprocessing even if
|
||||
-- only that whitespace changes, which is wasteful but still necessary because it may
|
||||
-- influence the range of error messages such as from a trailing `exact`
|
||||
if stx.eqWithInfo old.data.stx then
|
||||
if (← isBeforeEditPos parserState.pos ctx) && old.data.stx == stx then
|
||||
-- Here we must make sure to pass the *new* parser state; see NOTE in `unchanged`
|
||||
return (← unchanged old parserState)
|
||||
-- on first change, make sure to cancel old invocation
|
||||
|
||||
@@ -665,6 +665,27 @@ def mkIffOfEq (h : Expr) : MetaM Expr := do
|
||||
else
|
||||
mkAppM ``Iff.of_eq #[h]
|
||||
|
||||
/--
|
||||
Given proofs of `P₁`, …, `Pₙ`, returns a proof of `P₁ ∧ … ∧ Pₙ`.
|
||||
If `n = 0` returns a proof of `True`.
|
||||
If `n = 1` returns the proof of `P₁`.
|
||||
-/
|
||||
def mkAndIntroN : Array Expr → MetaM Expr
|
||||
| #[] => return mkConst ``True.intro []
|
||||
| #[e] => return e
|
||||
| es => es.foldrM (start := es.size - 1) (fun a b => mkAppM ``And.intro #[a,b]) es.back
|
||||
|
||||
|
||||
/-- Given a proof of `P₁ ∧ … ∧ Pᵢ ∧ … ∧ Pₙ`, return the proof of `Pᵢ` -/
|
||||
def mkProjAndN (n i : Nat) (e : Expr) : Expr := Id.run do
|
||||
let mut value := e
|
||||
for _ in [:i] do
|
||||
value := mkProj ``And 1 value
|
||||
if i + 1 < n then
|
||||
value := mkProj ``And 0 value
|
||||
return value
|
||||
|
||||
|
||||
builtin_initialize do
|
||||
registerTraceClass `Meta.appBuilder
|
||||
registerTraceClass `Meta.appBuilder.result (inherited := true)
|
||||
|
||||
@@ -6,7 +6,6 @@ Authors: Joachim Breitner
|
||||
|
||||
prelude
|
||||
import Lean.Meta.AppBuilder
|
||||
import Lean.Meta.PProdN
|
||||
import Lean.Meta.ArgsPacker.Basic
|
||||
|
||||
/-!
|
||||
@@ -519,7 +518,7 @@ def curry (argsPacker : ArgsPacker) (e : Expr) : MetaM Expr := do
|
||||
let mut es := #[]
|
||||
for i in [:argsPacker.numFuncs] do
|
||||
es := es.push (← argsPacker.curryProj e i)
|
||||
PProdN.mk 0 es
|
||||
mkAndIntroN es
|
||||
|
||||
/--
|
||||
Given type `(a ⊗' b ⊕' c ⊗' d) → e`, brings `a → b → e` and `c → d → e`
|
||||
@@ -534,7 +533,7 @@ where
|
||||
| [], acc => k acc
|
||||
| t::ts, acc => do
|
||||
let name := if argsPacker.numFuncs = 1 then name else .mkSimple s!"{name}{acc.size+1}"
|
||||
withLocalDeclD name t fun x => do
|
||||
withLocalDecl name .default t fun x => do
|
||||
go ts (acc.push x)
|
||||
|
||||
/--
|
||||
|
||||
@@ -8,18 +8,67 @@ import Lean.Meta.InferType
|
||||
import Lean.AuxRecursor
|
||||
import Lean.AddDecl
|
||||
import Lean.Meta.CompletionName
|
||||
import Lean.Meta.PProdN
|
||||
|
||||
namespace Lean
|
||||
open Meta
|
||||
|
||||
/-- Transforms `e : xᵢ → (t₁ ×' t₂)` into `(xᵢ → t₁) ×' (xᵢ → t₂) -/
|
||||
private def etaPProd (xs : Array Expr) (e : Expr) : MetaM Expr := do
|
||||
if xs.isEmpty then return e
|
||||
let r := mkAppN e xs
|
||||
let r₁ ← mkLambdaFVars xs (← mkPProdFst r)
|
||||
let r₂ ← mkLambdaFVars xs (← mkPProdSnd r)
|
||||
mkPProdMk r₁ r₂
|
||||
section PProd
|
||||
|
||||
/--!
|
||||
Helpers to construct types and values of `PProd` and project out of them, set up to use `And`
|
||||
instead of `PProd` if the universes allow. Maybe be extracted into a Utils module when useful
|
||||
elsewhere.
|
||||
-/
|
||||
|
||||
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}"
|
||||
|
||||
end PProd
|
||||
|
||||
/--
|
||||
If `minorType` is the type of a minor premies of a recursor, such as
|
||||
@@ -42,7 +91,7 @@ private def buildBelowMinorPremise (rlvl : Level) (motives : Array Expr) (minorT
|
||||
where
|
||||
ibelow := rlvl matches .zero
|
||||
go (prods : Array Expr) : List Expr → MetaM Expr
|
||||
| [] => PProdN.pack rlvl prods
|
||||
| [] => mkNProd rlvl prods
|
||||
| arg::args => do
|
||||
let argType ← inferType arg
|
||||
forallTelescope argType fun arg_args arg_type => do
|
||||
@@ -194,7 +243,7 @@ private def buildBRecOnMinorPremise (rlvl : Level) (motives : Array Expr)
|
||||
forallTelescope minorType fun minor_args minor_type => do
|
||||
let rec go (prods : Array Expr) : List Expr → MetaM Expr
|
||||
| [] => minor_type.withApp fun minor_type_fn minor_type_args => do
|
||||
let b ← PProdN.mk rlvl prods
|
||||
let b ← mkNProdMk rlvl prods
|
||||
let .some ⟨idx, _⟩ := motives.indexOf? minor_type_fn
|
||||
| throwError m!"Did not find {minor_type} in {motives}"
|
||||
mkPProdMk (mkAppN fs[idx]! (minor_type_args.push b)) b
|
||||
@@ -207,8 +256,14 @@ private def buildBRecOnMinorPremise (rlvl : Level) (motives : Array Expr)
|
||||
let type' ← mkForallFVars arg_args
|
||||
(← mkPProd arg_type (mkAppN belows[idx]! arg_type_args) )
|
||||
withLocalDeclD name type' fun arg' => do
|
||||
let r ← etaPProd arg_args arg'
|
||||
mkLambdaFVars #[arg'] (← go (prods.push r) args)
|
||||
if arg_args.isEmpty then
|
||||
mkLambdaFVars #[arg'] (← go (prods.push arg') args)
|
||||
else
|
||||
let r := mkAppN arg' arg_args
|
||||
let r₁ ← mkLambdaFVars arg_args (← mkPProdFst r)
|
||||
let r₂ ← mkLambdaFVars arg_args (← mkPProdSnd r)
|
||||
let r ← mkPProdMk r₁ r₂
|
||||
mkLambdaFVars #[arg'] (← go (prods.push r) args)
|
||||
else
|
||||
mkLambdaFVars #[arg] (← go prods args)
|
||||
go #[] minor_args.toList
|
||||
|
||||
@@ -6,7 +6,6 @@ Authors: Dany Fabian
|
||||
prelude
|
||||
import Lean.Meta.Constructions.CasesOn
|
||||
import Lean.Meta.Match.Match
|
||||
import Lean.Meta.Tactic.SolveByElim
|
||||
|
||||
namespace Lean.Meta.IndPredBelow
|
||||
open Match
|
||||
@@ -231,28 +230,22 @@ def mkBelowDecl (ctx : Context) : MetaM Declaration := do
|
||||
ctx.typeInfos[0]!.isUnsafe
|
||||
|
||||
partial def backwardsChaining (m : MVarId) (depth : Nat) : MetaM Bool := do
|
||||
m.withContext do
|
||||
if depth = 0 then return false
|
||||
else
|
||||
m.withContext do
|
||||
let lctx ← getLCtx
|
||||
let mTy ← m.getType
|
||||
if depth = 0 then
|
||||
trace[Meta.IndPredBelow.search] "searching for {mTy}: ran out of max depth"
|
||||
return false
|
||||
else
|
||||
let lctx ← getLCtx
|
||||
let r ← lctx.anyM fun localDecl =>
|
||||
if localDecl.isAuxDecl then
|
||||
return false
|
||||
else
|
||||
commitWhen do
|
||||
let (mvars, _, t) ← forallMetaTelescope localDecl.type
|
||||
if (← isDefEq mTy t) then
|
||||
trace[Meta.IndPredBelow.search] "searching for {mTy}: trying {mkFVar localDecl.fvarId} : {localDecl.type}"
|
||||
m.assign (mkAppN localDecl.toExpr mvars)
|
||||
mvars.allM fun v =>
|
||||
v.mvarId!.isAssigned <||> backwardsChaining v.mvarId! (depth - 1)
|
||||
else return false
|
||||
unless r do
|
||||
trace[Meta.IndPredBelow.search] "searching for {mTy} failed"
|
||||
return r
|
||||
lctx.anyM fun localDecl =>
|
||||
if localDecl.isAuxDecl then
|
||||
return false
|
||||
else
|
||||
commitWhen do
|
||||
let (mvars, _, t) ← forallMetaTelescope localDecl.type
|
||||
if ←isDefEq mTy t then
|
||||
m.assign (mkAppN localDecl.toExpr mvars)
|
||||
mvars.allM fun v =>
|
||||
v.mvarId!.isAssigned <||> backwardsChaining v.mvarId! (depth - 1)
|
||||
else return false
|
||||
|
||||
partial def proveBrecOn (ctx : Context) (indVal : InductiveVal) (type : Expr) : MetaM Expr := do
|
||||
let main ← mkFreshExprSyntheticOpaqueMVar type
|
||||
@@ -570,7 +563,7 @@ def findBelowIdx (xs : Array Expr) (motive : Expr) : MetaM $ Option (Expr × Nat
|
||||
let below ← mkFreshExprSyntheticOpaqueMVar belowTy
|
||||
try
|
||||
trace[Meta.IndPredBelow.match] "{←Meta.ppGoal below.mvarId!}"
|
||||
if (← below.mvarId!.applyRules { backtracking := false, maxDepth := 1 } []).isEmpty then
|
||||
if (← backwardsChaining below.mvarId! 10) then
|
||||
trace[Meta.IndPredBelow.match] "Found below term in the local context: {below}"
|
||||
if (← xs.anyM (isDefEq below)) then pure none else pure (below, idx.val)
|
||||
else
|
||||
@@ -603,6 +596,5 @@ def mkBelow (declName : Name) : MetaM Unit := do
|
||||
builtin_initialize
|
||||
registerTraceClass `Meta.IndPredBelow
|
||||
registerTraceClass `Meta.IndPredBelow.match
|
||||
registerTraceClass `Meta.IndPredBelow.search
|
||||
|
||||
end Lean.Meta.IndPredBelow
|
||||
|
||||
@@ -1,145 +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 provides functios to pack and unpack values using nested `PProd` or `And`,
|
||||
as used in the `.below` construction, in the `.brecOn` construction for mutual recursion and
|
||||
and the `mutual_induct` construction.
|
||||
|
||||
It uses `And` (equivalent to `PProd.{0}` when possible).
|
||||
|
||||
The nesting is `t₁ ×' (t₂ ×' t₃)`, not `t₁ ×' (t₂ ×' (t₃ ×' PUnit))`. This is more readable,
|
||||
slightly shorter, and means that the packing is the identity if `n=1`, which we rely on in some
|
||||
places. It comes at the expense that hat projection needs to know `n`.
|
||||
|
||||
Packing an empty list uses `True` or `PUnit` depending on the given `lvl`.
|
||||
|
||||
Also see `Lean.Meta.ArgsPacker` for a similar module for `PSigma` and `PSum`, used by well-founded recursion.
|
||||
-/
|
||||
|
||||
|
||||
namespace Lean.Meta
|
||||
|
||||
/-- Given types `t₁` and `t₂`, produces `t₁ ×' t₂` (or `t₁ ∧ t₂` if the universes allow) -/
|
||||
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
|
||||
|
||||
/-- Given values of typs `t₁` and `t₂`, produces value of type `t₁ ×' t₂` (or `t₁ ∧ t₂` if the universes allow) -/
|
||||
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
|
||||
|
||||
/-- `PProd.fst` or `And.left` (using `.proj`) -/
|
||||
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
|
||||
| _ => panic! "mkPProdFst: cannot handle{indentExpr e}\nof type{indentExpr t}"
|
||||
|
||||
/-- `PProd.snd` or `And.right` (using `.proj`) -/
|
||||
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
|
||||
| _ => panic! "mkPProdSnd: cannot handle{indentExpr e}\nof type{indentExpr t}"
|
||||
|
||||
|
||||
|
||||
namespace PProdN
|
||||
|
||||
/-- Given types `tᵢ`, produces `t₁ ×' t₂ ×' t₃` -/
|
||||
def pack (lvl : Level) (xs : Array Expr) : MetaM Expr := do
|
||||
if xs.size = 0 then
|
||||
if lvl matches .zero then return .const ``True []
|
||||
else return .const ``PUnit [lvl]
|
||||
let xBack := xs.back
|
||||
xs.pop.foldrM mkPProd xBack
|
||||
|
||||
/-- Given values `xᵢ` of type `tᵢ`, produces value of type `t₁ ×' t₂ ×' t₃` -/
|
||||
def mk (lvl : Level) (xs : Array Expr) : MetaM Expr := do
|
||||
if xs.size = 0 then
|
||||
if lvl matches .zero then return .const ``True.intro []
|
||||
else return .const ``PUnit.unit [lvl]
|
||||
let xBack := xs.back
|
||||
xs.pop.foldrM mkPProdMk xBack
|
||||
|
||||
/-- Given a value of type `t₁ ×' … ×' tᵢ ×' … ×' tₙ`, return a value of type `tᵢ` -/
|
||||
def proj (n i : Nat) (e : Expr) : MetaM Expr := do
|
||||
let mut value := e
|
||||
for _ in [:i] do
|
||||
value ← mkPProdSnd value
|
||||
if i+1 < n then
|
||||
mkPProdFst value
|
||||
else
|
||||
pure value
|
||||
|
||||
|
||||
|
||||
/--
|
||||
Packs multiple type-forming lambda expressions taking the same parameters using `PProd`.
|
||||
|
||||
The parameter `type` is the common type of the these expressions
|
||||
|
||||
For example
|
||||
```
|
||||
packLambdas (Nat → Sort u) #[(fun (n : Nat) => Nat), (fun (n : Nat) => Fin n -> Fin n )]
|
||||
```
|
||||
will return
|
||||
```
|
||||
fun (n : Nat) => (Nat ×' (Fin n → Fin n))
|
||||
```
|
||||
|
||||
It is the identity if `es.size = 1`.
|
||||
|
||||
It returns a dummy motive `(xs : ) → PUnit` or `(xs : … ) → True` if no expressions are given.
|
||||
(this is the reason we need the expected type in the `type` parameter).
|
||||
|
||||
-/
|
||||
def packLambdas (type : Expr) (es : Array Expr) : MetaM Expr := do
|
||||
if es.size = 1 then
|
||||
return es[0]!
|
||||
forallTelescope type fun xs sort => do
|
||||
assert! sort.isSort
|
||||
-- NB: Use beta, not instantiateLambda; when constructing the belowDict below
|
||||
-- we pass `C`, a plain FVar, here
|
||||
let es' := es.map (·.beta xs)
|
||||
let packed ← PProdN.pack sort.sortLevel! es'
|
||||
mkLambdaFVars xs packed
|
||||
|
||||
/--
|
||||
The value analogue to `PProdN.packLambdas`.
|
||||
|
||||
It is the identity if `es.size = 1`.
|
||||
-/
|
||||
def mkLambdas (type : Expr) (es : Array Expr) : MetaM Expr := do
|
||||
if es.size = 1 then
|
||||
return es[0]!
|
||||
forallTelescope type fun xs body => do
|
||||
let lvl ← getLevel body
|
||||
let es' := es.map (·.beta xs)
|
||||
let packed ← PProdN.mk lvl es'
|
||||
mkLambdaFVars xs packed
|
||||
|
||||
|
||||
end PProdN
|
||||
|
||||
end Lean.Meta
|
||||
File diff suppressed because it is too large
Load Diff
@@ -269,7 +269,7 @@ corresponding `end <id>` or the end of the file.
|
||||
"namespace " >> checkColGt >> ident
|
||||
/--
|
||||
`end` closes a `section` or `namespace` scope. If the scope is named `<id>`, it has to be closed
|
||||
with `end <id>`. The `end` command is optional at the end of a file.
|
||||
with `end <id>`.
|
||||
-/
|
||||
@[builtin_command_parser] def «end» := leading_parser
|
||||
"end" >> optional (ppSpace >> checkColGt >> ident)
|
||||
@@ -437,8 +437,6 @@ structure Pair (α : Type u) (β : Type v) : Type (max u v) where
|
||||
"#check_failure " >> termParser -- Like `#check`, but succeeds only if term does not type check
|
||||
@[builtin_command_parser] def eval := leading_parser
|
||||
"#eval " >> termParser
|
||||
@[builtin_command_parser] def evalBang := leading_parser
|
||||
"#eval! " >> termParser
|
||||
@[builtin_command_parser] def synth := leading_parser
|
||||
"#synth " >> termParser
|
||||
@[builtin_command_parser] def exit := leading_parser
|
||||
|
||||
@@ -350,17 +350,15 @@ def term.parenthesizer : CategoryParenthesizer | prec => do
|
||||
maybeParenthesize `term true wrapParens prec $
|
||||
parenthesizeCategoryCore `term prec
|
||||
where
|
||||
/-- Wraps the term `stx` in parentheses and then moves its `SourceInfo` to the result.
|
||||
The purpose of this is to move synthetic delaborator positions from the `stx` node to the parentheses node,
|
||||
which causes the info view to view the node with parentheses as referring to the parenthesized expression.
|
||||
If we did not move info, the info view would consider the parentheses to belong to the outer term.
|
||||
/-- Wraps the term `stx` in parentheses and then copies its `SourceInfo` to the result.
|
||||
The purpose of this is to copy synthetic delaborator positions from the `stx` node to the parentheses node,
|
||||
which causes the info view to view both of these nodes as referring to the same expression.
|
||||
If we did not copy info, the info view would consider the parentheses to belong to the outer term.
|
||||
Note: we do not do `withRef stx` because that causes the "(" and ")" tokens to have source info as well,
|
||||
causing the info view to highlight each parenthesis as an independent expression. -/
|
||||
wrapParens (stx : Syntax) : Syntax := Unhygienic.run do
|
||||
let stxInfo := SourceInfo.fromRef stx
|
||||
let stx := stx.setInfo .none
|
||||
let pstx ← `(($(⟨stx⟩)))
|
||||
return pstx.raw.setInfo stxInfo
|
||||
return pstx.raw.setInfo (SourceInfo.fromRef stx)
|
||||
|
||||
@[builtin_category_parenthesizer tactic]
|
||||
def tactic.parenthesizer : CategoryParenthesizer | prec => do
|
||||
|
||||
@@ -234,27 +234,31 @@ def getInteractiveGoals (p : Lsp.PlainGoalParams) : RequestM (RequestTask (Optio
|
||||
let doc ← readDoc
|
||||
let text := doc.meta.text
|
||||
let hoverPos := text.lspPosToUtf8Pos p.position
|
||||
mapTask (findInfoTreeAtPos doc hoverPos) <| Option.bindM fun infoTree => do
|
||||
let rs@(_ :: _) := infoTree.goalsAt? doc.meta.text hoverPos
|
||||
| return none
|
||||
let goals : List Widget.InteractiveGoals ← rs.mapM fun { ctxInfo := ci, tacticInfo := ti, useAfter := useAfter, .. } => do
|
||||
let ciAfter := { ci with mctx := ti.mctxAfter }
|
||||
let ci := if useAfter then ciAfter else { ci with mctx := ti.mctxBefore }
|
||||
-- compute the interactive goals
|
||||
let goals ← ci.runMetaM {} (do
|
||||
let goals := List.toArray <| if useAfter then ti.goalsAfter else ti.goalsBefore
|
||||
let goals ← goals.mapM Widget.goalToInteractive
|
||||
return {goals}
|
||||
)
|
||||
-- compute the goal diff
|
||||
ciAfter.runMetaM {} (do
|
||||
try
|
||||
Widget.diffInteractiveGoals useAfter ti goals
|
||||
catch _ =>
|
||||
-- fail silently, since this is just a bonus feature
|
||||
return goals
|
||||
)
|
||||
return some <| goals.foldl (· ++ ·) ∅
|
||||
-- NOTE: use `>=` since the cursor can be *after* the input
|
||||
withWaitFindSnap doc (fun s => s.endPos >= hoverPos)
|
||||
(notFoundX := return none) fun snap => do
|
||||
if let rs@(_ :: _) := snap.infoTree.goalsAt? doc.meta.text hoverPos then
|
||||
let goals : List Widget.InteractiveGoals ← rs.mapM fun { ctxInfo := ci, tacticInfo := ti, useAfter := useAfter, .. } => do
|
||||
let ciAfter := { ci with mctx := ti.mctxAfter }
|
||||
let ci := if useAfter then ciAfter else { ci with mctx := ti.mctxBefore }
|
||||
-- compute the interactive goals
|
||||
let goals ← ci.runMetaM {} (do
|
||||
let goals := List.toArray <| if useAfter then ti.goalsAfter else ti.goalsBefore
|
||||
let goals ← goals.mapM Widget.goalToInteractive
|
||||
return {goals}
|
||||
)
|
||||
-- compute the goal diff
|
||||
let goals ← ciAfter.runMetaM {} (do
|
||||
try
|
||||
Widget.diffInteractiveGoals useAfter ti goals
|
||||
catch _ =>
|
||||
-- fail silently, since this is just a bonus feature
|
||||
return goals
|
||||
)
|
||||
return goals
|
||||
return some <| goals.foldl (· ++ ·) ∅
|
||||
else
|
||||
return none
|
||||
|
||||
open Elab in
|
||||
def handlePlainGoal (p : PlainGoalParams)
|
||||
@@ -276,17 +280,19 @@ def getInteractiveTermGoal (p : Lsp.PlainTermGoalParams)
|
||||
let doc ← readDoc
|
||||
let text := doc.meta.text
|
||||
let hoverPos := text.lspPosToUtf8Pos p.position
|
||||
mapTask (findInfoTreeAtPos doc hoverPos) <| Option.bindM fun infoTree => do
|
||||
let some {ctx := ci, info := i@(Elab.Info.ofTermInfo ti), ..} := infoTree.termGoalAt? hoverPos
|
||||
| return none
|
||||
let ty ← ci.runMetaM i.lctx do
|
||||
instantiateMVars <| ti.expectedType?.getD (← Meta.inferType ti.expr)
|
||||
-- for binders, hide the last hypothesis (the binder itself)
|
||||
let lctx' := if ti.isBinder then i.lctx.pop else i.lctx
|
||||
let goal ← ci.runMetaM lctx' do
|
||||
Widget.goalToInteractive (← Meta.mkFreshExprMVar ty).mvarId!
|
||||
let range := if let some r := i.range? then r.toLspRange text else ⟨p.position, p.position⟩
|
||||
return some { goal with range, term := ⟨ti⟩ }
|
||||
withWaitFindSnap doc (fun s => s.endPos > hoverPos)
|
||||
(notFoundX := pure none) fun snap => do
|
||||
if let some {ctx := ci, info := i@(Elab.Info.ofTermInfo ti), ..} := snap.infoTree.termGoalAt? hoverPos then
|
||||
let ty ← ci.runMetaM i.lctx do
|
||||
instantiateMVars <| ti.expectedType?.getD (← Meta.inferType ti.expr)
|
||||
-- for binders, hide the last hypothesis (the binder itself)
|
||||
let lctx' := if ti.isBinder then i.lctx.pop else i.lctx
|
||||
let goal ← ci.runMetaM lctx' do
|
||||
Widget.goalToInteractive (← Meta.mkFreshExprMVar ty).mvarId!
|
||||
let range := if let some r := i.range? then r.toLspRange text else ⟨p.position, p.position⟩
|
||||
return some { goal with range, term := ⟨ti⟩ }
|
||||
else
|
||||
return none
|
||||
|
||||
def handlePlainTermGoal (p : PlainTermGoalParams)
|
||||
: RequestM (RequestTask (Option PlainTermGoal)) := do
|
||||
|
||||
@@ -37,8 +37,6 @@ def moduleFromDocumentUri (srcSearchPath : SearchPath) (uri : DocumentUri)
|
||||
open Elab in
|
||||
def locationLinksFromDecl (srcSearchPath : SearchPath) (uri : DocumentUri) (n : Name)
|
||||
(originRange? : Option Range) : MetaM (Array LocationLink) := do
|
||||
-- Potentially this name is a builtin that has not been imported yet:
|
||||
unless (← getEnv).contains n do return #[]
|
||||
let mod? ← findModuleOf? n
|
||||
let modUri? ← match mod? with
|
||||
| some modName => documentUriFromModule srcSearchPath modName
|
||||
|
||||
@@ -16,32 +16,6 @@ import Lean.Server.FileWorker.Utils
|
||||
|
||||
import Lean.Server.Rpc.Basic
|
||||
|
||||
namespace Lean.Language
|
||||
|
||||
/--
|
||||
Finds the first (in pre-order) snapshot task in `tree` whose `range?` contains `pos` and which
|
||||
contains an info tree, and then returns that info tree, waiting for any snapshot tasks on the way.
|
||||
Subtrees that do not contain the position are skipped without forcing their tasks.
|
||||
-/
|
||||
partial def SnapshotTree.findInfoTreeAtPos (tree : SnapshotTree) (pos : String.Pos) :
|
||||
Task (Option Elab.InfoTree) :=
|
||||
goSeq tree.children.toList
|
||||
where
|
||||
goSeq
|
||||
| [] => .pure none
|
||||
| t::ts =>
|
||||
if t.range?.any (·.contains pos) then
|
||||
t.task.bind (sync := true) fun tree => Id.run do
|
||||
if let some infoTree := tree.element.infoTree? then
|
||||
return .pure infoTree
|
||||
tree.findInfoTreeAtPos pos |>.bind (sync := true) fun
|
||||
| some infoTree => .pure (some infoTree)
|
||||
| none => goSeq ts
|
||||
else
|
||||
goSeq ts
|
||||
|
||||
end Lean.Language
|
||||
|
||||
namespace Lean.Server
|
||||
|
||||
structure RequestError where
|
||||
@@ -170,45 +144,6 @@ def withWaitFindSnapAtPos
|
||||
(notFoundX := throw ⟨.invalidParams, s!"no snapshot found at {lspPos}"⟩)
|
||||
(x := f)
|
||||
|
||||
open Language.Lean in
|
||||
/-- Finds the first `CommandParsedSnapshot` fulfilling `p`, asynchronously. -/
|
||||
partial def findCmdParsedSnap (doc : EditableDocument) (p : CommandParsedSnapshot → Bool) :
|
||||
Task (Option CommandParsedSnapshot) := Id.run do
|
||||
let some headerParsed := doc.initSnap.result?
|
||||
| .pure none
|
||||
headerParsed.processedSnap.task.bind (sync := true) fun headerProcessed => Id.run do
|
||||
let some headerSuccess := headerProcessed.result?
|
||||
| return .pure none
|
||||
headerSuccess.firstCmdSnap.task.bind (sync := true) go
|
||||
where
|
||||
go cmdParsed :=
|
||||
if p cmdParsed then
|
||||
.pure (some cmdParsed)
|
||||
else
|
||||
match cmdParsed.nextCmdSnap? with
|
||||
| some next => next.task.bind (sync := true) go
|
||||
| none => .pure none
|
||||
|
||||
open Language in
|
||||
/--
|
||||
Finds the info tree of the first snapshot task containing `pos`, asynchronously. The info tree may
|
||||
be from a nested snapshot, such as a single tactic.
|
||||
|
||||
See `SnapshotTree.findInfoTreeAtPos` for details on how the search is done.
|
||||
-/
|
||||
partial def findInfoTreeAtPos (doc : EditableDocument) (pos : String.Pos) :
|
||||
Task (Option Elab.InfoTree) :=
|
||||
-- NOTE: use `>=` since the cursor can be *after* the input (and there is no interesting info on
|
||||
-- the first character of the subsequent command if any)
|
||||
findCmdParsedSnap doc (·.data.parserState.pos ≥ pos) |>.bind (sync := true) fun
|
||||
| some cmdParsed => toSnapshotTree cmdParsed |>.findInfoTreeAtPos pos |>.bind (sync := true) fun
|
||||
| some infoTree => .pure <| some infoTree
|
||||
| none => cmdParsed.data.finishedSnap.task.map (sync := true) fun s =>
|
||||
-- the parser returns exactly one command per snapshot, and the elaborator creates exactly one node per command
|
||||
assert! s.cmdState.infoState.trees.size == 1
|
||||
some s.cmdState.infoState.trees[0]!
|
||||
| none => .pure none
|
||||
|
||||
open Elab.Command in
|
||||
def runCommandElabM (snap : Snapshot) (c : RequestT CommandElabM α) : RequestM α := do
|
||||
let rc ← readThe RequestContext
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.MonadEnv
|
||||
import Lean.Util.FoldConsts
|
||||
|
||||
namespace Lean
|
||||
|
||||
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
|
||||
|
||||
def collectAxioms [Monad m] [MonadEnv m] (constName : Name) : m (Array Name) := do
|
||||
let env ← getEnv
|
||||
let (_, s) := ((CollectAxioms.collect constName).run env).run {}
|
||||
pure s.axioms
|
||||
|
||||
end Lean
|
||||
@@ -9,11 +9,48 @@ import Lean.Util.PtrSet
|
||||
|
||||
namespace Lean
|
||||
namespace Expr
|
||||
namespace FindImpl
|
||||
|
||||
@[extern "lean_find_expr"]
|
||||
opaque findImpl? (p : @& (Expr → Bool)) (e : @& Expr) : Option Expr
|
||||
unsafe abbrev FindM := StateT (PtrSet Expr) Id
|
||||
|
||||
@[inline] def find? (p : Expr → Bool) (e : Expr) : Option Expr := findImpl? p e
|
||||
@[inline] unsafe def checkVisited (e : Expr) : OptionT FindM Unit := do
|
||||
if (← get).contains e then
|
||||
failure
|
||||
modify fun s => s.insert e
|
||||
|
||||
unsafe def findM? (p : Expr → Bool) (e : Expr) : OptionT FindM Expr :=
|
||||
let rec visit (e : Expr) := do
|
||||
checkVisited e
|
||||
if p e then
|
||||
pure e
|
||||
else match e with
|
||||
| .forallE _ d b _ => visit d <|> visit b
|
||||
| .lam _ d b _ => visit d <|> visit b
|
||||
| .mdata _ b => visit b
|
||||
| .letE _ t v b _ => visit t <|> visit v <|> visit b
|
||||
| .app f a => visit f <|> visit a
|
||||
| .proj _ _ b => visit b
|
||||
| _ => failure
|
||||
visit e
|
||||
|
||||
unsafe def findUnsafe? (p : Expr → Bool) (e : Expr) : Option Expr :=
|
||||
Id.run <| findM? p e |>.run' mkPtrSet
|
||||
|
||||
end FindImpl
|
||||
|
||||
@[implemented_by FindImpl.findUnsafe?]
|
||||
def find? (p : Expr → Bool) (e : Expr) : Option Expr :=
|
||||
/- This is a reference implementation for the unsafe one above -/
|
||||
if p e then
|
||||
some e
|
||||
else match e with
|
||||
| .forallE _ d b _ => find? p d <|> find? p b
|
||||
| .lam _ d b _ => find? p d <|> find? p b
|
||||
| .mdata _ b => find? p b
|
||||
| .letE _ t v b _ => find? p t <|> find? p v <|> find? p b
|
||||
| .app f a => find? p f <|> find? p a
|
||||
| .proj _ _ b => find? p b
|
||||
| _ => none
|
||||
|
||||
/-- Return true if `e` occurs in `t` -/
|
||||
def occurs (e : Expr) (t : Expr) : Bool :=
|
||||
@@ -27,13 +64,41 @@ inductive FindStep where
|
||||
/-- Search subterms -/ | visit
|
||||
/-- Do not search subterms -/ | done
|
||||
|
||||
@[extern "lean_find_ext_expr"]
|
||||
opaque findExtImpl? (p : @& (Expr → FindStep)) (e : @& Expr) : Option Expr
|
||||
namespace FindExtImpl
|
||||
|
||||
unsafe def findM? (p : Expr → FindStep) (e : Expr) : OptionT FindImpl.FindM Expr :=
|
||||
visit e
|
||||
where
|
||||
visitApp (e : Expr) :=
|
||||
match e with
|
||||
| .app f a .. => visitApp f <|> visit a
|
||||
| e => visit e
|
||||
|
||||
visit (e : Expr) := do
|
||||
FindImpl.checkVisited e
|
||||
match p e with
|
||||
| .done => failure
|
||||
| .found => pure e
|
||||
| .visit =>
|
||||
match e with
|
||||
| .forallE _ d b _ => visit d <|> visit b
|
||||
| .lam _ d b _ => visit d <|> visit b
|
||||
| .mdata _ b => visit b
|
||||
| .letE _ t v b _ => visit t <|> visit v <|> visit b
|
||||
| .app .. => visitApp e
|
||||
| .proj _ _ b => visit b
|
||||
| _ => failure
|
||||
|
||||
unsafe def findUnsafe? (p : Expr → FindStep) (e : Expr) : Option Expr :=
|
||||
Id.run <| findM? p e |>.run' mkPtrSet
|
||||
|
||||
end FindExtImpl
|
||||
|
||||
/--
|
||||
Similar to `find?`, but `p` can return `FindStep.done` to interrupt the search on subterms.
|
||||
Remark: Differently from `find?`, we do not invoke `p` for partial applications of an application. -/
|
||||
@[inline] def findExt? (p : Expr → FindStep) (e : Expr) : Option Expr := findExtImpl? p e
|
||||
@[implemented_by FindExtImpl.findUnsafe?]
|
||||
opaque findExt? (p : Expr → FindStep) (e : Expr) : Option Expr
|
||||
|
||||
end Expr
|
||||
end Lean
|
||||
|
||||
@@ -113,7 +113,7 @@ partial def findOLean (mod : Name) : IO FilePath := do
|
||||
let pkg := FilePath.mk <| mod.getRoot.toString (escape := false)
|
||||
let mut msg := s!"unknown module prefix '{pkg}'
|
||||
|
||||
No directory '{pkg}' or file '{pkg}.olean' in the search path entries:
|
||||
No directory '{pkg}' or file '{pkg}.lean' in the search path entries:
|
||||
{"\n".intercalate <| sp.map (·.toString)}"
|
||||
throw <| IO.userError msg
|
||||
|
||||
|
||||
@@ -7,13 +7,62 @@ prelude
|
||||
import Lean.Expr
|
||||
import Lean.Util.PtrSet
|
||||
|
||||
namespace Lean.Expr
|
||||
namespace Lean
|
||||
namespace Expr
|
||||
|
||||
@[extern "lean_replace_expr"]
|
||||
opaque replaceImpl (f? : @& (Expr → Option Expr)) (e : @& Expr) : Expr
|
||||
namespace ReplaceImpl
|
||||
|
||||
@[inline] def replace (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
replaceImpl f? e
|
||||
unsafe abbrev ReplaceM := StateM (PtrMap Expr Expr)
|
||||
|
||||
unsafe def cache (key : Expr) (exclusive : Bool) (result : Expr) : ReplaceM Expr := do
|
||||
unless exclusive do
|
||||
modify (·.insert key result)
|
||||
pure result
|
||||
|
||||
@[specialize]
|
||||
unsafe def replaceUnsafeM (f? : Expr → Option Expr) (e : Expr) : ReplaceM Expr := do
|
||||
let rec @[specialize] visit (e : Expr) := do
|
||||
/-
|
||||
TODO: We need better control over RC operations to ensure
|
||||
the following (unsafe) optimization is correctly applied.
|
||||
Optimization goal: only cache results for shared objects.
|
||||
|
||||
The main problem is that the current code generator ignores borrow annotations
|
||||
for code written in Lean. These annotations are only taken into account for extern functions.
|
||||
|
||||
Moveover, the borrow inference heuristic currently tags `e` as "owned" since it may be stored
|
||||
in the cache and is used in "update" functions.
|
||||
Thus, when visiting `e` sub-expressions the code generator increases their RC
|
||||
because we are recursively invoking `visit` :(
|
||||
|
||||
Thus, to fix this issue, we must
|
||||
1- Take borrow annotations into account for code written in Lean.
|
||||
2- Mark `e` is borrowed (i.e., `(e : @& Expr)`)
|
||||
-/
|
||||
let excl := isExclusiveUnsafe e
|
||||
unless excl do
|
||||
if let some result := (← get).find? e then
|
||||
return result
|
||||
match f? e with
|
||||
| some eNew => cache e excl eNew
|
||||
| none => match e with
|
||||
| .forallE _ d b _ => cache e excl <| e.updateForallE! (← visit d) (← visit b)
|
||||
| .lam _ d b _ => cache e excl <| e.updateLambdaE! (← visit d) (← visit b)
|
||||
| .mdata _ b => cache e excl <| e.updateMData! (← visit b)
|
||||
| .letE _ t v b _ => cache e excl <| e.updateLet! (← visit t) (← visit v) (← visit b)
|
||||
| .app f a => cache e excl <| e.updateApp! (← visit f) (← visit a)
|
||||
| .proj _ _ b => cache e excl <| e.updateProj! (← visit b)
|
||||
| e => return e
|
||||
visit e
|
||||
|
||||
@[inline]
|
||||
unsafe def replaceUnsafe (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
(replaceUnsafeM f? e).run' mkPtrMap
|
||||
|
||||
end ReplaceImpl
|
||||
|
||||
/- TODO: use withPtrAddr, withPtrEq to avoid unsafe tricks above.
|
||||
We also need an invariant at `State` and proofs for the `uget` operations. -/
|
||||
|
||||
@[specialize]
|
||||
def replaceNoCache (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
@@ -28,4 +77,6 @@ def replaceNoCache (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
| .proj _ _ b => let b := replaceNoCache f? b; e.updateProj! b
|
||||
| e => e
|
||||
|
||||
end Lean.Expr
|
||||
@[implemented_by ReplaceImpl.replaceUnsafe]
|
||||
partial def replace (f? : Expr → Option Expr) (e : Expr) : Expr :=
|
||||
e.replaceNoCache f?
|
||||
|
||||
@@ -416,9 +416,12 @@ open Lean Server RequestM in
|
||||
def getWidgets (pos : Lean.Lsp.Position) : RequestM (RequestTask (GetWidgetsResponse)) := do
|
||||
let doc ← readDoc
|
||||
let filemap := doc.meta.text
|
||||
mapTask (findInfoTreeAtPos doc <| filemap.lspPosToUtf8Pos pos) fun
|
||||
| some infoTree@(.context (.commandCtx cc) _) =>
|
||||
ContextInfo.runMetaM { cc with } {} do
|
||||
let nextLine := { line := pos.line + 1, character := 0 }
|
||||
let t := doc.cmdSnaps.waitUntil fun snap => filemap.lspPosToUtf8Pos nextLine ≤ snap.endPos
|
||||
mapTask t fun (snaps, _) => do
|
||||
let some snap := snaps.getLast?
|
||||
| return ⟨∅⟩
|
||||
runTermElabM snap do
|
||||
let env ← getEnv
|
||||
/- Panels from the environment. -/
|
||||
let ws' ← evalPanelWidgets
|
||||
@@ -433,7 +436,7 @@ def getWidgets (pos : Lean.Lsp.Position) : RequestM (RequestTask (GetWidgetsResp
|
||||
return uwd.name
|
||||
return { wi with name? }
|
||||
/- Panels from the infotree. -/
|
||||
let ws := widgetInfosAt? filemap infoTree pos.line
|
||||
let ws := widgetInfosAt? filemap snap.infoTree pos.line
|
||||
let ws : Array PanelWidgetInstance ← ws.toArray.mapM fun (wi : UserWidgetInfo) => do
|
||||
let name? ← env.find? wi.id
|
||||
|>.filter (·.type.isConstOf ``UserWidgetDefinition)
|
||||
@@ -442,7 +445,6 @@ def getWidgets (pos : Lean.Lsp.Position) : RequestM (RequestTask (GetWidgetsResp
|
||||
return uwd.name
|
||||
return { wi with range? := String.Range.toLspRange filemap <$> Syntax.getRange? wi.stx, name? }
|
||||
return { widgets := ws' ++ ws }
|
||||
| _ => return ⟨∅⟩
|
||||
|
||||
builtin_initialize
|
||||
Server.registerBuiltinRpcProcedure ``getWidgets _ _ getWidgets
|
||||
|
||||
@@ -82,11 +82,6 @@ theorem isEmpty_eq_false_iff_exists_isSome_getEntry? [BEq α] [ReflBEq α] :
|
||||
| [] => by simp
|
||||
| (⟨k, v⟩::l) => by simpa using ⟨k, by simp⟩
|
||||
|
||||
theorem isEmpty_iff_forall_isSome_getEntry? [BEq α] [ReflBEq α] :
|
||||
{l : List ((a : α) × β a)} → l.isEmpty ↔ ∀ a, (getEntry? a l).isSome = false
|
||||
| [] => by simp
|
||||
| (⟨k, v⟩::l) => ⟨by simp, fun h => have := h k; by simp at this⟩
|
||||
|
||||
section
|
||||
|
||||
variable {β : Type v}
|
||||
@@ -260,10 +255,6 @@ theorem isEmpty_eq_false_iff_exists_containsKey [BEq α] [ReflBEq α] {l : List
|
||||
l.isEmpty = false ↔ ∃ a, containsKey a l := by
|
||||
simp [isEmpty_eq_false_iff_exists_isSome_getEntry?, containsKey_eq_isSome_getEntry?]
|
||||
|
||||
theorem isEmpty_iff_forall_containsKey [BEq α] [ReflBEq α] {l : List ((a : α) × β a)} :
|
||||
l.isEmpty ↔ ∀ a, containsKey a l = false := by
|
||||
simp only [isEmpty_iff_forall_isSome_getEntry?, containsKey_eq_isSome_getEntry?]
|
||||
|
||||
@[simp]
|
||||
theorem getEntry?_eq_none [BEq α] {l : List ((a : α) × β a)} {a : α} :
|
||||
getEntry? a l = none ↔ containsKey a l = false := by
|
||||
@@ -588,7 +579,7 @@ theorem getEntry?_replaceEntry_of_true [BEq α] [PartialEquivBEq α] {l : List (
|
||||
|
||||
theorem getEntry?_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
|
||||
{v : β k} :
|
||||
getEntry? a (replaceEntry k v l) = if containsKey k l ∧ k == a then some ⟨k, v⟩ else
|
||||
getEntry? a (replaceEntry k v l) = bif containsKey k l && k == a then some ⟨k, v⟩ else
|
||||
getEntry? a l := by
|
||||
cases hl : containsKey k l
|
||||
· simp [getEntry?_replaceEntry_of_containsKey_eq_false hl]
|
||||
@@ -641,11 +632,13 @@ theorem getValueCast?_replaceEntry [BEq α] [LawfulBEq α] {l : List ((a : α)
|
||||
@[simp]
|
||||
theorem containsKey_replaceEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {a k : α}
|
||||
{v : β k} : containsKey a (replaceEntry k v l) = containsKey a l := by
|
||||
by_cases h : (getEntry? k l).isSome ∧ k == a
|
||||
· simp only [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h, and_self, ↓reduceIte,
|
||||
Option.isSome_some, Bool.true_eq]
|
||||
rw [← getEntry?_congr h.2, h.1]
|
||||
· simp [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h]
|
||||
cases h : containsKey k l && k == a
|
||||
· rw [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h, cond_false,
|
||||
containsKey_eq_isSome_getEntry?]
|
||||
· rw [containsKey_eq_isSome_getEntry?, getEntry?_replaceEntry, h, cond_true, Option.isSome_some,
|
||||
Eq.comm]
|
||||
rw [Bool.and_eq_true] at h
|
||||
exact containsKey_of_beq h.1 h.2
|
||||
|
||||
/-- Internal implementation detail of the hash map -/
|
||||
def eraseKey [BEq α] (k : α) : List ((a : α) × β a) → List ((a : α) × β a)
|
||||
@@ -688,7 +681,7 @@ theorem sublist_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
· simpa using Sublist.cons_right Sublist.refl
|
||||
|
||||
theorem length_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(eraseKey k l).length = if containsKey k l then l.length - 1 else l.length := by
|
||||
(eraseKey k l).length = bif containsKey k l then l.length - 1 else l.length := by
|
||||
induction l using assoc_induction
|
||||
· simp
|
||||
· next k' v' t ih =>
|
||||
@@ -697,7 +690,7 @@ theorem length_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
· rw [cond_false, Bool.false_or, List.length_cons, ih]
|
||||
cases h : containsKey k t
|
||||
· simp
|
||||
· simp only [Nat.succ_eq_add_one, List.length_cons, Nat.add_sub_cancel, if_true]
|
||||
· simp only [cond_true, Nat.succ_eq_add_one, List.length_cons, Nat.add_sub_cancel]
|
||||
rw [Nat.sub_add_cancel]
|
||||
cases t
|
||||
· simp at h
|
||||
@@ -708,11 +701,6 @@ theorem length_eraseKey_le [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(eraseKey k l).length ≤ l.length :=
|
||||
sublist_eraseKey.length_le
|
||||
|
||||
theorem length_le_length_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
l.length ≤ (eraseKey k l).length + 1 := by
|
||||
rw [length_eraseKey]
|
||||
split <;> omega
|
||||
|
||||
theorem isEmpty_eraseKey [BEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
(eraseKey k l).isEmpty = (l.isEmpty || (l.length == 1 && containsKey k l)) := by
|
||||
rw [Bool.eq_iff_iff]
|
||||
@@ -867,18 +855,15 @@ theorem isEmpty_insertEntry [BEq α] {l : List ((a : α) × β a)} {k : α} {v :
|
||||
· rw [insertEntry_of_containsKey h, isEmpty_replaceEntry, isEmpty_eq_false_of_containsKey h]
|
||||
|
||||
theorem length_insertEntry [BEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
(insertEntry k v l).length = if containsKey k l then l.length else l.length + 1 := by
|
||||
(insertEntry k v l).length = bif containsKey k l then l.length else l.length + 1 := by
|
||||
simp [insertEntry, Bool.apply_cond List.length]
|
||||
|
||||
theorem length_le_length_insertEntry [BEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
l.length ≤ (insertEntry k v l).length := by
|
||||
rw [length_insertEntry]
|
||||
split <;> omega
|
||||
|
||||
theorem length_insertEntry_le [BEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
(insertEntry k v l).length ≤ l.length + 1 := by
|
||||
rw [length_insertEntry]
|
||||
split <;> omega
|
||||
cases containsKey k l
|
||||
· simpa using Nat.le_add_right ..
|
||||
· simp
|
||||
|
||||
section
|
||||
|
||||
@@ -901,23 +886,23 @@ theorem getValue?_insertEntry_of_false [BEq α] [PartialEquivBEq α] {l : List (
|
||||
· rw [insertEntry_of_containsKey h', getValue?_replaceEntry_of_false h]
|
||||
|
||||
theorem getValue?_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
{v : β} : getValue? a (insertEntry k v l) = if k == a then some v else getValue? a l := by
|
||||
{v : β} : getValue? a (insertEntry k v l) = bif k == a then some v else getValue? a l := by
|
||||
cases h : k == a
|
||||
· simp [getValue?_insertEntry_of_false h, h]
|
||||
· simp [getValue?_insertEntry_of_beq h, h]
|
||||
|
||||
theorem getValue?_insertEntry_self [BEq α] [EquivBEq α] {l : List ((_ : α) × β)} {k : α} {v : β} :
|
||||
getValue? k (insertEntry k v l) = some v := by
|
||||
simp [getValue?_insertEntry]
|
||||
rw [getValue?_insertEntry, Bool.cond_pos BEq.refl]
|
||||
|
||||
end
|
||||
|
||||
theorem getEntry?_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} :
|
||||
getEntry? a (insertEntry k v l) = if k == a then some ⟨k, v⟩ else getEntry? a l := by
|
||||
getEntry? a (insertEntry k v l) = bif k == a then some ⟨k, v⟩ else getEntry? a l := by
|
||||
cases hl : containsKey k l
|
||||
· rw [insertEntry_of_containsKey_eq_false hl, getEntry?_cons, cond_eq_if]
|
||||
· simp [insertEntry_of_containsKey hl, getEntry?_replaceEntry, hl]
|
||||
· rw [insertEntry_of_containsKey_eq_false hl, getEntry?_cons]
|
||||
· rw [insertEntry_of_containsKey hl, getEntry?_replaceEntry, hl, Bool.true_and, BEq.comm]
|
||||
|
||||
theorem getValueCast?_insertEntry [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} : getValueCast? a (insertEntry k v l) =
|
||||
@@ -953,21 +938,21 @@ theorem getValueCastD_insertEntry_self [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValue!_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k a : α} {v : β} :
|
||||
getValue! a (insertEntry k v l) = if k == a then v else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_insertEntry, apply_ite Option.get!]
|
||||
getValue! a (insertEntry k v l) = bif k == a then v else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_insertEntry, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValue!_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k : α} {v : β} : getValue! k (insertEntry k v l) = v := by
|
||||
simp [getValue!_insertEntry, BEq.refl]
|
||||
rw [getValue!_insertEntry, BEq.refl, cond_true]
|
||||
|
||||
theorem getValueD_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {fallback v : β} : getValueD a (insertEntry k v l) fallback =
|
||||
if k == a then v else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_insertEntry, apply_ite (fun x => Option.getD x fallback)]
|
||||
bif k == a then v else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_insertEntry, Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem getValueD_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k : α} {fallback v : β} : getValueD k (insertEntry k v l) fallback = v := by
|
||||
simp [getValueD_insertEntry, BEq.refl]
|
||||
rw [getValueD_insertEntry, BEq.refl, cond_true]
|
||||
|
||||
@[simp]
|
||||
theorem containsKey_insertEntry [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
@@ -1006,7 +991,7 @@ theorem getValue_insertEntry {β : Type v} [BEq α] [PartialEquivBEq α] {l : Li
|
||||
if h' : k == a then v
|
||||
else getValue a l (containsKey_of_containsKey_insertEntry h (Bool.eq_false_iff.2 h')) := by
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, apply_dite Option.some,
|
||||
getValue?_insertEntry, ← dite_eq_ite]
|
||||
getValue?_insertEntry, cond_eq_if, ← dite_eq_ite]
|
||||
simp only [← getValue?_eq_some_getValue]
|
||||
|
||||
theorem getValue_insertEntry_self {β : Type v} [BEq α] [EquivBEq α] {l : List ((_ : α) × β)} {k : α}
|
||||
@@ -1035,7 +1020,7 @@ theorem isEmpty_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
|
||||
theorem getEntry?_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} : getEntry? a (insertEntryIfNew k v l) =
|
||||
if k == a && !containsKey k l then some ⟨k, v⟩ else getEntry? a l := by
|
||||
bif k == a && !containsKey k l then some ⟨k, v⟩ else getEntry? a l := by
|
||||
cases h : containsKey k l
|
||||
· simp [insertEntryIfNew_of_containsKey_eq_false h, getEntry?_cons]
|
||||
· simp [insertEntryIfNew_of_containsKey h]
|
||||
@@ -1051,22 +1036,18 @@ theorem getValueCast?_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValue?_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {v : β} : getValue? a (insertEntryIfNew k v l) =
|
||||
if k == a ∧ containsKey k l = false then some v else getValue? a l := by
|
||||
bif k == a && !containsKey k l then some v else getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_insertEntryIfNew,
|
||||
apply_ite (Option.map (fun (y : ((_ : α) × β)) => y.2))]
|
||||
Bool.apply_cond (Option.map (fun (y : ((_ : α) × β)) => y.2))]
|
||||
|
||||
theorem containsKey_insertEntryIfNew [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)}
|
||||
{k a : α} {v : β k} :
|
||||
containsKey a (insertEntryIfNew k v l) = ((k == a) || containsKey a l) := by
|
||||
simp only [containsKey_eq_isSome_getEntry?, getEntry?_insertEntryIfNew, apply_ite Option.isSome,
|
||||
Option.isSome_some, if_true_left]
|
||||
simp only [Bool.and_eq_true, Bool.not_eq_true', Option.not_isSome, Option.isNone_iff_eq_none,
|
||||
getEntry?_eq_none, Bool.if_true_left, Bool.decide_and, Bool.decide_eq_true,
|
||||
Bool.decide_eq_false]
|
||||
simp only [containsKey_eq_isSome_getEntry?, getEntry?_insertEntryIfNew, Bool.apply_cond Option.isSome,
|
||||
Option.isSome_some, Bool.cond_true_left]
|
||||
cases h : k == a
|
||||
· simp
|
||||
· rw [containsKey_eq_isSome_getEntry?, getEntry?_congr h]
|
||||
simp
|
||||
· rw [Bool.true_and, Bool.true_or, getEntry?_congr h, Bool.not_or_self]
|
||||
|
||||
theorem containsKey_insertEntryIfNew_self [BEq α] [EquivBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
{v : β k} : containsKey k (insertEntryIfNew k v l) := by
|
||||
@@ -1104,7 +1085,7 @@ theorem getValue_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l
|
||||
if h' : k == a ∧ containsKey k l = false then v
|
||||
else getValue a l (containsKey_of_containsKey_insertEntryIfNew' h h') := by
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, apply_dite Option.some,
|
||||
getValue?_insertEntryIfNew, ← dite_eq_ite]
|
||||
getValue?_insertEntryIfNew, cond_eq_if, ← dite_eq_ite]
|
||||
simp [← getValue?_eq_some_getValue]
|
||||
|
||||
theorem getValueCast!_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
@@ -1115,8 +1096,8 @@ theorem getValueCast!_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValue!_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k a : α} {v : β} : getValue! a (insertEntryIfNew k v l) =
|
||||
if k == a ∧ containsKey k l = false then v else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_insertEntryIfNew, apply_ite Option.get!]
|
||||
bif k == a && !containsKey k l then v else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_insertEntryIfNew, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValueCastD_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{v : β k} {fallback : β a} : getValueCastD a (insertEntryIfNew k v l) fallback =
|
||||
@@ -1127,23 +1108,20 @@ theorem getValueCastD_insertEntryIfNew [BEq α] [LawfulBEq α] {l : List ((a :
|
||||
|
||||
theorem getValueD_insertEntryIfNew {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {fallback v : β} : getValueD a (insertEntryIfNew k v l) fallback =
|
||||
if k == a ∧ containsKey k l = false then v else getValueD a l fallback := by
|
||||
bif k == a && !containsKey k l then v else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_insertEntryIfNew,
|
||||
apply_ite (fun x => Option.getD x fallback)]
|
||||
Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem length_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
(insertEntryIfNew k v l).length = if containsKey k l then l.length else l.length + 1 := by
|
||||
(insertEntryIfNew k v l).length = bif containsKey k l then l.length else l.length + 1 := by
|
||||
simp [insertEntryIfNew, Bool.apply_cond List.length]
|
||||
|
||||
theorem length_le_length_insertEntryIfNew [BEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
l.length ≤ (insertEntryIfNew k v l).length := by
|
||||
rw [length_insertEntryIfNew]
|
||||
split <;> omega
|
||||
|
||||
theorem length_insertEntryIfNew_le [BEq α] {l : List ((a : α) × β a)} {k : α} {v : β k} :
|
||||
(insertEntryIfNew k v l).length ≤ l.length + 1 := by
|
||||
rw [length_insertEntryIfNew]
|
||||
split <;> omega
|
||||
cases containsKey k l
|
||||
· simpa using Nat.le_add_right ..
|
||||
· simp
|
||||
|
||||
@[simp]
|
||||
theorem keys_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k : α} :
|
||||
@@ -1191,7 +1169,7 @@ theorem getEntry?_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((a
|
||||
|
||||
theorem getEntry?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) :
|
||||
getEntry? a (eraseKey k l) = if k == a then none else getEntry? a l := by
|
||||
getEntry? a (eraseKey k l) = bif k == a then none else getEntry? a l := by
|
||||
cases h : k == a
|
||||
· simp [getEntry?_eraseKey_of_false h, h]
|
||||
· simp [getEntry?_eraseKey_of_beq hl h, h]
|
||||
@@ -1244,8 +1222,8 @@ theorem getValue?_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((_
|
||||
|
||||
theorem getValue?_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)} {k a : α}
|
||||
(hl : DistinctKeys l) :
|
||||
getValue? a (eraseKey k l) = if k == a then none else getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_eraseKey hl, apply_ite (Option.map _)]
|
||||
getValue? a (eraseKey k l) = bif k == a then none else getValue? a l := by
|
||||
simp [getValue?_eq_getEntry?, getEntry?_eraseKey hl, Bool.apply_cond (Option.map _)]
|
||||
|
||||
end
|
||||
|
||||
@@ -1263,25 +1241,25 @@ theorem containsKey_eraseKey_of_false [BEq α] [PartialEquivBEq α] {l : List ((
|
||||
|
||||
theorem containsKey_eraseKey [BEq α] [PartialEquivBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) : containsKey a (eraseKey k l) = (!(k == a) && containsKey a l) := by
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey hl, apply_ite]
|
||||
simp [containsKey_eq_isSome_getEntry?, getEntry?_eraseKey hl, Bool.apply_cond]
|
||||
|
||||
theorem getValueCast?_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
(hl : DistinctKeys l) :
|
||||
getValueCast? a (eraseKey k l) = if k == a then none else getValueCast? a l := by
|
||||
getValueCast? a (eraseKey k l) = bif k == a then none else getValueCast? a l := by
|
||||
rw [getValueCast?_eq_getEntry?, Option.dmap_congr (getEntry?_eraseKey hl)]
|
||||
by_cases h : k == a
|
||||
· rw [Option.dmap_congr (if_pos h), Option.dmap_none, if_pos h]
|
||||
· rw [Option.dmap_congr (if_neg h), getValueCast?_eq_getEntry?]
|
||||
exact (if_neg h).symm
|
||||
rcases Bool.eq_false_or_eq_true (k == a) with h|h
|
||||
· rw [Option.dmap_congr (Bool.cond_pos h), Option.dmap_none, Bool.cond_pos h]
|
||||
· rw [Option.dmap_congr (Bool.cond_neg h), getValueCast?_eq_getEntry?]
|
||||
exact (Bool.cond_neg h).symm
|
||||
|
||||
theorem getValueCast?_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
(hl : DistinctKeys l) : getValueCast? k (eraseKey k l) = none := by
|
||||
rw [getValueCast?_eraseKey hl, if_pos BEq.refl]
|
||||
rw [getValueCast?_eraseKey hl, Bool.cond_pos BEq.refl]
|
||||
|
||||
theorem getValueCast!_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
[Inhabited (β a)] (hl : DistinctKeys l) :
|
||||
getValueCast! a (eraseKey k l) = if k == a then default else getValueCast! a l := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_eraseKey hl, apply_ite Option.get!]
|
||||
getValueCast! a (eraseKey k l) = bif k == a then default else getValueCast! a l := by
|
||||
simp [getValueCast!_eq_getValueCast?, getValueCast?_eraseKey hl, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValueCast!_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
[Inhabited (β k)] (hl : DistinctKeys l) : getValueCast! k (eraseKey k l) = default := by
|
||||
@@ -1289,9 +1267,9 @@ theorem getValueCast!_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α)
|
||||
|
||||
theorem getValueCastD_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α}
|
||||
{fallback : β a} (hl : DistinctKeys l) : getValueCastD a (eraseKey k l) fallback =
|
||||
if k == a then fallback else getValueCastD a l fallback := by
|
||||
bif k == a then fallback else getValueCastD a l fallback := by
|
||||
simp [getValueCastD_eq_getValueCast?, getValueCast?_eraseKey hl,
|
||||
apply_ite (fun x => Option.getD x fallback)]
|
||||
Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem getValueCastD_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k : α}
|
||||
{fallback : β k} (hl : DistinctKeys l) :
|
||||
@@ -1300,8 +1278,8 @@ theorem getValueCastD_eraseKey_self [BEq α] [LawfulBEq α] {l : List ((a : α)
|
||||
|
||||
theorem getValue!_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k a : α} (hl : DistinctKeys l) :
|
||||
getValue! a (eraseKey k l) = if k == a then default else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_eraseKey hl, apply_ite Option.get!]
|
||||
getValue! a (eraseKey k l) = bif k == a then default else getValue! a l := by
|
||||
simp [getValue!_eq_getValue?, getValue?_eraseKey hl, Bool.apply_cond Option.get!]
|
||||
|
||||
theorem getValue!_eraseKey_self {β : Type v} [BEq α] [PartialEquivBEq α] [Inhabited β]
|
||||
{l : List ((_ : α) × β)} {k : α} (hl : DistinctKeys l) :
|
||||
@@ -1310,8 +1288,8 @@ theorem getValue!_eraseKey_self {β : Type v} [BEq α] [PartialEquivBEq α] [Inh
|
||||
|
||||
theorem getValueD_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {fallback : β} (hl : DistinctKeys l) : getValueD a (eraseKey k l) fallback =
|
||||
if k == a then fallback else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_eraseKey hl, apply_ite (fun x => Option.getD x fallback)]
|
||||
bif k == a then fallback else getValueD a l fallback := by
|
||||
simp [getValueD_eq_getValue?, getValue?_eraseKey hl, Bool.apply_cond (fun x => Option.getD x fallback)]
|
||||
|
||||
theorem getValueD_eraseKey_self {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k : α} {fallback : β} (hl : DistinctKeys l) :
|
||||
@@ -1326,15 +1304,15 @@ theorem getValueCast_eraseKey [BEq α] [LawfulBEq α] {l : List ((a : α) × β
|
||||
(hl : DistinctKeys l) : getValueCast a (eraseKey k l) h =
|
||||
getValueCast a l (containsKey_of_containsKey_eraseKey hl h) := by
|
||||
rw [containsKey_eraseKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
|
||||
rw [← Option.some_inj, ← getValueCast?_eq_some_getValueCast, getValueCast?_eraseKey hl, h.1]
|
||||
simp [← getValueCast?_eq_some_getValueCast]
|
||||
rw [← Option.some_inj, ← getValueCast?_eq_some_getValueCast, getValueCast?_eraseKey hl, h.1,
|
||||
cond_false, ← getValueCast?_eq_some_getValueCast]
|
||||
|
||||
theorem getValue_eraseKey {β : Type v} [BEq α] [PartialEquivBEq α] {l : List ((_ : α) × β)}
|
||||
{k a : α} {h} (hl : DistinctKeys l) :
|
||||
getValue a (eraseKey k l) h = getValue a l (containsKey_of_containsKey_eraseKey hl h) := by
|
||||
rw [containsKey_eraseKey hl, Bool.and_eq_true, Bool.not_eq_true'] at h
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, getValue?_eraseKey hl, h.1]
|
||||
simp [← getValue?_eq_some_getValue]
|
||||
rw [← Option.some_inj, ← getValue?_eq_some_getValue, getValue?_eraseKey hl, h.1, cond_false,
|
||||
← getValue?_eq_some_getValue]
|
||||
|
||||
theorem getEntry?_of_perm [BEq α] [PartialEquivBEq α] {l l' : List ((a : α) × β a)} {a : α}
|
||||
(hl : DistinctKeys l) (h : Perm l l') : getEntry? a l = getEntry? a l' := by
|
||||
|
||||
@@ -123,12 +123,9 @@ theorem contains_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashable α] :
|
||||
m.1.isEmpty = false ↔ ∃ a, m.contains a = true := by
|
||||
simp only [contains_eq_containsKey (Raw.WF.out h)]
|
||||
simp_to_model using List.isEmpty_eq_false_iff_exists_containsKey
|
||||
|
||||
theorem isEmpty_iff_forall_contains [EquivBEq α] [LawfulHashable α] :
|
||||
m.1.isEmpty ↔ ∀ a, m.contains a = false := by
|
||||
simp_to_model using List.isEmpty_iff_forall_containsKey
|
||||
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insert k v).contains a = ((k == a) || m.contains a) := by
|
||||
simp_to_model using List.containsKey_insertEntry
|
||||
@@ -148,17 +145,13 @@ theorem isEmpty_eq_size_eq_zero : m.1.isEmpty = (m.1.size == 0) := by
|
||||
simp [Raw.isEmpty]
|
||||
|
||||
theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insert k v).1.size = if m.contains k then m.1.size else m.1.size + 1 := by
|
||||
(m.insert k v).1.size = bif m.contains k then m.1.size else m.1.size + 1 := by
|
||||
simp_to_model using List.length_insertEntry
|
||||
|
||||
theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
m.1.size ≤ (m.insert k v).1.size := by
|
||||
simp_to_model using List.length_le_length_insertEntry
|
||||
|
||||
theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insert k v).1.size ≤ m.1.size + 1 := by
|
||||
simp_to_model using List.length_insertEntry_le
|
||||
|
||||
@[simp]
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw₀ α β).erase k = empty c := by
|
||||
simp [erase, empty]
|
||||
@@ -176,17 +169,13 @@ theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α}
|
||||
simp_to_model using List.containsKey_of_containsKey_eraseKey
|
||||
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).1.size = if m.contains k then m.1.size - 1 else m.1.size := by
|
||||
(m.erase k).1.size = bif m.contains k then m.1.size - 1 else m.1.size := by
|
||||
simp_to_model using List.length_eraseKey
|
||||
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).1.size ≤ m.1.size := by
|
||||
simp_to_model using List.length_eraseKey_le
|
||||
|
||||
theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
m.1.size ≤ (m.erase k).1.size + 1 := by
|
||||
simp_to_model using List.length_le_length_eraseKey
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k := by
|
||||
rw [containsThenInsert_eq_containsₘ, contains_eq_containsₘ]
|
||||
@@ -226,7 +215,7 @@ theorem get?_eq_none [LawfulBEq α] {a : α} : m.contains a = false → m.get? a
|
||||
simp_to_model using List.getValueCast?_eq_none
|
||||
|
||||
theorem get?_erase [LawfulBEq α] {k a : α} :
|
||||
(m.erase k).get? a = if k == a then none else m.get? a := by
|
||||
(m.erase k).get? a = bif k == a then none else m.get? a := by
|
||||
simp_to_model using List.getValueCast?_eraseKey
|
||||
|
||||
theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none := by
|
||||
@@ -245,7 +234,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
simp_to_model; empty
|
||||
|
||||
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insert k v) a = if k == a then some v else get? m a := by
|
||||
get? (m.insert k v) a = bif k == a then some v else get? m a := by
|
||||
simp_to_model using List.getValue?_insertEntry
|
||||
|
||||
theorem get?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
@@ -261,7 +250,7 @@ theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
simp_to_model using List.getValue?_eq_none.2
|
||||
|
||||
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.erase k) a = if k == a then none else get? m a := by
|
||||
Const.get? (m.erase k) a = bif k == a then none else get? m a := by
|
||||
simp_to_model using List.getValue?_eraseKey
|
||||
|
||||
theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
@@ -351,7 +340,7 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
simp_to_model using List.getValueCast!_eq_default
|
||||
|
||||
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.erase k).get! a = if k == a then default else m.get! a := by
|
||||
(m.erase k).get! a = bif k == a then default else m.get! a := by
|
||||
simp_to_model using List.getValueCast!_eraseKey
|
||||
|
||||
theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] :
|
||||
@@ -383,7 +372,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
simp_to_model; empty
|
||||
|
||||
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insert k v) a = if k == a then v else get! m a := by
|
||||
get! (m.insert k v) a = bif k == a then v else get! m a := by
|
||||
simp_to_model using List.getValue!_insertEntry
|
||||
|
||||
theorem get!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} {v : β} :
|
||||
@@ -395,7 +384,7 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
simp_to_model using List.getValue!_eq_default
|
||||
|
||||
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.erase k) a = if k == a then default else get! m a := by
|
||||
get! (m.erase k) a = bif k == a then default else get! m a := by
|
||||
simp_to_model using List.getValue!_eraseKey
|
||||
|
||||
theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} :
|
||||
@@ -446,7 +435,7 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
simp_to_model using List.getValueCastD_eq_fallback
|
||||
|
||||
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback := by
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback := by
|
||||
simp_to_model using List.getValueCastD_eraseKey
|
||||
|
||||
theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} :
|
||||
@@ -482,7 +471,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
simp_to_model; empty
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insert k v) a fallback = if k == a then v else getD m a fallback := by
|
||||
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback := by
|
||||
simp_to_model using List.getValueD_insertEntry
|
||||
|
||||
theorem getD_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback v : β} :
|
||||
@@ -494,7 +483,7 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
simp_to_model using List.getValueD_eq_fallback
|
||||
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.erase k) a fallback = if k == a then fallback else getD m a fallback := by
|
||||
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback := by
|
||||
simp_to_model using List.getValueD_eraseKey
|
||||
|
||||
theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} :
|
||||
@@ -550,17 +539,13 @@ theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a
|
||||
simp_to_model using List.containsKey_of_containsKey_insertEntryIfNew'
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insertIfNew k v).1.size = if m.contains k then m.1.size else m.1.size + 1 := by
|
||||
(m.insertIfNew k v).1.size = bif m.contains k then m.1.size else m.1.size + 1 := by
|
||||
simp_to_model using List.length_insertEntryIfNew
|
||||
|
||||
theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
m.1.size ≤ (m.insertIfNew k v).1.size := by
|
||||
simp_to_model using List.length_le_length_insertEntryIfNew
|
||||
|
||||
theorem size_insertIfNew_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insertIfNew k v).1.size ≤ m.1.size + 1 := by
|
||||
simp_to_model using List.length_insertEntryIfNew_le
|
||||
|
||||
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).get? a =
|
||||
if h : k == a ∧ m.contains k = false then some (cast (congrArg β (eq_of_beq h.1)) v)
|
||||
@@ -590,7 +575,7 @@ namespace Const
|
||||
variable {β : Type v} (m : Raw₀ α (fun _ => β)) (h : m.1.WF)
|
||||
|
||||
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insertIfNew k v) a = if k == a ∧ m.contains k = false then some v else get? m a := by
|
||||
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a := by
|
||||
simp_to_model using List.getValue?_insertEntryIfNew
|
||||
|
||||
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
@@ -600,12 +585,12 @@ theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h
|
||||
simp_to_model using List.getValue_insertEntryIfNew
|
||||
|
||||
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insertIfNew k v) a = if k == a ∧ m.contains k = false then v else get! m a := by
|
||||
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a := by
|
||||
simp_to_model using List.getValue!_insertEntryIfNew
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insertIfNew k v) a fallback =
|
||||
if k == a ∧ m.contains k = false then v else getD m a fallback := by
|
||||
bif k == a && !m.contains k then v else getD m a fallback := by
|
||||
simp_to_model using List.getValueD_insertEntryIfNew
|
||||
|
||||
end Const
|
||||
|
||||
@@ -472,8 +472,7 @@ theorem wfImp_eraseₘaux [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable
|
||||
buckets_hash_self := isHashSelf_eraseₘaux m a h
|
||||
size_eq := by
|
||||
rw [(toListModel_eraseₘaux m a h).length_eq, eraseₘaux, length_eraseKey,
|
||||
← containsₘ_eq_containsKey h, h']
|
||||
simp [h.size_eq]
|
||||
← containsₘ_eq_containsKey h, h', cond_true, h.size_eq]
|
||||
distinct := h.distinct.eraseKey.perm (toListModel_eraseₘaux m a h)
|
||||
|
||||
theorem toListModel_perm_eraseKey_of_containsₘ_eq_false [BEq α] [Hashable α] [EquivBEq α]
|
||||
|
||||
@@ -63,30 +63,6 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
@[simp] theorem not_mem_emptyc {a : α} : ¬a ∈ (∅ : DHashMap α β) :=
|
||||
not_mem_empty
|
||||
|
||||
theorem contains_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → m.contains a = false :=
|
||||
Raw₀.contains_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem not_mem_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → ¬a ∈ m := by
|
||||
simpa [mem_iff_contains] using contains_of_isEmpty
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, m.contains a = true :=
|
||||
Raw₀.isEmpty_eq_false_iff_exists_contains_eq_true ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, a ∈ m := by
|
||||
simpa [mem_iff_contains] using isEmpty_eq_false_iff_exists_contains_eq_true
|
||||
|
||||
theorem isEmpty_iff_forall_contains [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, m.contains a = false :=
|
||||
Raw₀.isEmpty_iff_forall_contains ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem isEmpty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, ¬a ∈ m := by
|
||||
simpa [mem_iff_contains] using isEmpty_iff_forall_contains
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} :
|
||||
(m.insert k v).contains a = (k == a || m.contains a) :=
|
||||
@@ -126,17 +102,13 @@ theorem size_emptyc : (∅ : DHashMap α β).size = 0 :=
|
||||
theorem isEmpty_eq_size_eq_zero : m.isEmpty = (m.size == 0) := rfl
|
||||
|
||||
theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insert k v).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insert k v).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
Raw₀.size_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
m.size ≤ (m.insert k v).size :=
|
||||
Raw₀.size_le_size_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insert k v).size ≤ m.size + 1 :=
|
||||
Raw₀.size_insert_le ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : DHashMap α β).erase k = empty c :=
|
||||
Subtype.eq (congrArg Subtype.val (Raw₀.erase_empty (k := k)) :) -- Lean code is happy
|
||||
@@ -168,16 +140,12 @@ theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.
|
||||
simp
|
||||
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = if k ∈ m then m.size - 1 else m.size :=
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size :=
|
||||
Raw₀.size_erase _ m.2
|
||||
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size :=
|
||||
Raw₀.size_erase_le _ m.2
|
||||
|
||||
theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
m.size ≤ (m.erase k).size + 1 :=
|
||||
Raw₀.size_le_size_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k :=
|
||||
Raw₀.containsThenInsert_fst _
|
||||
@@ -226,7 +194,7 @@ theorem get?_eq_none [LawfulBEq α] {a : α} : ¬a ∈ m → m.get? a = none :=
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false
|
||||
|
||||
theorem get?_erase [LawfulBEq α] {k a : α} :
|
||||
(m.erase k).get? a = if k == a then none else m.get? a :=
|
||||
(m.erase k).get? a = bif k == a then none else m.get? a :=
|
||||
Raw₀.get?_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -250,7 +218,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
Raw₀.Const.get?_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insert k v) a = if k == a then some v else get? m a :=
|
||||
get? (m.insert k v) a = bif k == a then some v else get? m a :=
|
||||
Raw₀.Const.get?_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -270,7 +238,7 @@ theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α } : ¬a ∈ m →
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false
|
||||
|
||||
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.erase k) a = if k == a then none else get? m a :=
|
||||
Const.get? (m.erase k) a = bif k == a then none else get? m a :=
|
||||
Raw₀.Const.get?_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -371,7 +339,7 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false
|
||||
|
||||
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.erase k).get! a = if k == a then default else m.get! a :=
|
||||
(m.erase k).get! a = bif k == a then default else m.get! a :=
|
||||
Raw₀.get!_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -413,7 +381,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
Raw₀.Const.get!_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insert k v) a = if k == a then v else get! m a :=
|
||||
get! (m.insert k v) a = bif k == a then v else get! m a :=
|
||||
Raw₀.Const.get!_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -430,7 +398,7 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false
|
||||
|
||||
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.erase k) a = if k == a then default else get! m a :=
|
||||
get! (m.erase k) a = bif k == a then default else get! m a :=
|
||||
Raw₀.Const.get!_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -497,7 +465,7 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false
|
||||
|
||||
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback :=
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback :=
|
||||
Raw₀.getD_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -544,7 +512,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
Raw₀.Const.getD_of_isEmpty ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insert k v) a fallback = if k == a then v else getD m a fallback :=
|
||||
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback :=
|
||||
Raw₀.Const.getD_insert ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -561,7 +529,7 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false
|
||||
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.erase k) a fallback = if k == a then fallback else getD m a fallback :=
|
||||
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback :=
|
||||
Raw₀.Const.getD_erase ⟨m.1, _⟩ m.2
|
||||
|
||||
@[simp]
|
||||
@@ -643,17 +611,13 @@ theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v
|
||||
simpa [mem_iff_contains, -contains_insertIfNew] using contains_of_contains_insertIfNew'
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insertIfNew k v).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insertIfNew k v).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
Raw₀.size_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
m.size ≤ (m.insertIfNew k v).size :=
|
||||
Raw₀.size_le_size_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem size_insertIfNew_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insertIfNew k v).size ≤ m.size + 1 :=
|
||||
Raw₀.size_insertIfNew_le _ m.2
|
||||
|
||||
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} : (m.insertIfNew k v).get? a =
|
||||
if h : k == a ∧ ¬k ∈ m then some (cast (congrArg β (eq_of_beq h.1)) v) else m.get? a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
@@ -683,9 +647,8 @@ namespace Const
|
||||
variable {β : Type v} {m : DHashMap α (fun _ => β)}
|
||||
|
||||
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then some v else get? m a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.Const.get?_insertIfNew ⟨m.1, _⟩ m.2
|
||||
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a :=
|
||||
Raw₀.Const.get?_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
get (m.insertIfNew k v) a h₁ =
|
||||
@@ -694,15 +657,13 @@ theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h
|
||||
exact Raw₀.Const.get_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then v else get! m a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.Const.get!_insertIfNew ⟨m.1, _⟩ m.2
|
||||
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a :=
|
||||
Raw₀.Const.get!_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insertIfNew k v) a fallback =
|
||||
if k == a ∧ ¬k ∈ m then v else getD m a fallback := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
exact Raw₀.Const.getD_insertIfNew ⟨m.1, _⟩ m.2
|
||||
bif k == a && !m.contains k then v else getD m a fallback :=
|
||||
Raw₀.Const.getD_insertIfNew ⟨m.1, _⟩ m.2
|
||||
|
||||
end Const
|
||||
|
||||
|
||||
@@ -111,30 +111,6 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
@[simp] theorem not_mem_emptyc {a : α} : ¬a ∈ (∅ : Raw α β) :=
|
||||
not_mem_empty
|
||||
|
||||
theorem contains_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → m.contains a = false := by
|
||||
simp_to_raw using Raw₀.contains_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem not_mem_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → ¬a ∈ m := by
|
||||
simpa [mem_iff_contains] using contains_of_isEmpty h
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, m.contains a = true := by
|
||||
simp_to_raw using Raw₀.isEmpty_eq_false_iff_exists_contains_eq_true ⟨m, _⟩
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, a ∈ m := by
|
||||
simpa [mem_iff_contains] using isEmpty_eq_false_iff_exists_contains_eq_true h
|
||||
|
||||
theorem isEmpty_iff_forall_contains [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, m.contains a = false := by
|
||||
simp_to_raw using Raw₀.isEmpty_iff_forall_contains ⟨m, _⟩
|
||||
|
||||
theorem isEmpty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, ¬a ∈ m := by
|
||||
simpa [mem_iff_contains] using isEmpty_iff_forall_contains h
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {a k : α} {v : β k} :
|
||||
(m.insert k v).contains a = (k == a || m.contains a) := by
|
||||
@@ -174,18 +150,13 @@ theorem isEmpty_eq_size_eq_zero : m.isEmpty = (m.size == 0) := by
|
||||
simp [isEmpty]
|
||||
|
||||
theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insert k v).size = if k ∈ m then m.size else m.size + 1 := by
|
||||
simp only [mem_iff_contains]
|
||||
(m.insert k v).size = bif m.contains k then m.size else m.size + 1 := by
|
||||
simp_to_raw using Raw₀.size_insert
|
||||
|
||||
theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
m.size ≤ (m.insert k v).size := by
|
||||
simp_to_raw using Raw₀.size_le_size_insert ⟨m, _⟩ h
|
||||
|
||||
theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insert k v).size ≤ m.size + 1 := by
|
||||
simp_to_raw using Raw₀.size_insert_le ⟨m, _⟩ h
|
||||
|
||||
@[simp]
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw α β).erase k = empty c := by
|
||||
rw [erase_eq (by wf_trivial)]
|
||||
@@ -218,17 +189,12 @@ theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.
|
||||
simpa [mem_iff_contains] using contains_of_contains_erase h
|
||||
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = if k ∈ m then m.size - 1 else m.size := by
|
||||
simp only [mem_iff_contains]
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size := by
|
||||
simp_to_raw using Raw₀.size_erase
|
||||
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size := by
|
||||
simp_to_raw using Raw₀.size_erase_le
|
||||
|
||||
theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
m.size ≤ (m.erase k).size + 1 := by
|
||||
simp_to_raw using Raw₀.size_le_size_erase ⟨m, _⟩
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k := by
|
||||
simp_to_raw using Raw₀.containsThenInsert_fst
|
||||
@@ -277,7 +243,7 @@ theorem get?_eq_none [LawfulBEq α] {a : α} : ¬a ∈ m → m.get? a = none :=
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false h
|
||||
|
||||
theorem get?_erase [LawfulBEq α] {k a : α} :
|
||||
(m.erase k).get? a = if k == a then none else m.get? a := by
|
||||
(m.erase k).get? a = bif k == a then none else m.get? a := by
|
||||
simp_to_raw using Raw₀.get?_erase
|
||||
|
||||
@[simp]
|
||||
@@ -301,7 +267,7 @@ theorem get?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
simp_to_raw using Raw₀.Const.get?_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insert k v) a = if k == a then some v else get? m a := by
|
||||
get? (m.insert k v) a = bif k == a then some v else get? m a := by
|
||||
simp_to_raw using Raw₀.Const.get?_insert
|
||||
|
||||
@[simp]
|
||||
@@ -321,7 +287,7 @@ theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m →
|
||||
simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false h
|
||||
|
||||
theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
Const.get? (m.erase k) a = if k == a then none else get? m a := by
|
||||
Const.get? (m.erase k) a = bif k == a then none else get? m a := by
|
||||
simp_to_raw using Raw₀.Const.get?_erase
|
||||
|
||||
@[simp]
|
||||
@@ -422,7 +388,7 @@ theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] :
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false h
|
||||
|
||||
theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] :
|
||||
(m.erase k).get! a = if k == a then default else m.get! a := by
|
||||
(m.erase k).get! a = bif k == a then default else m.get! a := by
|
||||
simp_to_raw using Raw₀.get!_erase
|
||||
|
||||
@[simp]
|
||||
@@ -463,7 +429,7 @@ theorem get!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
simp_to_raw using Raw₀.Const.get!_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insert k v) a = if k == a then v else get! m a := by
|
||||
get! (m.insert k v) a = bif k == a then v else get! m a := by
|
||||
simp_to_raw using Raw₀.Const.get!_insert
|
||||
|
||||
@[simp]
|
||||
@@ -480,7 +446,7 @@ theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α
|
||||
simpa [mem_iff_contains] using get!_eq_default_of_contains_eq_false h
|
||||
|
||||
theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
get! (m.erase k) a = if k == a then default else get! m a := by
|
||||
get! (m.erase k) a = bif k == a then default else get! m a := by
|
||||
simp_to_raw using Raw₀.Const.get!_erase
|
||||
|
||||
@[simp]
|
||||
@@ -547,7 +513,7 @@ theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} :
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false h
|
||||
|
||||
theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} :
|
||||
(m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback := by
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback := by
|
||||
simp_to_raw using Raw₀.getD_erase
|
||||
|
||||
@[simp]
|
||||
@@ -593,7 +559,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
simp_to_raw using Raw₀.Const.getD_of_isEmpty ⟨m, _⟩
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insert k v) a fallback = if k == a then v else getD m a fallback := by
|
||||
getD (m.insert k v) a fallback = bif k == a then v else getD m a fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_insert
|
||||
|
||||
@[simp]
|
||||
@@ -610,7 +576,7 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
simpa [mem_iff_contains] using getD_eq_fallback_of_contains_eq_false h
|
||||
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
getD (m.erase k) a fallback = if k == a then fallback else getD m a fallback := by
|
||||
getD (m.erase k) a fallback = bif k == a then fallback else getD m a fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_erase
|
||||
|
||||
@[simp]
|
||||
@@ -692,18 +658,13 @@ theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v
|
||||
simpa [mem_iff_contains] using contains_of_contains_insertIfNew' h
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insertIfNew k v).size = if k ∈ m then m.size else m.size + 1 := by
|
||||
simp only [mem_iff_contains]
|
||||
(m.insertIfNew k v).size = bif m.contains k then m.size else m.size + 1 := by
|
||||
simp_to_raw using Raw₀.size_insertIfNew
|
||||
|
||||
theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
m.size ≤ (m.insertIfNew k v).size := by
|
||||
simp_to_raw using Raw₀.size_le_size_insertIfNew ⟨m, _⟩
|
||||
|
||||
theorem size_insertIfNew_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} :
|
||||
(m.insertIfNew k v).size ≤ m.size + 1 := by
|
||||
simp_to_raw using Raw₀.size_insertIfNew_le
|
||||
|
||||
theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} :
|
||||
(m.insertIfNew k v).get? a =
|
||||
if h : k == a ∧ ¬k ∈ m then some (cast (congrArg β (eq_of_beq h.1)) v)
|
||||
@@ -736,8 +697,7 @@ namespace Const
|
||||
variable {β : Type v} {m : DHashMap.Raw α (fun _ => β)} (h : m.WF)
|
||||
|
||||
theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
get? (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then some v else get? m a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
get? (m.insertIfNew k v) a = bif k == a && !m.contains k then some v else get? m a := by
|
||||
simp_to_raw using Raw₀.Const.get?_insertIfNew
|
||||
|
||||
theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
@@ -748,14 +708,12 @@ theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h
|
||||
simp_to_raw using Raw₀.Const.get_insertIfNew ⟨m, _⟩
|
||||
|
||||
theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
get! (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then v else get! m a := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
get! (m.insertIfNew k v) a = bif k == a && !m.contains k then v else get! m a := by
|
||||
simp_to_raw using Raw₀.Const.get!_insertIfNew
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
getD (m.insertIfNew k v) a fallback =
|
||||
if k == a ∧ ¬k ∈ m then v else getD m a fallback := by
|
||||
simp only [mem_iff_contains, Bool.not_eq_true]
|
||||
bif k == a && !m.contains k then v else getD m a fallback := by
|
||||
simp_to_raw using Raw₀.Const.getD_insertIfNew
|
||||
|
||||
end Const
|
||||
|
||||
@@ -71,30 +71,6 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
@[simp] theorem not_mem_emptyc {a : α} : ¬a ∈ (∅ : HashMap α β) :=
|
||||
DHashMap.not_mem_emptyc
|
||||
|
||||
theorem contains_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → m.contains a = false :=
|
||||
DHashMap.contains_of_isEmpty
|
||||
|
||||
theorem not_mem_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → ¬a ∈ m :=
|
||||
DHashMap.not_mem_of_isEmpty
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, m.contains a = true :=
|
||||
DHashMap.isEmpty_eq_false_iff_exists_contains_eq_true
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, a ∈ m :=
|
||||
DHashMap.isEmpty_eq_false_iff_exists_mem
|
||||
|
||||
theorem isEmpty_iff_forall_contains [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, m.contains a = false :=
|
||||
DHashMap.isEmpty_iff_forall_contains
|
||||
|
||||
theorem isEmpty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, ¬a ∈ m :=
|
||||
DHashMap.isEmpty_iff_forall_not_mem
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
(m.insert k v).contains a = (k == a || m.contains a) :=
|
||||
@@ -134,17 +110,13 @@ theorem isEmpty_eq_size_eq_zero : m.isEmpty = (m.size == 0) :=
|
||||
DHashMap.isEmpty_eq_size_eq_zero
|
||||
|
||||
theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insert k v).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insert k v).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
DHashMap.size_insert
|
||||
|
||||
theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
m.size ≤ (m.insert k v).size :=
|
||||
DHashMap.size_le_size_insert
|
||||
|
||||
theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insert k v).size ≤ m.size + 1 :=
|
||||
DHashMap.size_insert_le
|
||||
|
||||
@[simp]
|
||||
theorem erase_empty {a : α} {c : Nat} : (empty c : HashMap α β).erase a = empty c :=
|
||||
ext DHashMap.erase_empty
|
||||
@@ -176,16 +148,12 @@ theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.
|
||||
DHashMap.mem_of_mem_erase
|
||||
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = if k ∈ m then m.size - 1 else m.size :=
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size :=
|
||||
DHashMap.size_erase
|
||||
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size :=
|
||||
DHashMap.size_erase_le
|
||||
|
||||
theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
m.size ≤ (m.erase k).size + 1 :=
|
||||
DHashMap.size_le_size_erase
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β} : (m.containsThenInsert k v).1 = m.contains k :=
|
||||
DHashMap.containsThenInsert_fst
|
||||
@@ -217,7 +185,7 @@ theorem getElem?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
DHashMap.Const.get?_of_isEmpty
|
||||
|
||||
theorem getElem?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
(m.insert k v)[a]? = if k == a then some v else m[a]? :=
|
||||
(m.insert k v)[a]? = bif k == a then some v else m[a]? :=
|
||||
DHashMap.Const.get?_insert
|
||||
|
||||
@[simp]
|
||||
@@ -237,7 +205,7 @@ theorem getElem?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m
|
||||
DHashMap.Const.get?_eq_none
|
||||
|
||||
theorem getElem?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k)[a]? = if k == a then none else m[a]? :=
|
||||
(m.erase k)[a]? = bif k == a then none else m[a]? :=
|
||||
DHashMap.Const.get?_erase
|
||||
|
||||
@[simp]
|
||||
@@ -283,7 +251,7 @@ theorem getElem!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a
|
||||
DHashMap.Const.get!_of_isEmpty
|
||||
|
||||
theorem getElem!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
(m.insert k v)[a]! = if k == a then v else m[a]! :=
|
||||
(m.insert k v)[a]! = bif k == a then v else m[a]! :=
|
||||
DHashMap.Const.get!_insert
|
||||
|
||||
@[simp]
|
||||
@@ -300,7 +268,7 @@ theorem getElem!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a
|
||||
DHashMap.Const.get!_eq_default
|
||||
|
||||
theorem getElem!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
(m.erase k)[a]! = if k == a then default else m[a]! :=
|
||||
(m.erase k)[a]! = bif k == a then default else m[a]! :=
|
||||
DHashMap.Const.get!_erase
|
||||
|
||||
@[simp]
|
||||
@@ -342,7 +310,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
DHashMap.Const.getD_of_isEmpty
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
(m.insert k v).getD a fallback = if k == a then v else m.getD a fallback :=
|
||||
(m.insert k v).getD a fallback = bif k == a then v else m.getD a fallback :=
|
||||
DHashMap.Const.getD_insert
|
||||
|
||||
@[simp]
|
||||
@@ -359,7 +327,7 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
DHashMap.Const.getD_eq_fallback
|
||||
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
(m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback :=
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback :=
|
||||
DHashMap.Const.getD_erase
|
||||
|
||||
@[simp]
|
||||
@@ -435,19 +403,15 @@ theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v
|
||||
DHashMap.mem_of_mem_insertIfNew'
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insertIfNew k v).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insertIfNew k v).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
DHashMap.size_insertIfNew
|
||||
|
||||
theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
m.size ≤ (m.insertIfNew k v).size :=
|
||||
DHashMap.size_le_size_insertIfNew
|
||||
|
||||
theorem size_insertIfNew_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insertIfNew k v).size ≤ m.size + 1 :=
|
||||
DHashMap.size_insertIfNew_le
|
||||
|
||||
theorem getElem?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
(m.insertIfNew k v)[a]? = if k == a ∧ ¬k ∈ m then some v else m[a]? :=
|
||||
(m.insertIfNew k v)[a]? = bif k == a && !m.contains k then some v else m[a]? :=
|
||||
DHashMap.Const.get?_insertIfNew
|
||||
|
||||
theorem getElem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
@@ -456,12 +420,12 @@ theorem getElem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β
|
||||
DHashMap.Const.get_insertIfNew (h₁ := h₁)
|
||||
|
||||
theorem getElem!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
(m.insertIfNew k v)[a]! = if k == a ∧ ¬k ∈ m then v else m[a]! :=
|
||||
(m.insertIfNew k v)[a]! = bif k == a && !m.contains k then v else m[a]! :=
|
||||
DHashMap.Const.get!_insertIfNew
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
(m.insertIfNew k v).getD a fallback =
|
||||
if k == a ∧ ¬k ∈ m then v else m.getD a fallback :=
|
||||
bif k == a && !m.contains k then v else m.getD a fallback :=
|
||||
DHashMap.Const.getD_insertIfNew
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -70,30 +70,6 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
@[simp] theorem not_mem_emptyc {a : α} : ¬a ∈ (∅ : Raw α β) :=
|
||||
DHashMap.Raw.not_mem_emptyc
|
||||
|
||||
theorem contains_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → m.contains a = false :=
|
||||
DHashMap.Raw.contains_of_isEmpty h.out
|
||||
|
||||
theorem not_mem_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → ¬a ∈ m :=
|
||||
DHashMap.Raw.not_mem_of_isEmpty h.out
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, m.contains a = true :=
|
||||
DHashMap.Raw.isEmpty_eq_false_iff_exists_contains_eq_true h.out
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, a ∈ m :=
|
||||
DHashMap.Raw.isEmpty_eq_false_iff_exists_mem h.out
|
||||
|
||||
theorem isEmpty_iff_forall_contains [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, m.contains a = false :=
|
||||
DHashMap.Raw.isEmpty_iff_forall_contains h.out
|
||||
|
||||
theorem isEmpty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, ¬a ∈ m :=
|
||||
DHashMap.Raw.isEmpty_iff_forall_not_mem h.out
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
(m.insert k v).contains a = (k == a || m.contains a) :=
|
||||
@@ -133,17 +109,13 @@ theorem isEmpty_eq_size_eq_zero : m.isEmpty = (m.size == 0) :=
|
||||
DHashMap.Raw.isEmpty_eq_size_eq_zero
|
||||
|
||||
theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insert k v).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insert k v).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
DHashMap.Raw.size_insert h.out
|
||||
|
||||
theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
m.size ≤ (m.insert k v).size :=
|
||||
DHashMap.Raw.size_le_size_insert h.out
|
||||
|
||||
theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insert k v).size ≤ m.size + 1 :=
|
||||
DHashMap.Raw.size_insert_le h.out
|
||||
|
||||
@[simp]
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw α β).erase k = empty c :=
|
||||
ext DHashMap.Raw.erase_empty
|
||||
@@ -175,16 +147,12 @@ theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.
|
||||
DHashMap.Raw.mem_of_mem_erase h.out
|
||||
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = if k ∈ m then m.size - 1 else m.size :=
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size :=
|
||||
DHashMap.Raw.size_erase h.out
|
||||
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size :=
|
||||
DHashMap.Raw.size_erase_le h.out
|
||||
|
||||
theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
m.size ≤ (m.erase k).size + 1 :=
|
||||
DHashMap.Raw.size_le_size_erase h.out
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} {v : β} : (m.containsThenInsert k v).1 = m.contains k :=
|
||||
DHashMap.Raw.containsThenInsert_fst h.out
|
||||
@@ -216,7 +184,7 @@ theorem getElem?_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
DHashMap.Raw.Const.get?_of_isEmpty h.out
|
||||
|
||||
theorem getElem?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
(m.insert k v)[a]? = if k == a then some v else m[a]? :=
|
||||
(m.insert k v)[a]? = bif k == a then some v else m[a]? :=
|
||||
DHashMap.Raw.Const.get?_insert h.out
|
||||
|
||||
@[simp]
|
||||
@@ -236,7 +204,7 @@ theorem getElem?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m
|
||||
DHashMap.Raw.Const.get?_eq_none h.out
|
||||
|
||||
theorem getElem?_erase [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.erase k)[a]? = if k == a then none else m[a]? :=
|
||||
(m.erase k)[a]? = bif k == a then none else m[a]? :=
|
||||
DHashMap.Raw.Const.get?_erase h.out
|
||||
|
||||
@[simp]
|
||||
@@ -282,7 +250,7 @@ theorem getElem!_of_isEmpty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a
|
||||
DHashMap.Raw.Const.get!_of_isEmpty h.out
|
||||
|
||||
theorem getElem!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
(m.insert k v)[a]! = if k == a then v else m[a]! :=
|
||||
(m.insert k v)[a]! = bif k == a then v else m[a]! :=
|
||||
DHashMap.Raw.Const.get!_insert h.out
|
||||
|
||||
@[simp]
|
||||
@@ -299,7 +267,7 @@ theorem getElem!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a
|
||||
DHashMap.Raw.Const.get!_eq_default h.out
|
||||
|
||||
theorem getElem!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} :
|
||||
(m.erase k)[a]! = if k == a then default else m[a]! :=
|
||||
(m.erase k)[a]! = bif k == a then default else m[a]! :=
|
||||
DHashMap.Raw.Const.get!_erase h.out
|
||||
|
||||
@[simp]
|
||||
@@ -340,7 +308,7 @@ theorem getD_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
DHashMap.Raw.Const.getD_of_isEmpty h.out
|
||||
|
||||
theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
(m.insert k v).getD a fallback = if k == a then v else m.getD a fallback :=
|
||||
(m.insert k v).getD a fallback = bif k == a then v else m.getD a fallback :=
|
||||
DHashMap.Raw.Const.getD_insert h.out
|
||||
|
||||
@[simp]
|
||||
@@ -357,7 +325,7 @@ theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback :
|
||||
DHashMap.Raw.Const.getD_eq_fallback h.out
|
||||
|
||||
theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} :
|
||||
(m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback :=
|
||||
(m.erase k).getD a fallback = bif k == a then fallback else m.getD a fallback :=
|
||||
DHashMap.Raw.Const.getD_erase h.out
|
||||
|
||||
@[simp]
|
||||
@@ -433,19 +401,15 @@ theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v
|
||||
DHashMap.Raw.mem_of_mem_insertIfNew' h.out
|
||||
|
||||
theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insertIfNew k v).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insertIfNew k v).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
DHashMap.Raw.size_insertIfNew h.out
|
||||
|
||||
theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
m.size ≤ (m.insertIfNew k v).size :=
|
||||
DHashMap.Raw.size_le_size_insertIfNew h.out
|
||||
|
||||
theorem size_insertIfNew_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β} :
|
||||
(m.insertIfNew k v).size ≤ m.size + 1 :=
|
||||
DHashMap.Raw.size_insertIfNew_le h.out
|
||||
|
||||
theorem getElem?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} :
|
||||
(m.insertIfNew k v)[a]? = if k == a ∧ ¬k ∈ m then some v else m[a]? :=
|
||||
(m.insertIfNew k v)[a]? = bif k == a && !m.contains k then some v else m[a]? :=
|
||||
DHashMap.Raw.Const.get?_insertIfNew h.out
|
||||
|
||||
theorem getElem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} :
|
||||
@@ -454,12 +418,12 @@ theorem getElem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β
|
||||
DHashMap.Raw.Const.get_insertIfNew h.out (h₁ := h₁)
|
||||
|
||||
theorem getElem!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} :
|
||||
(m.insertIfNew k v)[a]! = if k == a ∧ ¬k ∈ m then v else m[a]! :=
|
||||
(m.insertIfNew k v)[a]! = bif k == a && !m.contains k then v else m[a]! :=
|
||||
DHashMap.Raw.Const.get!_insertIfNew h.out
|
||||
|
||||
theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} :
|
||||
(m.insertIfNew k v).getD a fallback =
|
||||
if k == a ∧ ¬k ∈ m then v else m.getD a fallback :=
|
||||
bif k == a && !m.contains k then v else m.getD a fallback :=
|
||||
DHashMap.Raw.Const.getD_insertIfNew h.out
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -65,30 +65,6 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
@[simp] theorem not_mem_emptyc {a : α} : ¬a ∈ (∅ : HashSet α) :=
|
||||
HashMap.not_mem_emptyc
|
||||
|
||||
theorem contains_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → m.contains a = false :=
|
||||
HashMap.contains_of_isEmpty
|
||||
|
||||
theorem not_mem_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → ¬a ∈ m :=
|
||||
HashMap.not_mem_of_isEmpty
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, m.contains a = true :=
|
||||
HashMap.isEmpty_eq_false_iff_exists_contains_eq_true
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, a ∈ m :=
|
||||
HashMap.isEmpty_eq_false_iff_exists_mem
|
||||
|
||||
theorem isEmpty_iff_forall_contains [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, m.contains a = false :=
|
||||
HashMap.isEmpty_iff_forall_contains
|
||||
|
||||
theorem isEmpty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, ¬a ∈ m :=
|
||||
HashMap.isEmpty_iff_forall_not_mem
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.insert k).contains a = (k == a || m.contains a) :=
|
||||
@@ -126,16 +102,12 @@ theorem isEmpty_eq_size_eq_zero : m.isEmpty = (m.size == 0) :=
|
||||
HashMap.isEmpty_eq_size_eq_zero
|
||||
|
||||
theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.insert k).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insert k).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
HashMap.size_insertIfNew
|
||||
|
||||
theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} : m.size ≤ (m.insert k).size :=
|
||||
HashMap.size_le_size_insertIfNew
|
||||
|
||||
theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.insert k).size ≤ m.size + 1 :=
|
||||
HashMap.size_insertIfNew_le
|
||||
|
||||
@[simp]
|
||||
theorem erase_empty {a : α} {c : Nat} : (empty c : HashSet α).erase a = empty c :=
|
||||
ext HashMap.erase_empty
|
||||
@@ -167,16 +139,12 @@ theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.
|
||||
HashMap.mem_of_mem_erase
|
||||
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = if k ∈ m then m.size - 1 else m.size :=
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size :=
|
||||
HashMap.size_erase
|
||||
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size :=
|
||||
HashMap.size_erase_le
|
||||
|
||||
theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
m.size ≤ (m.erase k).size + 1 :=
|
||||
HashMap.size_le_size_erase
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} : (m.containsThenInsert k).1 = m.contains k :=
|
||||
HashMap.containsThenInsertIfNew_fst
|
||||
|
||||
@@ -65,30 +65,6 @@ theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) :
|
||||
@[simp] theorem not_mem_emptyc {a : α} : ¬a ∈ (∅ : Raw α) :=
|
||||
HashMap.Raw.not_mem_emptyc
|
||||
|
||||
theorem contains_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → m.contains a = false :=
|
||||
HashMap.Raw.contains_of_isEmpty h.out
|
||||
|
||||
theorem not_mem_of_isEmpty [EquivBEq α] [LawfulHashable α] {a : α} :
|
||||
m.isEmpty → ¬a ∈ m :=
|
||||
HashMap.Raw.not_mem_of_isEmpty h.out
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_contains_eq_true [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, m.contains a = true :=
|
||||
HashMap.Raw.isEmpty_eq_false_iff_exists_contains_eq_true h.out
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = false ↔ ∃ a, a ∈ m :=
|
||||
HashMap.Raw.isEmpty_eq_false_iff_exists_mem h.out
|
||||
|
||||
theorem isEmpty_iff_forall_contains [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, m.contains a = false :=
|
||||
HashMap.Raw.isEmpty_iff_forall_contains h.out
|
||||
|
||||
theorem isEmpty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] :
|
||||
m.isEmpty = true ↔ ∀ a, ¬a ∈ m :=
|
||||
HashMap.Raw.isEmpty_iff_forall_not_mem h.out
|
||||
|
||||
@[simp]
|
||||
theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} :
|
||||
(m.insert k).contains a = (k == a || m.contains a) :=
|
||||
@@ -126,15 +102,12 @@ theorem isEmpty_eq_size_eq_zero : m.isEmpty = (m.size == 0) :=
|
||||
HashMap.Raw.isEmpty_eq_size_eq_zero
|
||||
|
||||
theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.insert k).size = if k ∈ m then m.size else m.size + 1 :=
|
||||
(m.insert k).size = bif m.contains k then m.size else m.size + 1 :=
|
||||
HashMap.Raw.size_insertIfNew h.out
|
||||
|
||||
theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} : m.size ≤ (m.insert k).size :=
|
||||
HashMap.Raw.size_le_size_insertIfNew h.out
|
||||
|
||||
theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.insert k).size ≤ m.size + 1 :=
|
||||
HashMap.Raw.size_insertIfNew_le h.out
|
||||
|
||||
@[simp]
|
||||
theorem erase_empty {k : α} {c : Nat} : (empty c : Raw α).erase k = empty c :=
|
||||
ext HashMap.Raw.erase_empty
|
||||
@@ -166,16 +139,12 @@ theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.
|
||||
HashMap.Raw.mem_of_mem_erase h.out
|
||||
|
||||
theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
(m.erase k).size = if k ∈ m then m.size - 1 else m.size :=
|
||||
(m.erase k).size = bif m.contains k then m.size - 1 else m.size :=
|
||||
HashMap.Raw.size_erase h.out
|
||||
|
||||
theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size :=
|
||||
HashMap.Raw.size_erase_le h.out
|
||||
|
||||
theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} :
|
||||
m.size ≤ (m.erase k).size + 1 :=
|
||||
HashMap.Raw.size_le_size_erase h.out
|
||||
|
||||
@[simp]
|
||||
theorem containsThenInsert_fst {k : α} : (m.containsThenInsert k).1 = m.contains k :=
|
||||
HashMap.Raw.containsThenInsertIfNew_fst h.out
|
||||
|
||||
41
src/kernel/cache_stack.h
Normal file
41
src/kernel/cache_stack.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Leonardo de Moura
|
||||
*/
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "runtime/debug.h"
|
||||
|
||||
/** \brief Macro for creating a stack of objects of type Cache in thread local storage.
|
||||
The argument \c Arg is provided to every new instance of Cache.
|
||||
The macro provides the helper class Cache_ref that "reuses" cache objects from the stack.
|
||||
*/
|
||||
#define MK_CACHE_STACK(Cache, Arg) \
|
||||
struct Cache ## _stack { \
|
||||
unsigned m_top; \
|
||||
std::vector<std::unique_ptr<Cache>> m_cache_stack; \
|
||||
Cache ## _stack():m_top(0) {} \
|
||||
}; \
|
||||
MK_THREAD_LOCAL_GET_DEF(Cache ## _stack, get_ ## Cache ## _stack); \
|
||||
class Cache ## _ref { \
|
||||
Cache * m_cache; \
|
||||
public: \
|
||||
Cache ## _ref() { \
|
||||
Cache ## _stack & s = get_ ## Cache ## _stack(); \
|
||||
lean_assert(s.m_top <= s.m_cache_stack.size()); \
|
||||
if (s.m_top == s.m_cache_stack.size()) \
|
||||
s.m_cache_stack.push_back(std::unique_ptr<Cache>(new Cache(Arg))); \
|
||||
m_cache = s.m_cache_stack[s.m_top].get(); \
|
||||
s.m_top++; \
|
||||
} \
|
||||
~Cache ## _ref() { \
|
||||
Cache ## _stack & s = get_ ## Cache ## _stack(); \
|
||||
lean_assert(s.m_top > 0); \
|
||||
s.m_top--; \
|
||||
m_cache->clear(); \
|
||||
} \
|
||||
Cache * operator->() const { return m_cache; } \
|
||||
};
|
||||
@@ -161,7 +161,7 @@ bool declaration::is_unsafe() const {
|
||||
|
||||
bool use_unsafe(environment const & env, expr const & e) {
|
||||
bool found = false;
|
||||
for_each(e, [&](expr const & e) {
|
||||
for_each(e, [&](expr const & e, unsigned) {
|
||||
if (found) return false;
|
||||
if (is_constant(e)) {
|
||||
if (auto info = env.find(const_name(e))) {
|
||||
@@ -181,7 +181,7 @@ declaration::declaration():declaration(*g_dummy) {}
|
||||
|
||||
static unsigned get_max_height(environment const & env, expr const & v) {
|
||||
unsigned h = 0;
|
||||
for_each(v, [&](expr const & e) {
|
||||
for_each(v, [&](expr const & e, unsigned) {
|
||||
if (is_constant(e)) {
|
||||
auto d = env.find(const_name(e));
|
||||
if (d && d->get_hints().get_height() > h)
|
||||
|
||||
@@ -498,7 +498,7 @@ optional<expr> has_expr_metavar_strict(expr const & e) {
|
||||
if (!has_expr_metavar(e))
|
||||
return none_expr();
|
||||
optional<expr> r;
|
||||
for_each(e, [&](expr const & e) {
|
||||
for_each(e, [&](expr const & e, unsigned) {
|
||||
if (r || !has_expr_metavar(e)) return false;
|
||||
if (is_metavar_app(e)) { r = e; return false; }
|
||||
return true;
|
||||
|
||||
@@ -5,221 +5,119 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Author: Leonardo de Moura
|
||||
*/
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include "runtime/memory.h"
|
||||
#include "runtime/interrupt.h"
|
||||
#include "runtime/flet.h"
|
||||
#include "kernel/for_each_fn.h"
|
||||
#include "kernel/cache_stack.h"
|
||||
|
||||
#ifndef LEAN_DEFAULT_FOR_EACH_CACHE_CAPACITY
|
||||
#define LEAN_DEFAULT_FOR_EACH_CACHE_CAPACITY 1024*8
|
||||
#endif
|
||||
|
||||
namespace lean {
|
||||
|
||||
/*
|
||||
If `partial_apps = true`, then given a term `g a b`, we also apply the function `m_f` to `g a`,
|
||||
and not only to `g`, `a`, and `b`.
|
||||
*/
|
||||
template<bool partial_apps> class for_each_fn {
|
||||
std::unordered_set<lean_object *> m_cache;
|
||||
std::function<bool(expr const &)> m_f; // NOLINT
|
||||
|
||||
bool visited(expr const & e) {
|
||||
if (!is_shared(e)) return false;
|
||||
if (m_cache.find(e.raw()) != m_cache.end()) return true;
|
||||
m_cache.insert(e.raw());
|
||||
return false;
|
||||
}
|
||||
|
||||
void apply_fn(expr const & e) {
|
||||
if (is_app(e)) {
|
||||
apply_fn(app_fn(e));
|
||||
apply(app_arg(e));
|
||||
} else {
|
||||
apply(e);
|
||||
}
|
||||
}
|
||||
|
||||
void apply(expr const & e) {
|
||||
switch (e.kind()) {
|
||||
case expr_kind::Const: case expr_kind::BVar: case expr_kind::Sort:
|
||||
m_f(e);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (visited(e))
|
||||
return;
|
||||
|
||||
if (!m_f(e))
|
||||
return;
|
||||
|
||||
switch (e.kind()) {
|
||||
case expr_kind::Const: case expr_kind::BVar:
|
||||
case expr_kind::Sort: case expr_kind::Lit:
|
||||
case expr_kind::MVar: case expr_kind::FVar:
|
||||
return;
|
||||
case expr_kind::MData:
|
||||
apply(mdata_expr(e));
|
||||
return;
|
||||
case expr_kind::Proj:
|
||||
apply(proj_expr(e));
|
||||
return;
|
||||
case expr_kind::App:
|
||||
if (partial_apps)
|
||||
apply(app_fn(e));
|
||||
else
|
||||
apply_fn(app_fn(e));
|
||||
apply(app_arg(e));
|
||||
return;
|
||||
case expr_kind::Lambda: case expr_kind::Pi:
|
||||
apply(binding_domain(e));
|
||||
apply(binding_body(e));
|
||||
return;
|
||||
case expr_kind::Let:
|
||||
apply(let_type(e));
|
||||
apply(let_value(e));
|
||||
apply(let_body(e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
for_each_fn(std::function<bool(expr const &)> && f):m_f(f) {} // NOLINT
|
||||
for_each_fn(std::function<bool(expr const &)> const & f):m_f(f) {} // NOLINT
|
||||
void operator()(expr const & e) { apply(e); }
|
||||
};
|
||||
|
||||
class for_each_offset_fn {
|
||||
struct key_hasher {
|
||||
std::size_t operator()(std::pair<lean_object *, unsigned> const & p) const {
|
||||
return hash((size_t)p.first, p.second);
|
||||
}
|
||||
struct for_each_cache {
|
||||
struct entry {
|
||||
object const * m_cell;
|
||||
unsigned m_offset;
|
||||
entry():m_cell(nullptr) {}
|
||||
};
|
||||
std::unordered_set<std::pair<lean_object *, unsigned>, key_hasher> m_cache;
|
||||
std::function<bool(expr const &, unsigned)> m_f; // NOLINT
|
||||
unsigned m_capacity;
|
||||
std::vector<entry> m_cache;
|
||||
std::vector<unsigned> m_used;
|
||||
for_each_cache(unsigned c):m_capacity(c), m_cache(c) {}
|
||||
|
||||
bool visited(expr const & e, unsigned offset) {
|
||||
if (!is_shared(e)) return false;
|
||||
if (m_cache.find(std::make_pair(e.raw(), offset)) != m_cache.end()) return true;
|
||||
m_cache.insert(std::make_pair(e.raw(), offset));
|
||||
return false;
|
||||
unsigned i = hash(hash(e), offset) % m_capacity;
|
||||
if (m_cache[i].m_cell == e.raw() && m_cache[i].m_offset == offset) {
|
||||
return true;
|
||||
} else {
|
||||
if (m_cache[i].m_cell == nullptr)
|
||||
m_used.push_back(i);
|
||||
m_cache[i].m_cell = e.raw();
|
||||
m_cache[i].m_offset = offset;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (unsigned i : m_used)
|
||||
m_cache[i].m_cell = nullptr;
|
||||
m_used.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/* CACHE_RESET: NO */
|
||||
MK_CACHE_STACK(for_each_cache, LEAN_DEFAULT_FOR_EACH_CACHE_CAPACITY)
|
||||
|
||||
class for_each_fn {
|
||||
for_each_cache_ref m_cache;
|
||||
std::function<bool(expr const &, unsigned)> m_f; // NOLINT
|
||||
|
||||
void apply(expr const & e, unsigned offset) {
|
||||
switch (e.kind()) {
|
||||
case expr_kind::Const: case expr_kind::BVar: case expr_kind::Sort:
|
||||
m_f(e, offset);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
buffer<pair<expr const &, unsigned>> todo;
|
||||
todo.emplace_back(e, offset);
|
||||
while (true) {
|
||||
begin_loop:
|
||||
if (todo.empty())
|
||||
break;
|
||||
check_memory("expression traversal");
|
||||
auto p = todo.back();
|
||||
todo.pop_back();
|
||||
expr const & e = p.first;
|
||||
unsigned offset = p.second;
|
||||
|
||||
if (visited(e, offset))
|
||||
return;
|
||||
switch (e.kind()) {
|
||||
case expr_kind::Const: case expr_kind::BVar:
|
||||
case expr_kind::Sort:
|
||||
m_f(e, offset);
|
||||
goto begin_loop;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_f(e, offset))
|
||||
return;
|
||||
if (is_shared(e) && m_cache->visited(e, offset))
|
||||
goto begin_loop;
|
||||
|
||||
switch (e.kind()) {
|
||||
case expr_kind::Const: case expr_kind::BVar:
|
||||
case expr_kind::Sort: case expr_kind::Lit:
|
||||
case expr_kind::MVar: case expr_kind::FVar:
|
||||
return;
|
||||
case expr_kind::MData:
|
||||
apply(mdata_expr(e), offset);
|
||||
return;
|
||||
case expr_kind::Proj:
|
||||
apply(proj_expr(e), offset);
|
||||
return;
|
||||
case expr_kind::App:
|
||||
apply(app_fn(e), offset);
|
||||
apply(app_arg(e), offset);
|
||||
return;
|
||||
case expr_kind::Lambda: case expr_kind::Pi:
|
||||
apply(binding_domain(e), offset);
|
||||
apply(binding_body(e), offset+1);
|
||||
return;
|
||||
case expr_kind::Let:
|
||||
apply(let_type(e), offset);
|
||||
apply(let_value(e), offset);
|
||||
apply(let_body(e), offset+1);
|
||||
return;
|
||||
if (!m_f(e, offset))
|
||||
goto begin_loop;
|
||||
|
||||
switch (e.kind()) {
|
||||
case expr_kind::Const: case expr_kind::BVar:
|
||||
case expr_kind::Sort: case expr_kind::Lit:
|
||||
case expr_kind::MVar: case expr_kind::FVar:
|
||||
goto begin_loop;
|
||||
case expr_kind::MData:
|
||||
todo.emplace_back(mdata_expr(e), offset);
|
||||
goto begin_loop;
|
||||
case expr_kind::Proj:
|
||||
todo.emplace_back(proj_expr(e), offset);
|
||||
goto begin_loop;
|
||||
case expr_kind::App:
|
||||
todo.emplace_back(app_arg(e), offset);
|
||||
todo.emplace_back(app_fn(e), offset);
|
||||
goto begin_loop;
|
||||
case expr_kind::Lambda: case expr_kind::Pi:
|
||||
todo.emplace_back(binding_body(e), offset + 1);
|
||||
todo.emplace_back(binding_domain(e), offset);
|
||||
goto begin_loop;
|
||||
case expr_kind::Let:
|
||||
todo.emplace_back(let_body(e), offset + 1);
|
||||
todo.emplace_back(let_value(e), offset);
|
||||
todo.emplace_back(let_type(e), offset);
|
||||
goto begin_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
for_each_offset_fn(std::function<bool(expr const &, unsigned)> && f):m_f(f) {} // NOLINT
|
||||
for_each_offset_fn(std::function<bool(expr const &, unsigned)> const & f):m_f(f) {} // NOLINT
|
||||
for_each_fn(std::function<bool(expr const &, unsigned)> && f):m_f(f) {} // NOLINT
|
||||
for_each_fn(std::function<bool(expr const &, unsigned)> const & f):m_f(f) {} // NOLINT
|
||||
void operator()(expr const & e) { apply(e, 0); }
|
||||
};
|
||||
|
||||
void for_each(expr const & e, std::function<bool(expr const &)> && f) { // NOLINT
|
||||
return for_each_fn<true>(f)(e);
|
||||
}
|
||||
|
||||
void for_each(expr const & e, std::function<bool(expr const &, unsigned)> && f) { // NOLINT
|
||||
return for_each_offset_fn(f)(e);
|
||||
}
|
||||
|
||||
extern "C" LEAN_EXPORT obj_res lean_find_expr(b_obj_arg p, b_obj_arg e_) {
|
||||
lean_object * found = nullptr;
|
||||
expr const & e = TO_REF(expr, e_);
|
||||
for_each_fn<true>([&](expr const & e) {
|
||||
if (found != nullptr) return false;
|
||||
lean_inc(p);
|
||||
lean_inc(e.raw());
|
||||
if (lean_unbox(lean_apply_1(p, e.raw()))) {
|
||||
found = e.raw();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})(e);
|
||||
if (found) {
|
||||
lean_inc(found);
|
||||
lean_object * r = lean_alloc_ctor(1, 1, 0);
|
||||
lean_ctor_set(r, 0, found);
|
||||
return r;
|
||||
} else {
|
||||
return lean_box(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Similar to `lean_find_expr`, but `p` returns
|
||||
```
|
||||
inductive FindStep where
|
||||
/-- Found desired subterm -/ | found
|
||||
/-- Search subterms -/ | visit
|
||||
/-- Do not search subterms -/ | done
|
||||
```
|
||||
*/
|
||||
extern "C" LEAN_EXPORT obj_res lean_find_ext_expr(b_obj_arg p, b_obj_arg e_) {
|
||||
lean_object * found = nullptr;
|
||||
expr const & e = TO_REF(expr, e_);
|
||||
// Recall that `findExt?` skips partial applications.
|
||||
for_each_fn<false>([&](expr const & e) {
|
||||
if (found != nullptr) return false;
|
||||
lean_inc(p);
|
||||
lean_inc(e.raw());
|
||||
switch(lean_unbox(lean_apply_1(p, e.raw()))) {
|
||||
case 0: // found
|
||||
found = e.raw();
|
||||
return false;
|
||||
case 1: // visit
|
||||
return true;
|
||||
case 2: // done
|
||||
return false;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
})(e);
|
||||
if (found) {
|
||||
lean_inc(found);
|
||||
lean_object * r = lean_alloc_ctor(1, 1, 0);
|
||||
lean_ctor_set(r, 0, found);
|
||||
return r;
|
||||
} else {
|
||||
return lean_box(0);
|
||||
}
|
||||
return for_each_fn(f)(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,18 +13,15 @@ Author: Leonardo de Moura
|
||||
#include "kernel/expr_sets.h"
|
||||
|
||||
namespace lean {
|
||||
/**
|
||||
\brief Expression visitor.
|
||||
/** \brief Expression visitor.
|
||||
|
||||
The argument \c f must be a lambda (function object) containing the method
|
||||
The argument \c f must be a lambda (function object) containing the method
|
||||
|
||||
<code>
|
||||
bool operator()(expr const & e, unsigned offset)
|
||||
</code>
|
||||
<code>
|
||||
bool operator()(expr const & e, unsigned offset)
|
||||
</code>
|
||||
|
||||
The \c offset is the number of binders under which \c e occurs.
|
||||
The \c offset is the number of binders under which \c e occurs.
|
||||
*/
|
||||
void for_each(expr const & e, std::function<bool(expr const &, unsigned)> && f); // NOLINT
|
||||
|
||||
void for_each(expr const & e, std::function<bool(expr const &)> && f); // NOLINT
|
||||
}
|
||||
|
||||
@@ -6,35 +6,75 @@ Author: Leonardo de Moura
|
||||
*/
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "kernel/replace_fn.h"
|
||||
#include "kernel/cache_stack.h"
|
||||
|
||||
#ifndef LEAN_DEFAULT_REPLACE_CACHE_CAPACITY
|
||||
#define LEAN_DEFAULT_REPLACE_CACHE_CAPACITY 1024*8
|
||||
#endif
|
||||
|
||||
namespace lean {
|
||||
struct replace_cache {
|
||||
struct entry {
|
||||
object * m_cell;
|
||||
unsigned m_offset;
|
||||
expr m_result;
|
||||
entry():m_cell(nullptr) {}
|
||||
};
|
||||
unsigned m_capacity;
|
||||
std::vector<entry> m_cache;
|
||||
std::vector<unsigned> m_used;
|
||||
replace_cache(unsigned c):m_capacity(c), m_cache(c) {}
|
||||
|
||||
expr * find(expr const & e, unsigned offset) {
|
||||
unsigned i = hash(hash(e), offset) % m_capacity;
|
||||
if (m_cache[i].m_cell == e.raw() && m_cache[i].m_offset == offset)
|
||||
return &m_cache[i].m_result;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void insert(expr const & e, unsigned offset, expr const & v) {
|
||||
unsigned i = hash(hash(e), offset) % m_capacity;
|
||||
if (m_cache[i].m_cell == nullptr)
|
||||
m_used.push_back(i);
|
||||
m_cache[i].m_cell = e.raw();
|
||||
m_cache[i].m_offset = offset;
|
||||
m_cache[i].m_result = v;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (unsigned i : m_used) {
|
||||
m_cache[i].m_cell = nullptr;
|
||||
m_cache[i].m_result = expr();
|
||||
}
|
||||
m_used.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/* CACHE_RESET: NO */
|
||||
MK_CACHE_STACK(replace_cache, LEAN_DEFAULT_REPLACE_CACHE_CAPACITY)
|
||||
|
||||
class replace_rec_fn {
|
||||
struct key_hasher {
|
||||
std::size_t operator()(std::pair<lean_object *, unsigned> const & p) const {
|
||||
return hash((size_t)p.first, p.second);
|
||||
}
|
||||
};
|
||||
std::unordered_map<std::pair<lean_object *, unsigned>, expr, key_hasher> m_cache;
|
||||
replace_cache_ref m_cache;
|
||||
std::function<optional<expr>(expr const &, unsigned)> m_f;
|
||||
bool m_use_cache;
|
||||
|
||||
expr save_result(expr const & e, unsigned offset, expr const & r, bool shared) {
|
||||
if (shared)
|
||||
m_cache.insert(mk_pair(mk_pair(e.raw(), offset), r));
|
||||
m_cache->insert(e, offset, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
expr apply(expr const & e, unsigned offset) {
|
||||
bool shared = false;
|
||||
if (m_use_cache && is_shared(e)) {
|
||||
auto it = m_cache.find(mk_pair(e.raw(), offset));
|
||||
if (it != m_cache.end())
|
||||
return it->second;
|
||||
if (auto r = m_cache->find(e, offset))
|
||||
return *r;
|
||||
shared = true;
|
||||
}
|
||||
check_system("replace");
|
||||
|
||||
if (optional<expr> r = m_f(e, offset)) {
|
||||
return save_result(e, offset, *r, shared);
|
||||
} else {
|
||||
@@ -81,73 +121,4 @@ public:
|
||||
expr replace(expr const & e, std::function<optional<expr>(expr const &, unsigned)> const & f, bool use_cache) {
|
||||
return replace_rec_fn(f, use_cache)(e);
|
||||
}
|
||||
|
||||
class replace_fn {
|
||||
std::unordered_map<lean_object *, expr> m_cache;
|
||||
lean_object * m_f;
|
||||
|
||||
expr save_result(expr const & e, expr const & r, bool shared) {
|
||||
if (shared)
|
||||
m_cache.insert(mk_pair(e.raw(), r));
|
||||
return r;
|
||||
}
|
||||
|
||||
expr apply(expr const & e) {
|
||||
bool shared = false;
|
||||
if (is_shared(e)) {
|
||||
auto it = m_cache.find(e.raw());
|
||||
if (it != m_cache.end())
|
||||
return it->second;
|
||||
shared = true;
|
||||
}
|
||||
|
||||
lean_inc(e.raw());
|
||||
lean_inc_ref(m_f);
|
||||
lean_object * r = lean_apply_1(m_f, e.raw());
|
||||
if (!lean_is_scalar(r)) {
|
||||
expr e_new(lean_ctor_get(r, 0), true);
|
||||
lean_dec_ref(r);
|
||||
return save_result(e, e_new, shared);
|
||||
}
|
||||
|
||||
switch (e.kind()) {
|
||||
case expr_kind::Const: case expr_kind::Sort:
|
||||
case expr_kind::BVar: case expr_kind::Lit:
|
||||
case expr_kind::MVar: case expr_kind::FVar:
|
||||
return save_result(e, e, shared);
|
||||
case expr_kind::MData: {
|
||||
expr new_e = apply(mdata_expr(e));
|
||||
return save_result(e, update_mdata(e, new_e), shared);
|
||||
}
|
||||
case expr_kind::Proj: {
|
||||
expr new_e = apply(proj_expr(e));
|
||||
return save_result(e, update_proj(e, new_e), shared);
|
||||
}
|
||||
case expr_kind::App: {
|
||||
expr new_f = apply(app_fn(e));
|
||||
expr new_a = apply(app_arg(e));
|
||||
return save_result(e, update_app(e, new_f, new_a), shared);
|
||||
}
|
||||
case expr_kind::Pi: case expr_kind::Lambda: {
|
||||
expr new_d = apply(binding_domain(e));
|
||||
expr new_b = apply(binding_body(e));
|
||||
return save_result(e, update_binding(e, new_d, new_b), shared);
|
||||
}
|
||||
case expr_kind::Let: {
|
||||
expr new_t = apply(let_type(e));
|
||||
expr new_v = apply(let_value(e));
|
||||
expr new_b = apply(let_body(e));
|
||||
return save_result(e, update_let(e, new_t, new_v, new_b), shared);
|
||||
}}
|
||||
lean_unreachable();
|
||||
}
|
||||
public:
|
||||
replace_fn(lean_object * f):m_f(f) {}
|
||||
expr operator()(expr const & e) { return apply(e); }
|
||||
};
|
||||
|
||||
extern "C" LEAN_EXPORT obj_res lean_replace_expr(b_obj_arg f, b_obj_arg e) {
|
||||
expr r = replace_fn(f)(TO_REF(expr, e));
|
||||
return r.steal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ expr type_checker::infer_app(expr const & e, bool infer_only) {
|
||||
|
||||
static void mark_used(unsigned n, expr const * fvars, expr const & b, bool * used) {
|
||||
if (!has_fvar(b)) return;
|
||||
for_each(b, [&](expr const & x) {
|
||||
for_each(b, [&](expr const & x, unsigned) {
|
||||
if (!has_fvar(x)) return false;
|
||||
if (is_fvar(x)) {
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
|
||||
@@ -30,8 +30,6 @@ structure BuildConfig where
|
||||
dependent jobs will still continue unimpeded).
|
||||
-/
|
||||
failLv : LogLevel := .error
|
||||
/-- The minimum log level for an log entry to be reported. -/
|
||||
outLv : LogLevel := verbosity.minLogLv
|
||||
/--
|
||||
The stream to which Lake reports build progress.
|
||||
By default, Lake uses `stderr`.
|
||||
@@ -40,6 +38,10 @@ structure BuildConfig where
|
||||
/-- Whether to use ANSI escape codes in build output. -/
|
||||
ansiMode : AnsiMode := .auto
|
||||
|
||||
/-- The minimum log level for an log entry to be reported. -/
|
||||
@[inline] def BuildConfig.outLv (cfg : BuildConfig) : LogLevel :=
|
||||
cfg.verbosity.minLogLv
|
||||
|
||||
/--
|
||||
Whether the build should show progress information.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ In this section, we define the primitives that make up a builder.
|
||||
A dependently typed monadic *fetch* function.
|
||||
|
||||
That is, a function within the monad `m` and takes an input `a : α`
|
||||
describing what to fetch and produces some output `b : β a` (dependently
|
||||
describing what to fetch and and produces some output `b : β a` (dependently
|
||||
typed) or `b : B` (not) describing what was fetched. All build functions are
|
||||
fetch functions, but not all fetch functions need build something.
|
||||
-/
|
||||
|
||||
@@ -13,7 +13,6 @@ inductive CliError
|
||||
| unknownCommand (cmd : String)
|
||||
| missingArg (arg : String)
|
||||
| missingOptArg (opt arg : String)
|
||||
| invalidOptArg (opt arg : String)
|
||||
| unknownShortOption (opt : Char)
|
||||
| unknownLongOption (opt : String)
|
||||
| unexpectedArguments (args : List String)
|
||||
@@ -52,8 +51,7 @@ def toString : CliError → String
|
||||
| missingCommand => "missing command"
|
||||
| unknownCommand cmd => s!"unknown command '{cmd}'"
|
||||
| missingArg arg => s!"missing {arg}"
|
||||
| missingOptArg opt arg => s!"missing {arg} for {opt}"
|
||||
| invalidOptArg opt arg => s!"invalid argument for {opt}; expected {arg}"
|
||||
| missingOptArg opt arg => s!"missing {arg} after {opt}"
|
||||
| unknownShortOption opt => s!"unknown short option '-{opt}'"
|
||||
| unknownLongOption opt => s!"unknown long option '{opt}'"
|
||||
| unexpectedArguments as => s!"unexpected arguments: {" ".intercalate as}"
|
||||
|
||||
@@ -35,31 +35,23 @@ COMMANDS:
|
||||
translate-config change language of the package configuration
|
||||
serve start the Lean language server
|
||||
|
||||
BASIC OPTIONS:
|
||||
OPTIONS:
|
||||
--version print version and exit
|
||||
--help, -h print help of the program or a command and exit
|
||||
--dir, -d=file use the package configuration in a specific directory
|
||||
--file, -f=file use a specific file for the package configuration
|
||||
--quiet, -q hide progress messages
|
||||
--verbose, -v show verbose information (command invocations)
|
||||
--lean=cmd specify the `lean` command used by Lake
|
||||
-K key[=value] set the configuration file option named key
|
||||
--old only rebuild modified modules (ignore transitive deps)
|
||||
--rehash, -H hash all files for traces (do not trust `.hash` files)
|
||||
--update, -U update manifest before building
|
||||
--reconfigure, -R elaborate configuration files instead of using OLeans
|
||||
--no-build exit immediately if a build target is not up-to-date
|
||||
|
||||
OUTPUT OPTIONS:
|
||||
--quiet, -q hide informational logs and the progress indicator
|
||||
--verbose, -v show trace logs (command invocations) and built targets
|
||||
--ansi, --no-ansi toggle the use of ANSI escape codes to prettify output
|
||||
--log-level=lv minimum log level to output on success
|
||||
(levels: trace, info, warning, error)
|
||||
--fail-level=lv minimum log level to fail a build (default: error)
|
||||
--iofail fail build if any I/O or other info is logged
|
||||
(same as --fail-level=info)
|
||||
--wfail fail build if warnings are logged
|
||||
(same as --fail-level=warning)
|
||||
|
||||
--iofail fail build if any I/O or other info is logged
|
||||
--ansi, --no-ansi toggle the use of ANSI escape codes to prettify output
|
||||
--no-build exit immediately if a build target is not up-to-date
|
||||
|
||||
See `lake help <command>` for more information on a specific command."
|
||||
|
||||
|
||||
@@ -156,22 +156,6 @@ def mathToolchainBlobUrl : String :=
|
||||
def mathToolchainUrl : String :=
|
||||
"https://github.com/leanprover-community/mathlib4/blob/master/lean-toolchain"
|
||||
|
||||
def leanActionWorkflowContents :=
|
||||
"name: Lean Action CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: leanprover/lean-action@v1
|
||||
"
|
||||
|
||||
/-- Lake package template identifier. -/
|
||||
inductive InitTemplate
|
||||
@@ -211,24 +195,12 @@ def InitTemplate.configFileContents (tmp : InitTemplate) (lang : ConfigLang) (p
|
||||
| .math, .lean => mathLeanConfigFileContents pkgNameStr (escapeName! root)
|
||||
| .math, .toml => mathTomlConfigFileContents pkgNameStr root.toString
|
||||
|
||||
def createLeanActionWorkflow (dir : FilePath) : LogIO PUnit := do
|
||||
logVerbose "creating lean-action CI workflow"
|
||||
let workflowDir := dir / ".github" / "workflows"
|
||||
let workflowFile := workflowDir / "lean_action_ci.yml"
|
||||
if (← workflowFile.pathExists) then
|
||||
logVerbose "lean-action CI workflow already exists"
|
||||
return
|
||||
IO.FS.createDirAll workflowDir
|
||||
IO.FS.writeFile workflowFile leanActionWorkflowContents
|
||||
logVerbose s!"created lean-action CI workflow at '{workflowFile}'"
|
||||
|
||||
/-- Initialize a new Lake package in the given directory with the given name. -/
|
||||
def initPkg (dir : FilePath) (name : Name) (tmp : InitTemplate) (lang : ConfigLang) (env : Lake.Env) : LogIO PUnit := do
|
||||
let configFile := dir / defaultConfigFile.addExtension lang.fileExtension
|
||||
if (← configFile.pathExists) then
|
||||
error "package already initialized"
|
||||
|
||||
createLeanActionWorkflow dir
|
||||
-- determine the name to use for the root
|
||||
-- use upper camel case unless the specific module name already exists
|
||||
let (root, rootFile?) ← id do
|
||||
|
||||
@@ -41,12 +41,8 @@ structure LakeOptions where
|
||||
trustHash : Bool := true
|
||||
noBuild : Bool := false
|
||||
failLv : LogLevel := .error
|
||||
outLv? : Option LogLevel := .none
|
||||
ansiMode : AnsiMode := .auto
|
||||
|
||||
def LakeOptions.outLv (opts : LakeOptions) : LogLevel :=
|
||||
opts.outLv?.getD opts.verbosity.minLogLv
|
||||
|
||||
/-- Get the Lean installation. Error if missing. -/
|
||||
def LakeOptions.getLeanInstall (opts : LakeOptions) : Except CliError LeanInstall :=
|
||||
match opts.leanInstall? with
|
||||
@@ -86,7 +82,6 @@ def LakeOptions.mkBuildConfig (opts : LakeOptions) (out := OutStream.stderr) : B
|
||||
noBuild := opts.noBuild
|
||||
verbosity := opts.verbosity
|
||||
failLv := opts.failLv
|
||||
outLv := opts.outLv
|
||||
ansiMode := opts.ansiMode
|
||||
out := out
|
||||
|
||||
@@ -106,7 +101,7 @@ def CliM.run (self : CliM α) (args : List String) : BaseIO ExitCode := do
|
||||
|
||||
@[inline] def CliStateM.runLogIO (x : LogIO α) : CliStateM α := do
|
||||
let opts ← get
|
||||
MainM.runLogIO x opts.outLv opts.ansiMode
|
||||
MainM.runLogIO x opts.verbosity.minLogLv opts.ansiMode
|
||||
|
||||
instance (priority := low) : MonadLift LogIO CliStateM := ⟨CliStateM.runLogIO⟩
|
||||
|
||||
@@ -122,10 +117,6 @@ def takeOptArg (opt arg : String) : CliM String := do
|
||||
| none => throw <| CliError.missingOptArg opt arg
|
||||
| some arg => pure arg
|
||||
|
||||
@[inline] def takeOptArg' (opt arg : String) (f : String → Option α) : CliM α := do
|
||||
if let some a := f (← takeOptArg opt arg) then return a
|
||||
throw <| CliError.invalidOptArg opt arg
|
||||
|
||||
/--
|
||||
Verify that there are no CLI arguments remaining
|
||||
before running the given action.
|
||||
@@ -176,25 +167,13 @@ def lakeLongOption : (opt : String) → CliM PUnit
|
||||
| "--rehash" => modifyThe LakeOptions ({· with trustHash := false})
|
||||
| "--wfail" => modifyThe LakeOptions ({· with failLv := .warning})
|
||||
| "--iofail" => modifyThe LakeOptions ({· with failLv := .info})
|
||||
| "--log-level" => do
|
||||
let outLv ← takeOptArg' "--log-level" "log level" LogLevel.ofString?
|
||||
modifyThe LakeOptions ({· with outLv? := outLv})
|
||||
| "--fail-level" => do
|
||||
let failLv ← takeOptArg' "--fail-level" "log level" LogLevel.ofString?
|
||||
modifyThe LakeOptions ({· with failLv})
|
||||
| "--ansi" => modifyThe LakeOptions ({· with ansiMode := .ansi})
|
||||
| "--no-ansi" => modifyThe LakeOptions ({· with ansiMode := .noAnsi})
|
||||
| "--dir" => do
|
||||
let rootDir ← takeOptArg "--dir" "path"
|
||||
modifyThe LakeOptions ({· with rootDir})
|
||||
| "--file" => do
|
||||
let configFile ← takeOptArg "--file" "path"
|
||||
modifyThe LakeOptions ({· with configFile})
|
||||
| "--dir" => do let rootDir ← takeOptArg "--dir" "path"; modifyThe LakeOptions ({· with rootDir})
|
||||
| "--file" => do let configFile ← takeOptArg "--file" "path"; modifyThe LakeOptions ({· with configFile})
|
||||
| "--lean" => do setLean <| ← takeOptArg "--lean" "path or command"
|
||||
| "--help" => modifyThe LakeOptions ({· with wantsHelp := true})
|
||||
| "--" => do
|
||||
let subArgs ← takeArgs
|
||||
modifyThe LakeOptions ({· with subArgs})
|
||||
| "--" => do let subArgs ← takeArgs; modifyThe LakeOptions ({· with subArgs})
|
||||
| opt => throw <| CliError.unknownLongOption opt
|
||||
|
||||
def lakeOption :=
|
||||
@@ -341,7 +320,6 @@ protected def resolveDeps : CliM PUnit := do
|
||||
processOptions lakeOption
|
||||
let opts ← getThe LakeOptions
|
||||
let config ← mkLoadConfig opts
|
||||
noArgsRem do
|
||||
discard <| loadWorkspace config opts.updateDeps
|
||||
|
||||
protected def update : CliM PUnit := do
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user