Compare commits

..

1 Commits

Author SHA1 Message Date
Leonardo de Moura
29cc25963f chore: document replaceUnsafeM issue 2024-07-18 09:07:38 -07:00
564 changed files with 2298 additions and 6101 deletions

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 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

View File

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

View File

@@ -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".
![installing the vscode-lean4 extension](images/code-ext.png)
![installing the vscode-lean4 extension](images/code-ext.png)
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N` / `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".
![show setup guide](images/show-setup-guide.png)
![show setup guide](images/show-setup-guide.png)
1. Follow the Lean 4 setup guide. It will:
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.
![setup guide](images/setup_guide.png)
![setup guide](images/setup_guide.png)

View File

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

View File

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

View File

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

View File

@@ -36,4 +36,3 @@ import Init.Data.Channel
import Init.Data.Cast
import Init.Data.Sum
import Init.Data.BEq
import Init.Data.Subtype

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -127,12 +127,12 @@ results `y` for which `f x` returns `some y`.
@[inline]
def filterMapM {m : Type u Type v} [Monad m] {α β : Type u} (f : α m (Option β)) (as : List α) : m (List β) :=
let rec @[specialize] loop
| [], bs => pure bs.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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 α) :=

View File

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

View File

@@ -320,7 +320,7 @@ Because this is in the `Eq` namespace, if you have a variable `h : a = b`,
For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality)
-/
@[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 β :=

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 #[]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
View 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; } \
};

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -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();
}
}

View File

@@ -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++) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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